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

lua udp reassembly

0

Hi, I am new both to writing dissectors and to lua, but I have anyway managed to write a mostly working set of dissectors for our protocol stack. Thanks a lot for wireshark as a whole and for the lua capabilities. It makes it really easy to work with.

My biggest remaining problem (as in "mostly working") is that my messages can be segmented into several UDP-packets. I cannot figure out how to save the segments for later use and then, when all segments are received, concatenate them.

I would appreciate some hints here.

This could have been my protocol:

--- dissector
local data_dis = Dissector.get ("data")

local p_frag = Proto ("frag", "Frag proto")

local f_frag_message_index = ProtoField.uint32 ("frag.message_index","Message index") local f_frag_part_index = ProtoField.uint8 ("frag.part_index","Part Index") local f_frag_part_length = ProtoField.uint8 ("frag.part_length","Part Length") local f_frag_part_lastIndicator = ProtoField.uint8 ("frag.last_indicator","Last Part Indicator")

p_frag.fields = {f_frag_message_index, f_frag_part_index, f_frag_part_length, f_frag_part_lastIndicator}

function p_frag.dissector(tvb, pinfo, tree) local subtree = tree:add (p_frag, tvb()) subtree:add (f_frag_message_index, tvb(0,4)) subtree:add (f_frag_part_index, tvb(4,1)) subtree:add (f_frag_part_length, tvb(5,1)) subtree:add (f_frag_part_lastIndicator, tvb(6,1))

– Actually, here I call one of few other dissectors depending of the – value of the next byte, bur for this example it would be nice to – send the complete message to the data dissector. data_dis:call(tvb(7):tvb(),pinfo,subtree) end

local udp_encap_table = DissectorTable.get("udp.port") udp_encap_table:add(2900,p_frag)

— end of dissector

## script to generate a few packets
#!/bin/bash
messageIndex="00000000"
partIndex="00"
partLength="05"
lastIndicator="01"
data="53686f7274"

packetSender() { sudo nping 4.3.2.1 –udp -c 1 –source-ip 1.2.3.4 –source-port 2900 –dest-port 2900 –data "$messageIndex$partIndex$partLength$lastIndicator$data" }

First complete packet

packetSender

Then a fragmented packet

first fragment of index 1

messageIndex="00000001" partIndex="00" partLength="04" lastIndicator="00" data="4d756368" packetSender

Beware, new message!

messageIndex="00000002" partIndex="00" partLength="06" lastIndicator="01" data="426577617265" packetSender

#second fragment of index 1 messageIndex="00000001" partIndex="01" partLength="06" lastIndicator="00" data="2c206d756368" packetSender

#third and final fragment of index 1 messageIndex="00000001" partIndex="02" partLength="07" lastIndicator="01" data="206c6f6e676572" packetSender

end of packet generating script

asked 17 Sep ‘16, 08:50

mj99's gravatar image

mj99

26227
accept rate: 50%

edited 22 Sep ‘16, 14:53

JeffMorriss's gravatar image

JeffMorriss ♦
6.2k572


2 Answers:

0

In case someone wants to do reassembly in lua - this is what I will be using until someone points out improvements:

local data_dis = Dissector.get ("data")

local p_frag = Proto ("frag", "Frag proto")

local f_frag_message_index = ProtoField.uint32 ("frag.message_index","Message index") local f_frag_part_index = ProtoField.uint8 ("frag.part_index","Part Index") local f_frag_part_length = ProtoField.uint8 ("frag.part_length","Part Length") local f_frag_part_lastIndicator = ProtoField.uint8 ("frag.last_indicator","Last Part Indicator")

p_frag.fields = {f_frag_message_index, f_frag_part_index, f_frag_part_length, f_frag_part_lastIndicator}

function p_frag.init () – print ("(re-)initialise") fragments = {} concats = {} end

function p_frag.dissector(tvb, pinfo, tree) local subtree = tree:add (p_frag, tvb())

local messageInd = tvb(0,4):uint() subtree:add (f_frag_message_index, tvb(0,4))

local partInd = tvb(4,1):uint() subtree:add (f_frag_part_index, tvb(4,1)) subtree:add (f_frag_part_length, tvb(5,1))

local lastFlag = tvb(6,1):uint () subtree:add (f_frag_part_lastIndicator, tvb(6,1))

if pinfo.visited == false then local range = tvb(7) local bytes = range:bytes() local completeMessage local newTvb local B = 0 local F = 1

  if fragments[messageInd] == nil then
     fragments[messageInd] = {}
     -- print ("Creating mess " .. messageInd)
  end

  if fragments[messageInd][partInd] == nil then
     fragments[messageInd][partInd] = {}
     -- print ("Creating mess " .. messageInd .. ", part " .. partInd)
  end

  fragments[messageInd][partInd][B] = bytes
  fragments[messageInd][partInd][F] = lastFlag

  local ind = 0
  completeMessage = ByteArray.new()
  while ind < 8 do -- maximum 8 fragments
     -- print ("Testing ind = " .. ind)
     if fragments[messageInd][ind] ~= nil then
        completeMessage = completeMessage .. fragments[messageInd][ind][B]
     else
        -- print ("No part with that index, break")
        break
     end
     if fragments[messageInd][ind][F] == 1 then
        -- print ("Flag, break!")
        if ind > 0 then
           -- print("pinfo.number = " .. pinfo.number)
           concats[pinfo.number] = completeMessage
           fragments[messageInd] = {}
        end
        break
     end
     ind = ind + 1
  end

