local p_rpmsg = Proto("rpmsg_trace", "RPMSG Protocol")
local RPMSG_NAME_SIZE = 32
local f = p_rpmsg.fields
f.trans_type = ProtoField.uint32("rpmsg.trans.type", "Rdev", base.HEX)
f.src = ProtoField.uint32("rpmsg.src", "Source", base.DEC)
f.dst = ProtoField.uint32("rpmsg.dst", "Destination", base.DEC)
f.service_type = ProtoField.uint32("rpmsg.service_type", "Service Type", base.HEX, {
[1] = "PING",
[2] = "NS",
[3] = "SOCKET",
[4] = "SOCKET",
[5] = "TEST",
[6] = "FS",
[7] = "FS",
[8] = "FS",
[9] = "FS",
[25] = "FS"
})
f.len = ProtoField.uint16("rpmsg.len", "Length", base.DEC)
f.flags = ProtoField.uint16("rpmsg.flags", "Flags", base.HEX)
f.ping_cmd = ProtoField.uint32("rpmsg.ping.cmd", "Command", base.HEX)
f.ping_len = ProtoField.uint32("rpmsg.ping.len", "Length", base.DEC)
f.ping_cookie = ProtoField.uint64("rpmsg.ping.cookie", "Cookie", base.HEX)
f.ping_data = ProtoField.bytes("rpmsg.ping.data", "Data")
f.ns_name = ProtoField.string("rpmsg.ns.name", "Name", base.ASCII)
f.ns_addr = ProtoField.uint32("rpmsg.ns.addr", "Address", base.DEC)
f.ns_flags = ProtoField.uint32("rpmsg.ns.flags", "Flags", base.HEX)
f.socket_cmd = ProtoField.uint32("rpmsg.socket.cmd", "Command", base.HEX)
f.socket_size = ProtoField.uint32("rpmsg.socket.size", "Size", base.DEC)
f.socket_pid = ProtoField.uint32("rpmsg.socket.pid", "PID", base.DEC)
f.socket_uid = ProtoField.uint32("rpmsg.socket.uid", "UID", base.DEC)
f.socket_gid = ProtoField.uint32("rpmsg.socket.gid", "GID", base.DEC)
f.socket_pos = ProtoField.uint32("rpmsg.socket.pos", "Position", base.DEC)
f.socket_data_len = ProtoField.uint32("rpmsg.socket.data_len", "Data Length", base.DEC)
f.socket_data = ProtoField.bytes("rpmsg.socket.data", "Data")
f.test_node = ProtoField.bytes("rpmsg.test.node", "List Node (node[4])")
f.test_cmd = ProtoField.uint32("rpmsg.test.cmd", "Command", base.HEX)
f.test_len = ProtoField.uint32("rpmsg.test.len", "Data Length", base.DEC)
f.test_cookie = ProtoField.uint64("rpmsg.test.cookie", "Cookie", base.HEX)
f.test_data = ProtoField.bytes("rpmsg.test.data", "Data")
f.fs_header_command = ProtoField.uint32("rpmsg.fs.header.command", "Command", base.HEX)
f.fs_header_result = ProtoField.int32("rpmsg.fs.header.result", "Result", base.DEC)
f.fs_header_cookie = ProtoField.uint64("rpmsg.fs.header.cookie", "Cookie", base.HEX)
f.fs_open_flags = ProtoField.int32("rpmsg.fs.open.flags", "Flags", base.DEC)
f.fs_open_mode = ProtoField.int32("rpmsg.fs.open.mode", "Mode", base.DEC)
f.fs_open_pathname = ProtoField.string("rpmsg.fs.open.pathname", "Pathname", base.ASCII)
f.fs_close_fd = ProtoField.int32("rpmsg.fs.close.fd", "File Descriptor", base.DEC)
f.fs_rw_fd = ProtoField.int32("rpmsg.fs.rw.fd", "File Descriptor", base.DEC)
f.fs_rw_count = ProtoField.uint32("rpmsg.fs.rw.count", "Count", base.DEC)
f.fs_rw_offset = ProtoField.int64("rpmsg.fs.rw.offset", "Offset", base.DEC)
f.fs_rw_buf = ProtoField.bytes("rpmsg.fs.rw.buf", "Buffer Data")
f.fs_stat_dev = ProtoField.uint32("rpmsg.fs.stat.dev", "Device ID", base.HEX)
f.fs_stat_mode = ProtoField.uint32("rpmsg.fs.stat.mode", "File Mode", base.HEX)
f.fs_stat_rdev = ProtoField.uint32("rpmsg.fs.stat.rdev", "Device ID (special)", base.HEX)
f.fs_stat_ino = ProtoField.uint16("rpmsg.fs.stat.ino", "Inode Number", base.DEC)
f.fs_stat_nlink = ProtoField.uint16("rpmsg.fs.stat.nlink", "Hard Links", base.DEC)
f.fs_stat_size = ProtoField.int64("rpmsg.fs.stat.size", "File Size", base.DEC)
f.fs_stat_atim_sec = ProtoField.int64("rpmsg.fs.stat.atim_sec", "Access Time (sec)", base.DEC)
f.fs_stat_atim_nsec = ProtoField.int64("rpmsg.fs.stat.atim_nsec", "Access Time (nsec)", base.DEC)
f.fs_stat_mtim_sec = ProtoField.int64("rpmsg.fs.stat.mtim_sec", "Modify Time (sec)", base.DEC)
f.fs_stat_mtim_nsec = ProtoField.int64("rpmsg.fs.stat.mtim_nsec", "Modify Time (nsec)", base.DEC)
f.fs_stat_ctim_sec = ProtoField.int64("rpmsg.fs.stat.ctim_sec", "Change Time (sec)", base.DEC)
f.fs_stat_ctim_nsec = ProtoField.int64("rpmsg.fs.stat.ctim_nsec", "Change Time (nsec)", base.DEC)
f.fs_stat_blocks = ProtoField.uint64("rpmsg.fs.stat.blocks", "Blocks Allocated", base.DEC)
f.fs_stat_uid = ProtoField.int16("rpmsg.fs.stat.uid", "User ID", base.DEC)
f.fs_stat_gid = ProtoField.int16("rpmsg.fs.stat.gid", "Group ID", base.DEC)
f.fs_stat_blksize = ProtoField.int16("rpmsg.fs.stat.blksize", "Block Size", base.DEC)
f.fs_stat_reserved = ProtoField.uint16("rpmsg.fs.stat.reserved", "Reserved", base.HEX)
f.fs_fstat_fd = ProtoField.int32("rpmsg.fs.fstat.fd", "File Descriptor", base.DEC)
f.fs_fstat_pathname = ProtoField.string("rpmsg.fs.fstat.pathname", "Pathname", base.ASCII)
local FS_CMD = {
OPEN = 1,
CLOSE = 2,
READ = 3,
WRITE = 4,
FSTAT = 20,
}
function p_rpmsg.dissector(buf, pkt, root)
local offset = 0
local buf_len = buf:len()
if buf_len < offset + 4 then return end
local trans_tree = root:add(p_rpmsg, buf(offset, 4), "Transport Layer")
trans_tree:add(f.trans_type, buf(offset, 4))
offset = offset + 4
if buf_len < offset + 16 then return end
local header_tree = root:add(p_rpmsg, buf(offset, 16), "Header Layer")
local src_val = buf(offset, 4):le_uint()
header_tree:add_le(f.src, buf(offset, 4))
pkt.cols.src = tostring(src_val)
offset = offset + 4
local dst_val = buf(offset, 4):le_uint()
header_tree:add_le(f.dst, buf(offset, 4))
pkt.cols.dst = tostring(dst_val)
offset = offset + 4
local service_type = buf(offset, 4):le_uint()
local service_type_str = ""
if service_type == 1 then service_type_str = "PING"
elseif service_type == 2 then service_type_str = "NS"
elseif service_type == 3 or service_type == 4 then service_type_str = "SOCKET"
elseif service_type == 5 then service_type_str = "TEST"
elseif service_type == 6 or service_type == 7 or service_type == 8 or service_type == 9 or service_type == 25 then
service_type_str = "FS"
end
local service_type_item = header_tree:add_le(f.service_type, buf(offset, 4))
service_type_item:set_text("Service Type: " .. service_type_str)
offset = offset + 4
header_tree:add_le(f.len, buf(offset, 2)); offset = offset + 2
header_tree:add_le(f.flags, buf(offset, 2)); offset = offset + 2
if service_type == 1 then
pkt.cols.protocol = "PING"
if buf_len < offset + 16 then return end
local ping_tree = root:add(p_rpmsg, buf(offset), "Ping Protocol")
ping_tree:add_le(f.ping_cmd, buf(offset, 4)); offset = offset + 4
ping_tree:add_le(f.ping_len, buf(offset, 4)); offset = offset + 4
ping_tree:add_le(f.ping_cookie, buf(offset, 8)); offset = offset + 8
local data_len = buf_len - offset
if data_len > 0 then
ping_tree:add(f.ping_data, buf(offset, data_len))
end
elseif service_type == 2 then
pkt.cols.protocol = "NS"
if buf_len < offset + RPMSG_NAME_SIZE + 8 then return end
local ns_tree = root:add(p_rpmsg, buf(offset), "NS Protocol")
ns_tree:add(f.ns_name, buf(offset, RPMSG_NAME_SIZE)); offset = offset + RPMSG_NAME_SIZE
ns_tree:add_le(f.ns_addr, buf(offset, 4)); offset = offset + 4
ns_tree:add_le(f.ns_flags, buf(offset, 4))
elseif service_type == 3 then
pkt.cols.protocol = "SOCKET_SYNC"
if buf_len < offset + 20 then return end
local socket_tree = root:add(p_rpmsg, buf(offset), "Socket Layer")
socket_tree:add_le(f.socket_cmd, buf(offset, 4)); offset = offset + 4
local sync_tree = socket_tree:add(p_rpmsg, buf(offset), "sync Protocol")
sync_tree:add_le(f.socket_size, buf(offset, 4)); offset = offset + 4
sync_tree:add_le(f.socket_pid, buf(offset, 4)); offset = offset + 4
sync_tree:add_le(f.socket_uid, buf(offset, 4)); offset = offset + 4
sync_tree:add_le(f.socket_gid, buf(offset, 4))
elseif service_type == 4 then
pkt.cols.protocol = "SOCKET_DATA"
if buf_len < offset + 12 then return end
local socket_tree = root:add(p_rpmsg, buf(offset), "Socket Layer")
socket_tree:add_le(f.socket_cmd, buf(offset, 4)); offset = offset + 4
local data_tree = socket_tree:add(p_rpmsg, buf(offset), "data Protocol")
data_tree:add_le(f.socket_pos, buf(offset, 4)); offset = offset + 4
data_tree:add_le(f.socket_data_len, buf(offset, 4)); offset = offset + 4
local data_len = buf_len - offset
if data_len > 0 then
data_tree:add(f.socket_data, buf(offset, data_len))
end
elseif service_type == 5 then
pkt.cols.protocol = "TEST"
if buf:len() >= offset + 24 then
local test_tree = root:add(p_rpmsg, buf(offset, buf:len() - offset), "Test Payload")
local node_bytes = buf(offset, 16)
test_tree:add(f.test_node, node_bytes)
offset = offset + 16
test_tree:add_le(f.test_cmd, buf(offset, 4)); offset = offset + 4
local data_len = buf(offset, 4):le_uint()
test_tree:add_le(f.test_len, buf(offset, 4)); offset = offset + 4
test_tree:add_le(f.test_cookie, buf(offset, 8)); offset = offset + 8
if buf:len() >= offset + data_len then
test_tree:add_le(f.test_data, buf(offset, data_len))
offset = offset + data_len
else
test_tree:append_text("Truncated data")
end
else
root:add(p_rpmsg, buf(offset), "Malformed Test Message")
end
elseif service_type == 6 or service_type == 7 or service_type == 8 or service_type == 9 or service_type == 25 then
pkt.cols.protocol = "FS"
if buf_len < offset + 16 then
root:add(p_rpmsg, buf(offset), "Incomplete FS Header")
return
end
local fs_tree = root:add(p_rpmsg, buf(offset), "FS Protocol")
local header_tree = fs_tree:add(p_rpmsg, buf(offset, 16), "FS Header")
local command = buf(offset, 4):le_uint()
header_tree:add_le(f.fs_header_command, buf(offset, 4))
offset = offset + 4
header_tree:add_le(f.fs_header_result, buf(offset, 4))
offset = offset + 4
header_tree:add_le(f.fs_header_cookie, buf(offset, 8))
offset = offset + 8
if command == FS_CMD.OPEN then
fs_tree:append_text(" (OPEN)")
if buf_len >= offset + 8 then
local open_tree = fs_tree:add(p_rpmsg, buf(offset), "Open Command")
open_tree:add_le(f.fs_open_flags, buf(offset, 4))
offset = offset + 4
open_tree:add_le(f.fs_open_mode, buf(offset, 4))
offset = offset + 4
local pathname_end = offset
while pathname_end < buf_len and buf(pathname_end, 1):uint() ~= 0 do
pathname_end = pathname_end + 1
end
if pathname_end < buf_len then
local pathname_len = pathname_end - offset
open_tree:add(f.fs_open_pathname, buf(offset, pathname_len))
offset = pathname_end + 1
else
open_tree:append_text("Malformed pathname (missing null terminator)")
end
else
fs_tree:append_text("Incomplete OPEN command")
end
elseif command == FS_CMD.CLOSE then
fs_tree:append_text(" (CLOSE)")
if buf_len >= offset + 4 then
local close_tree = fs_tree:add(p_rpmsg, buf(offset), "Close Command")
close_tree:add_le(f.fs_close_fd, buf(offset, 4))
offset = offset + 4
else
fs_tree:append_text("Incomplete CLOSE command")
end
elseif command == FS_CMD.READ or command == FS_CMD.WRITE then
if command == FS_CMD.READ then
fs_tree:append_text(" (READ)")
else
fs_tree:append_text(" (WRITE)")
end
if buf_len >= offset + 16 then
local rw_tree = fs_tree:add(p_rpmsg, buf(offset), "Read/Write Command")
rw_tree:add_le(f.fs_rw_fd, buf(offset, 4))
offset = offset + 4
local count = buf(offset, 4):le_uint()
rw_tree:add_le(f.fs_rw_count, buf(offset, 4))
offset = offset + 4
rw_tree:add_le(f.fs_rw_offset, buf(offset, 8))
offset = offset + 8
if count > 0 and buf_len >= offset + count then
rw_tree:add(f.fs_rw_buf, buf(offset, count))
offset = offset + count
elseif count > 0 then
rw_tree:append_text("Incomplete data buffer")
end
else
fs_tree:append_text("Incomplete READ/WRITE command")
end
elseif command == FS_CMD.FSTAT then
fs_tree:append_text(" (FSTAT)")
local STAT_BUF_SIZE = 96
if buf_len >= offset + STAT_BUF_SIZE then
local fstat_tree = fs_tree:add(p_rpmsg, buf(offset), "Fstat Command")
local stat_tree = fstat_tree:add(p_rpmsg, buf(offset, STAT_BUF_SIZE), "Stat Buffer")
stat_tree:add_le(f.fs_stat_dev, buf(offset, 4)); offset = offset + 4
stat_tree:add_le(f.fs_stat_mode, buf(offset, 4)); offset = offset + 4
stat_tree:add_le(f.fs_stat_rdev, buf(offset, 4)); offset = offset + 4
stat_tree:add_le(f.fs_stat_ino, buf(offset, 2)); offset = offset + 2
stat_tree:add_le(f.fs_stat_nlink, buf(offset, 2)); offset = offset + 2
stat_tree:add_le(f.fs_stat_size, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_atim_sec, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_atim_nsec, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_mtim_sec, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_mtim_nsec, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_ctim_sec, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_ctim_nsec, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_blocks, buf(offset, 8)); offset = offset + 8
stat_tree:add_le(f.fs_stat_uid, buf(offset, 2)); offset = offset + 2
stat_tree:add_le(f.fs_stat_gid, buf(offset, 2)); offset = offset + 2
stat_tree:add_le(f.fs_stat_blksize, buf(offset, 2)); offset = offset + 2
stat_tree:add_le(f.fs_stat_reserved, buf(offset, 2)); offset = offset + 2
local union_tree = fstat_tree:add(p_rpmsg, buf(offset), "Union (fd or pathname)")
local first_byte = buf(offset, 1):uint()
if first_byte == 0 or (first_byte >= 0x30 and first_byte <= 0x39) then
if buf_len >= offset + 4 then
local fd_val = buf(offset, 4):le_int()
if fd_val >= 0 and fd_val <= 1024 then
union_tree:add_le(f.fs_fstat_fd, buf(offset, 4))
union_tree:append_text(" (File Descriptor)")
offset = offset + 4
else
local pathname_end = offset
while pathname_end < buf_len and buf(pathname_end, 1):uint() ~= 0 do
pathname_end = pathname_end + 1
end
if pathname_end < buf_len then
local pathname_len = pathname_end - offset
union_tree:add(f.fs_fstat_pathname, buf(offset, pathname_len))
union_tree:append_text(" (Pathname)")
offset = pathname_end + 1
else
union_tree:append_text("Unknown union content")
offset = buf_len
end
end
end
else
local pathname_end = offset
while pathname_end < buf_len and buf(pathname_end, 1):uint() ~= 0 do
pathname_end = pathname_end + 1
end
if pathname_end < buf_len then
local pathname_len = pathname_end - offset
union_tree:add(f.fs_fstat_pathname, buf(offset, pathname_len))
union_tree:append_text(" (Pathname)")
offset = pathname_end + 1
else
union_tree:append_text("Unknown union content")
offset = buf_len
end
end
local remaining = buf_len - offset
if remaining > 0 then
fstat_tree:add(p_rpmsg, buf(offset, remaining), "Extra Data (" .. remaining .. " bytes)")
end
else
fs_tree:append_text("Incomplete FSTAT command (need at least " .. (STAT_BUF_SIZE + 4) .. " bytes, got " .. (buf_len - offset) .. ")")
end
else
fs_tree:append_text(" (Unknown Command: " .. command .. ")")
local remaining = buf_len - offset
if remaining > 0 then
fs_tree:add(p_rpmsg, buf(offset, remaining), "Unknown Data (" .. remaining .. " bytes)")
end
end
end
end
local function register_proto()
local custom_table = DissectorTable.get("wtap_encap")
custom_table:add(147, p_rpmsg)
end
register_proto()