forked from openvela/nuttx
480 lines
22 KiB
Lua
480 lines
22 KiB
Lua
-------------------------------------------------------------------------------
|
|
-- rpmsg_protocal.lua - Wireshark dissector for RPMSG protocol analysis
|
|
--
|
|
-- Licensed to the Apache Software Foundation (ASF) under one or more
|
|
-- contributor license agreements. See the NOTICE file distributed with
|
|
-- this work for additional information regarding copyright ownership.
|
|
-- The ASF licenses this file to you under the Apache License, Version 2.0
|
|
-- (the "License"); you may not use this file except in compliance with
|
|
-- the License. You may obtain a copy of the License at
|
|
--
|
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
|
--
|
|
-- Unless required by applicable law or agreed to in writing, software
|
|
-- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
-- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
-- License for the specific language governing permissions and limitations
|
|
-- under the License.
|
|
--
|
|
-- Usage:
|
|
-- 1. Copy this file to your Wireshark plugins directory:
|
|
-- - Windows: %APPDATA%\Wireshark\plugins\
|
|
-- - Linux: ~/.local/lib/wireshark/plugins/
|
|
-- - macOS: ~/.config/wireshark/plugins/
|
|
-- 2. manual configuration
|
|
-- - First, try using only the Lua plugin. If Wireshark cannot automatically
|
|
-- - recognize packets with DLT=147, then use these two steps for manual
|
|
-- - configuration.
|
|
-- - mkdir -p ~/.config/wireshark
|
|
-- - echo 'uat:user_dlts:"User 0 (DLT=147)","rpmsg_trace","0","","0",""' >>
|
|
-- - ~/.config/wireshark/preferences
|
|
-- 3. restart Wireshark
|
|
-- - wireshark -r rpmsg_trace.pcap -o
|
|
-- - "uat:user_dlts:\"User 0 (DLT=147)\",\"rpmsg_trace\",\"\",\"\",\"0\",\"\""
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- Define the protocol
|
|
local p_rpmsg = Proto("rpmsg_trace", "RPMSG Protocol")
|
|
|
|
-- Constant definitions
|
|
local RPMSG_NAME_SIZE = 32
|
|
|
|
-- Field definitions
|
|
local f = p_rpmsg.fields
|
|
f.trans_type = ProtoField.uint32("rpmsg.trans.type", "Rdev", base.HEX)
|
|
|
|
-- RPMSG header fields (little-endian)
|
|
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)
|
|
|
|
-- Ping protocol fields
|
|
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")
|
|
|
|
-- NS protocol fields
|
|
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)
|
|
|
|
-- Socket protocol fields
|
|
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")
|
|
|
|
-- Test protocol fields
|
|
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")
|
|
|
|
-- ========== FS Protocol Fields ==========
|
|
-- Common header
|
|
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)
|
|
|
|
-- Open command
|
|
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)
|
|
|
|
-- Close command
|
|
f.fs_close_fd = ProtoField.int32("rpmsg.fs.close.fd", "File Descriptor", base.DEC)
|
|
|
|
-- Read/Write commands
|
|
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")
|
|
|
|
-- Fstat command - stat_priv_s structure fields
|
|
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)
|
|
|
|
-- Fstat command
|
|
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)
|
|
|
|
-- Command value definitions (example values - adjust according to your implementation)
|
|
local FS_CMD = {
|
|
OPEN = 1,
|
|
CLOSE = 2,
|
|
READ = 3,
|
|
WRITE = 4,
|
|
FSTAT = 20,
|
|
-- Add other commands as needed
|
|
}
|
|
|
|
-- Dissector function
|
|
function p_rpmsg.dissector(buf, pkt, root)
|
|
local offset = 0
|
|
local buf_len = buf:len()
|
|
|
|
-- 1. Parse transport layer
|
|
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
|
|
|
|
-- 2. Parse RPMSG header (16 bytes)
|
|
if buf_len < offset + 16 then return end
|
|
local header_tree = root:add(p_rpmsg, buf(offset, 16), "Header Layer")
|
|
-- parse src
|
|
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
|
|
|
|
-- parse dst
|
|
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
|
|
|
|
-- parse service_type
|
|
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
|
|
|
|
-- 3. Parse payload based on service_type field
|
|
if service_type == 1 then -- PING
|
|
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 -- NS
|
|
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 -- SOCKET SYNC
|
|
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 -- SOCKET DATA
|
|
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 -- TEST
|
|
pkt.cols.protocol = "TEST"
|
|
-- RPMSG_TEST: parse node[4] + cmd + len + data
|
|
if buf:len() >= offset + 24 then -- at least node[4](16) + cmd(4) + len(4)
|
|
local test_tree = root:add(p_rpmsg, buf(offset, buf:len() - offset), "Test Payload")
|
|
|
|
-- parse node[4]
|
|
local node_bytes = buf(offset, 16)
|
|
test_tree:add(f.test_node, node_bytes)
|
|
offset = offset + 16
|
|
|
|
-- parse cmd
|
|
test_tree:add_le(f.test_cmd, buf(offset, 4)); offset = offset + 4
|
|
|
|
-- parse data len
|
|
local data_len = buf(offset, 4):le_uint()
|
|
test_tree:add_le(f.test_len, buf(offset, 4)); offset = offset + 4
|
|
|
|
-- parse cookie
|
|
test_tree:add_le(f.test_cookie, buf(offset, 8)); offset = offset + 8
|
|
|
|
-- parse data
|
|
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
|
|
|
|
-- ========== FS SERVICE DISSECTION ==========
|
|
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"
|
|
|
|
-- Parse common FS header
|
|
if buf_len < offset + 16 then -- command(4) + result(4) + cookie(8) = 16 bytes
|
|
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")
|
|
|
|
-- Parse command field
|
|
local command = buf(offset, 4):le_uint()
|
|
header_tree:add_le(f.fs_header_command, buf(offset, 4))
|
|
offset = offset + 4
|
|
|
|
-- Parse result field
|
|
header_tree:add_le(f.fs_header_result, buf(offset, 4))
|
|
offset = offset + 4
|
|
|
|
-- Parse cookie field
|
|
header_tree:add_le(f.fs_header_cookie, buf(offset, 8))
|
|
offset = offset + 8
|
|
|
|
-- Parse specific command payload
|
|
if command == FS_CMD.OPEN then
|
|
fs_tree:append_text(" (OPEN)")
|
|
|
|
if buf_len >= offset + 8 then -- flags(4) + mode(4)
|
|
local open_tree = fs_tree:add(p_rpmsg, buf(offset), "Open Command")
|
|
|
|
-- Parse flags
|
|
open_tree:add_le(f.fs_open_flags, buf(offset, 4))
|
|
offset = offset + 4
|
|
|
|
-- Parse mode
|
|
open_tree:add_le(f.fs_open_mode, buf(offset, 4))
|
|
offset = offset + 4
|
|
|
|
-- Parse pathname (variable length, null-terminated string)
|
|
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 -- Skip the null terminator
|
|
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 -- fd(4)
|
|
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 -- fd(4) + count(4) + offset(8) = 16 bytes
|
|
local rw_tree = fs_tree:add(p_rpmsg, buf(offset), "Read/Write Command")
|
|
|
|
-- Parse file descriptor
|
|
rw_tree:add_le(f.fs_rw_fd, buf(offset, 4))
|
|
offset = offset + 4
|
|
|
|
-- Parse count
|
|
local count = buf(offset, 4):le_uint()
|
|
rw_tree:add_le(f.fs_rw_count, buf(offset, 4))
|
|
offset = offset + 4
|
|
|
|
-- Parse offset
|
|
rw_tree:add_le(f.fs_rw_offset, buf(offset, 8))
|
|
offset = offset + 8
|
|
|
|
-- Parse data buffer (if present)
|
|
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)")
|
|
|
|
-- FSTAT command structure: header(16) + stat_buf(96) + union(4 or variable length)
|
|
local STAT_BUF_SIZE = 96 -- Calculated based on rpmsgfs_stat_priv_s structure
|
|
|
|
if buf_len >= offset + STAT_BUF_SIZE then -- stat_buf + at least 4 bytes for union
|
|
local fstat_tree = fs_tree:add(p_rpmsg, buf(offset), "Fstat Command")
|
|
|
|
-- Parse stat buffer (rpmsgfs_stat_priv_s)
|
|
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
|
|
|
|
-- Parse union: either fd (4 bytes) or pathname (variable length)
|
|
local union_tree = fstat_tree:add(p_rpmsg, buf(offset), "Union (fd or pathname)")
|
|
|
|
-- Need to determine whether it's using fd or pathname based on implementation
|
|
-- Simple approach: check if first byte is a digit or printable character
|
|
local first_byte = buf(offset, 1):uint()
|
|
|
|
if first_byte == 0 or (first_byte >= 0x30 and first_byte <= 0x39) then
|
|
-- Possibly fd (0 or number), or empty path
|
|
if buf_len >= offset + 4 then
|
|
local fd_val = buf(offset, 4):le_int()
|
|
if fd_val >= 0 and fd_val <= 1024 then -- Reasonable fd range
|
|
union_tree:add_le(f.fs_fstat_fd, buf(offset, 4))
|
|
union_tree:append_text(" (File Descriptor)")
|
|
offset = offset + 4
|
|
else
|
|
-- Possibly pathname
|
|
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 -- Move to end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- Possibly pathname
|
|
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 -- Move to end
|
|
end
|
|
end
|
|
|
|
-- Display remaining data (if any)
|
|
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 .. ")")
|
|
|
|
-- Display remaining bytes for unknown commands
|
|
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
|
|
|
|
-- Register the protocol
|
|
local function register_proto()
|
|
local custom_table = DissectorTable.get("wtap_encap")
|
|
custom_table:add(147, p_rpmsg)
|
|
end
|
|
|
|
-- Explicitly register (to handle loading timing issues)
|
|
register_proto() |