end

data_dis:call(tvb(7):tvb(),pinfo,subtree) if concats[pinfo.number] ~= nil then newTvb = ByteArray.tvb(concats[pinfo.number]) data_dis:call(newTvb(0):tvb(), pinfo, subtree) end end

local udp_encap_table = DissectorTable.get("udp.port") udp_encap_table:add(2900,p_frag)

answered 22 Sep ‘16, 14:44

mj99's gravatar image

mj99
26227
accept rate: 50%

0

Ok, now I can concatenate messages spread over several packets. I can however see some problems with my concatenating technique:

  1. It will consume quite some memory since all fragments are saved forever.
  2. I will run into problems when my message indices are reused.

I would prefer to see the message together with dissection of the last received part of the message.

Can the fragments be discarded and the concatenated messages be saved somehow associated with the packet that contains the last part of the message? How is this usually handled?

-- dissector
-- Containing debug prints

fragments = {}

local data_dis = Dissector.get ("data")

local p_frag = Proto ("frag", "Frag proto")

local f_frag_message_index = ProtoField.uint32 ("frag.message_index","Message index") local f_frag_part_index = ProtoField.uint8 ("frag.part_index","Part Index") local f_frag_part_length = ProtoField.uint8 ("frag.part_length","Part Length") local f_frag_part_lastIndicator = ProtoField.uint8 ("frag.last_indicator","Last Part Indicator")

p_frag.fields = {f_frag_message_index, f_frag_part_index, f_frag_part_length, f_frag_part_lastIndicator}

function p_frag.dissector(tvb, pinfo, tree) local subtree = tree:add (p_frag, tvb())

local messageInd = tvb(0,4):uint() subtree:add (f_frag_message_index, tvb(0,4))

local partInd = tvb(4,1):uint() subtree:add (f_frag_part_index, tvb(4,1)) subtree:add (f_frag_part_length, tvb(5,1))

local lastFlag = tvb(6,1):uint () subtree:add (f_frag_part_lastIndicator, tvb(6,1))

local range = tvb(7) local bytes = range:bytes() local completeMessage local concatenated = 0 local newTvb local B = 0 local F = 1

if fragments[messageInd] == nil then fragments[messageInd] = {} print ("Creating mess " .. messageInd) end

if fragments[messageInd][partInd] == nil then fragments[messageInd][partInd] = {} print ("Creating mess " .. messageInd .. ", part " .. partInd) end

fragments[messageInd][partInd][B] = bytes fragments[messageInd][partInd][F] = lastFlag

local ind = 0 completeMessage = ByteArray.new() while ind < 8 do – maximum 8 fragments print ("Testing ind = " .. ind) if fragments[messageInd][ind] ~= nil then completeMessage = completeMessage .. fragments[messageInd][ind][B] else print ("No part with that index, break") break end if fragments[messageInd][ind][F] == 1 then print ("Flag, break!") if ind > 0 then print ("Concatenated") concatenated = 1 end break end ind = ind + 1 end

data_dis:call(tvb(7):tvb(),pinfo,subtree) if concatenated == 1 then newTvb = ByteArray.tvb(completeMessage) data_dis:call(newTvb(0):tvb(), pinfo, subtree) end end

local udp_encap_table = DissectorTable.get("udp.port") udp_encap_table:add(2900,p_frag)

answered 21 Sep ‘16, 13:36

mj99's gravatar image

mj99
26227
accept rate: 50%

Impressive - you’re probably the first to actually do reassembly in Lua. :-)

[Note that the below is based on my memory of how Wireshark’s reassembly code works; I haven’t had time to actually review your code.]

Wireshark’s internal reassembly routines (which AFAIK aren’t available via the Lua API) store the reassembled data forever (note: once the message is reassembled only the reassembled message is stored; the fragments are freed). That’s needed because Wireshark is only guaranteed to make a single pass through the file (while loading it) whereas the user may click around (and thus want a full dissection of whatever packet s/he clicks on) and so that reassembly data has to be available without redissecting the earlier (fragment) packets.

The reassembled data is stored such that it’s only retrieved when the final frame in the PDU is dissected.

Hope that helps…

(22 Sep ‘16, 06:18) JeffMorriss ♦

What is impressive is that is possible to do it with so little work. The praise goes to wireshark - or really to all persons involved in creating and maintaining it it.

(22 Sep ‘16, 13:47) mj99