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

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
Be the first one to answer this question!
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×832
×431

question asked: 16 Sep '13, 15:42

question was seen: 2,131 times

last updated: 17 Sep '13, 07:45

p​o​w​e​r​e​d by O​S​Q​A