Wireshark Cyphal/UDP Script

I’m playing around with this after I asked ChatGPT how to write a Lua script for Wireshark. This seems to decode the Cyphal/UDP header correctly:

-- Load the bit library
-- local bit = require("bit")

-- Protocol constants
local CYPHAL_UDP_PORT = 9382 -- The port number used by Cyphal/UDP

-- Custom protocol dissector
local cyphal_udp = Proto("cyphaludp", "Cyphal UDP Protocol")

version = ProtoField.uint8("cyphal_udp.version", "version", base.DEC)
priority =ProtoField.uint8("cyphal_udp.priority", "priority", base.DEC)
source_node_id = ProtoField.uint16("cyphal_udp.source_node_id", "source_node_id", base.DEC)
destination_node_id = ProtoField.uint16("cyphal_udp.destination_node_id", "destination_node_id", base.DEC)
data_specifier = ProtoField.uint16("cyphal_udp.data_specifier", "data_specifier", base.DEC)
service_not_message = ProtoField.bool("cyphal_udp.service_not_message", "service_not_message", base.NONE)
subject_id = ProtoField.uint16("cyphal_udp.subject_id", "subject_id", base.DEC)
service_id = ProtoField.uint16("cyphal_udp.service_id", "service_id", base.DEC)
request_not_response = ProtoField.bool("cyphal_udp.request_not_response", "request_not_response", base.NONE)
transfer_id = ProtoField.uint64("cyphal_udp.transfer_id", "transfer_id", base.DEC)
frame_index_eot = ProtoField.uint32("cyphal_udp.frame_index_eot", "frame_index_eot", base.HEX)
frame_index = ProtoField.uint32("cyphal_udp.frame_index", "frame_index", base.HEX)
end_of_transfer = ProtoField.bool("cyphal_udp.end_of_transfer", "end_of_transfer", base.NONE)
user_data = ProtoField.uint16("cyphal_udp.user_data", "user_data", base.HEX)
crc16_ccitt_false = ProtoField.uint16("cyphal_udp.crc16_ccitt_false", "crc16_ccitt_false", base.HEX)

-- Protocol fields
cyphal_udp.fields = {
    version, priority,
    source_node_id,
    destination_node_id,
    data_specifier,
    service_not_message,
    subject_id,
    service_id,
    request_not_response,
    transfer_id,
    frame_index_eot,
    frame_index,
    end_of_transfer,
    user_data,
    crc16_ccitt_false
    -- Add more fields as needed
}

-- Function to dissect the custom protocol
local function dissect_cyphal_udp(buffer, pinfo, tree)
    -- Create a subtree for the custom protocol
    local cyphal_udp_tree = tree:add(cyphal_udp, buffer(), "Cyphal UDP Header")

    -- Add fields to the subtree
    cyphal_udp_tree:add_le(version, buffer(0, 1))
    cyphal_udp_tree:add_le(priority, buffer(1, 1))
    cyphal_udp_tree:add_le(source_node_id, buffer(2, 2))
    cyphal_udp_tree:add_le(destination_node_id, buffer(4, 2))
    local ds = buffer(6, 2):le_uint()
    local port_id = bit.band(ds, 0x7FFF)
    local snm = bit.rshift(bit.band(ds, 0x8000), 15)
    local rnr = false
    if snm == 1 then
        if port_id > 16384 then
            port_id = port_id - 16384
            rnr = true
        end
        cyphal_udp_tree:add_le(request_not_response, rnr)
        cyphal_udp_tree:add_le(service_id, port_id)
    else
        cyphal_udp_tree:add_le(subject_id, port_id)
    end
    cyphal_udp_tree:add_le(service_not_message, snm)
    cyphal_udp_tree:add_le(data_specifier, buffer(6, 2))
    cyphal_udp_tree:add_le(transfer_id, buffer(8, 8))
    -- process the number as BE
    cyphal_udp_tree:add_le(frame_index_eot, buffer(16, 4))
    local fi = buffer(16, 4):le_uint()
    local fidx = bit.band(fi, 0x7FFFFFFF)
    local eot = bit.rshift(bit.band(fi, 0x80000000), 31)
    cyphal_udp_tree:add_le(frame_index, fidx)
    cyphal_udp_tree:add_le(end_of_transfer, eot)
    cyphal_udp_tree:add_le(user_data, buffer(20, 2))
    cyphal_udp_tree:add_le(crc16_ccitt_false, buffer(22, 2))
    -- Add more field dissectors as needed
end

-- UDP dissector
local udp_dissector = Dissector.get("udp")

-- Register the custom protocol dissector
function cyphal_udp.dissector(buffer, pinfo, tree)
    local src_port = pinfo.src_port
    local dst_port = pinfo.dst_port

    -- Check if the UDP packet uses the custom protocol port
    if src_port == CYPHAL_UDP_PORT or dst_port == CYPHAL_UDP_PORT then
        local subtree = tree:add(cyphal_udp, buffer(), "Cyphal/UDP Protocol Data")
        pinfo.cols.protocol = cyphal_udp.name
        -- Call the custom protocol dissector
        dissect_cyphal_udp(buffer, pinfo, subtree)
    else
        -- Call the default UDP dissector for other ports
        udp_dissector:call(buffer, pinfo, tree)
    end
end

-- Register the custom protocol
local udp_port_table = DissectorTable.get("udp.port")
udp_port_table:add(CYPHAL_UDP_PORT, cyphal_udp)


Install in your /Applications/Wireshark.app/Contents/PlugIns/wireshark/<version>/ (on Mac) and it should be automatic. I haven’t checked other OS versions for compatibility.

@pavel.kirienko, @scottdixon I’m going to start a Garage repo for WiresharkProtocols and put this into it.

1 Like

We’re live (see matrix chat)

2 Likes