This is a static archive of our old Q&A Site. Please post any new questions and answers at ask.wireshark.org.

tshark, -Xlua_script: io.flush() doesn’t seem to work

0

io.flush doesn't seem to do anything from inside a Lua listener. Concretely, my script looks like this in outline:

local ip = Listener.new("ip")
function ip.packet(pinfo, tvb)
    ...
    io.write(string.format("%d:%d:%.6f:%s:%s:%s:%d:%s:%s:%d:%d:%s\n", ...))
    io.flush()
end

but output is still being buffered. This wouldn't be an issue, except that the tshark process might get killed at any time by higher-level code, and it doesn't seem to flush pending output when it receives SIGTERM.

Please advise how I can ensure that each and every packet is logged as it comes in.

EDIT: Complete script below. Sorry about the length. This is being run as

tshark -q -l -Xlua_script:extract.lua "ip host W.X.Y.Z and tcp port NNNN" > client-X.pkts

from a controller script that forks off one of these for each client (each client gets a different port; W.X.Y.Z, however, is always the public IP address of the machine running this script). When the client terminates, the tshark process is killed (with SIGTERM). Thousands of packets are being lost from the log -- I know by construction that each client generates tens of megabytes of traffic, at least, but often the .pkts file is empty!

Client and controller are running on (separate) Linux VMs, tshark -v says:

TShark 1.10.2 (SVN Rev 51934 from /trunk-1.10)
...
Running on Linux 3.10-2-amd64, with locale en_US.utf8, with libpcap version
1.4.0, with libz 1.2.8.

complete script:

do
   local function map(func, array)
      local new_array = {}
      for i,v in ipairs(array) do
         new_array[i] = func(v)
      end
      return new_array
   end

http://ricilake.blogspot.com/2007/10/iterating-bits-in-lua.html local function hasbit(word, i) i = 2 ^ (i - 1) return word % (i + i) >= i end

local function decode_tcp_flags(flags) local labels = { – counting from the low end up "fin", "syn", "rst", "psh", "ack", "urg", "ece", "cwr", "ns", "rsv1","rsv2","rsv3" } local decoded = "" for i, label in ipairs(labels) do if hasbit(flags, i) then if string.len(decoded) > 0 then decoded = decoded .. "." end decoded = decoded .. label end end return decoded end

local rctypes = { [20] = "cipher", [21] = "alert", [22] = "encrypted-handshake", [23] = "data", [24] = "heartbeat" }

local hstypes = { [0] = "hello-request", [1] = "client-hello", [2] = "server-hello", [3] = "new-ticketed-session", [11] = "certificate", [12] = "server-key-exchange", [13] = "certificate-request", [14] = "server-done", [15] = "certificate-verify", [16] = "client-key-exchange", [20] = "finished", [22] = "certificate-status", [67] = "next-proto" }

local function decode_ssl_rctype(rctype, records) local label = rctypes[rctype] if label == nil then label = string.format("record-%d", rctype) end table.insert(records, label) end

local function decode_ssl_hstype(hstype, records) local label = hstypes[hstype] if label == nil then label = string.format("handshake-%d", hstype) end local lastrecord = table.remove(records) if lastrecord ~= "encrypted-handshake" then table.insert(records, lastrecord) end table.insert(records, label) end

local servers = {} local server_count = 0 local function new_server(addr) local x = math.floor(server_count / 26) + 1 local y = math.mod(server_count, 26) + 1

  local label = string.rep(string.sub("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
                                      y, y), x)
  servers[addr] = label
  server_count = server_count + 1
  return label

end

local fieldnames = { "tcp.len", "udp.length", "ip.src", "ip.dst", "tcp.srcport", "tcp.dstport", "udp.srcport", "udp.dstport", "tcp.flags", "tcp.stream", "tcp.analysis.retransmission", "tcp.analysis.duplicate_ack", "ssl.record.content_type", "ssl.record.length", "ssl.handshake.type" } local dissectors = map(Field.new, fieldnames)

local ip = Listener.new("ip") function ip.packet(pinfo, tvb) local number = tonumber(pinfo.number) local time = tonumber(pinfo.abs_ts) local packet_len = tonumber(pinfo.len) local stream = 0 local proto = "udp" local flags = "" local dupe = false local payload_len = 0 local srcip = nil local dstip = nil local srcport = nil local dstport = nil local direction = nil local host = nil local port = nil local sslrecords = {} local sslreclens = {}

  local fieldlist = { all_field_infos() }
  for ix, finfo in ipairs(fieldlist) do
     if finfo.name == "ip.src" then
        srcip = tostring(finfo.value)

     elseif finfo.name == "ip.dst" then
        dstip = tostring(finfo.value)

     elseif (finfo.name == "tcp.srcport" or
             finfo.name == "udp.srcport") then
        srcport = tonumber(finfo.value)

     elseif (finfo.name == "tcp.dstport" or
             finfo.name == "udp.dstport") then
        dstport = tonumber(finfo.value)

     elseif (finfo.name == "tcp.len" or
             finfo.name == "udp.length") then
        payload_len = tonumber(finfo.value)

     elseif finfo.name == "tcp.stream" then
        proto = "tcp"
        stream = tonumber(finfo.value) + 1

     elseif finfo.name == "tcp.flags" then
        flags = decode_tcp_flags(tonumber(finfo.value))

     elseif (finfo.name == "tcp.analysis.retransmission" or
             finfo.name == "tcp.analysis.duplicate_ack") then
        dupe = true

     elseif finfo.name == "ssl.record.content_type" then
        decode_ssl_rctype(tonumber(finfo.value), sslrecords)

     elseif finfo.name == "ssl.handshake.type" then
        decode_ssl_hstype(tonumber(finfo.value), sslrecords)

     elseif finfo.name == "ssl.record.length" then
        table.insert(sslreclens, tonumber(finfo.value))

     end
  end

  host = servers[srcip..":"..srcport]
  if host ~= nil then
     direction = "down"
     port = srcport
  else
     host = servers[dstip..":"..dstport]
     if host == nil then host = new_server(dstip..":"..dstport) end
     direction = "up"
     port = dstport
  end

  if dupe then
     if flags == ""
     then flags = "dup"
     else flags = flags .. ".dup"
     end
  end

  sslrecords = table.concat(sslrecords, ".")
  sslreclens = table.concat(sslreclens, ".")
  io.write(string.format("%d:%d:%.6f:%s:%s:%s:%d:%s:%s:%d:%d:%s\n",
                         number,
                         stream,
                         time,
                         proto,
                         direction,
                         host,
                         port,
                         flags,
                         sslrecords,
                         packet_len,
                         payload_len,
                         sslreclens))
  io.flush()

end end

asked 16 Sep ‘13, 15:42

Zack's gravatar image

Zack
26337
accept rate: 0%

edited 17 Sep ‘13, 07:45

can you please add the ‘full’ Lua code, in order to test it?

BTW: How did you notice that some packet are not being printed?

BTW#2: What is your OS and Wireshark version?

(17 Sep ‘13, 03:21) Kurt Knochner ♦

@KurtKnochner Code and more detail about usage added. I hope this answers all your questions.

(17 Sep ‘13, 07:43) Zack