Ask Your Question

[LUA] How to get a field from a decoded protobuf to decode the next protobuf

asked 2021-11-08 19:23:07 +0000

michaelarnauts gravatar image

I'm trying to create a protocol dissector in lua, but I can't find a way to get the data from a previously decoded protobuf to decode the next one within the same request.

I have a TCP protocol where a request contains the following data:

  • total length -> 32bit int, total length of the message
  • src -> 16bytes, identifier of sender
  • dst -> 16bytes, identifier of destination
  • header length -> 16bit, length of header message
  • header -> header message (protobuf)
  • message -> message (protobuf)

The operation message is always of the same message-type. This contains a type field that I have to read to know the type of the message message.

Is there a way to fetch the type from the first disector call so i can map this type to the correct version that I can then put in the pinfo.private["pb_msg_type"] field?

The lua code is based on the protobuf example.

        local protobuf_dissector = Dissector.get("protobuf")
        local function create_protobuf_dissector(name, desc)
        local proto = Proto(name, desc)
        local f_len = ProtoField.uint32(name .. ".length", "Message length", base.DEC)
        local f_src = ProtoField.bytes(name .. ".src", "Source", base.NONE)
        local f_dst = ProtoField.bytes(name .. ".dst", "Destination", base.NONE)
        local f_oplen = ProtoField.uint32(name .. ".oplength", "Operation length", base.DEC)

        proto.fields = { f_len, f_src, f_dst, f_oplen }

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

            -- Only process TCP packets
            if pinfo.port_type == 2 then
            local offset = 0
            local remaining_len = tvb:len()
            while remaining_len > 0 do
                if remaining_len < 4 then
                    -- head not enough
                    pinfo.desegment_offset = offset
                    pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
                    return -1

                local data_len = tvb(offset, 4):uint()

                if remaining_len - 4 < data_len then
                    -- data not enough
                    pinfo.desegment_offset = offset
                    pinfo.desegment_len = data_len - (remaining_len - 4)
                    return -1

                subtree:add(f_len, tvb(offset, 4))
                subtree:add(f_src, tvb(offset + 4, 16))
                subtree:add(f_dst, tvb(offset + 20, 16))

                -- Process header
                pinfo.private["pb_msg_type"] = "message,GatewayOperation"
                subtree:add(f_oplen, tvb(offset + 36, 2))
                local op_len = tvb(offset + 36, 2):uint()
                pcall(, protobuf_dissector, tvb(offset + 38, op_len):tvb(), pinfo, subtree)

                -- todo: extract type from 1st message and apply mapping to define the type of the 2nd message

                -- Process message
                pinfo.private["pb_msg_type"] = "message,VersionConfirm"
                pcall(, protobuf_dissector, tvb(offset + 38 + op_len):tvb(), pinfo, subtree)

                offset = offset + 40 + data_len
                remaining_len = remaining_len - 4 - data_len

        DissectorTable.get("tcp.port"):add(0, proto)
        return proto

        create_protobuf_dissector("Zehnder", "Zehnder Comfoconnect")
edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted

answered 2021-11-09 15:14:02 +0000

surlyjake gravatar image

It sounds like what you need is to create your own DissectorTable. The dissectortable allows you to map your payload to the correct parser based on a parameter. This is the same behavior you are using to trigger your protobuf dissector from the tcp dissectortable:

DissectorTable.get("tcp.port"):add(0, proto)

In your case, sounds like in your top-level protobuf protocol lua file, you should create a new dissectortable in the lua file for the operation message.'myoperation.type')

Then, create another Proto() for each of your messages. each of these message protocols will get the 'myoperation.type' dissectorTable and add a mapping using their 'type'. I do this in a .init() function so that you can put each protocol into its own file:

newmessage_proto = Proto('newmessage', 'this is the message used when type is 0x02')
newmessage_contents = ProtoField.bytes('message payload','msgpayload', base.NONE)
newmessage_proto.fields = {newmessage_contents}

function newmessage_proto.init()

function newmessage_proto.dissector(tvb, pinfo, tree) your dissection stuff for newmessage here..

Back in your top-level protobuf protocol file, inside of your .dissector function, you're going to consult the 'myoperation.type' dissectortable to find the appropriate sub-dissector based on the 'type'. I could not find in your example above exactly where you are extracting the 'type' field from, but

    -- todo: extract type
    local type = 0x02
    local dissector = DissectorTable.get('myoperation.type'):getDissector(type)

    if dissector ~= nil then
            -- Dissector was found, invoke subdissector with a new Tvb,
            -- update this to pass the right buffer range to your sub-dissector.
            dissector:call(buf(2):tvb(), pkt, tree)
            -- fallback dissector that just shows the raw data.
            data_dis:call(buf(2):tvb(), pkt, tree)

somewhat similar example which shows one protocol using the dissector table to determine how to decode its payload:

I hope this helps. i don't have my development env in front of me to test this, and I'm no LUA expert, so please forgive the inevitable syntax errors.

edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools



Asked: 2021-11-08 19:23:07 +0000

Seen: 733 times

Last updated: Nov 08 '21