A small postdissector is enough to exercise everything the
debugger does. This one measures the response time of any protocol
over UDP, what dns.time shows for DNS. On each reply it adds the
time since the client’s latest request in the same UDP stream, as
udprtt.time, and a link to that request, as udprtt.request.
Download udprtt.lua or
save the following as udprtt.lua:
-- Time from a UDP request to its reply in the same stream,
-- like dns.time for DNS, for any protocol over UDP
local udprtt = Proto("udprtt", "UDP Response Time")
local f_rtt = ProtoField.relative_time("udprtt.time", "Time since request")
local f_request = ProtoField.framenum("udprtt.request", "Request in frame",
base.NONE, frametype.REQUEST)
udprtt.fields = { f_rtt, f_request }
local f_stream = Field.new("udp.stream")
local f_time = Field.new("frame.time")
local timeout = NSTime(2, 0) -- later replies are not matched
local state = {}
function state:reset()
self.stream = {} -- stream index -> client address:port and latest request
self.reply = {} -- frame number -> request answered by this frame
end
function udprtt.init()
state:reset()
end
function udprtt.dissector(tvb, pinfo, tree)
local stream = f_stream()
if not stream then return end
local id = stream.value
local now = f_time().value
if not pinfo.visited then
local src = tostring(pinfo.src) .. ":" .. pinfo.src_port
local s = state.stream[id] or { client = src }
state.stream[id] = s
if src == s.client then
s.request = { frame = pinfo.number, time = now }
elseif s.request and now - s.request.time <= timeout then
state.reply[pinfo.number] = s.request
end
end
local request = state.reply[pinfo.number]
if request then
local subtree = tree:add(udprtt)
subtree:add(f_request, request.frame)
subtree:add(f_rtt, now - request.time)
end
end
register_postdissector(udprtt)
Every example later in this chapter resolves against names this
script defines, so you can paste any of them into the Watch
panel without setting up extra state. What’s in scope while
udprtt.dissector is paused:
udprtt, f_rtt, f_request, f_stream, f_time, timeout,
state.
tvb, pinfo, tree, stream, id, now, and
request, plus src and s inside the block that runs on the
first pass.
print, tostring, assert, string,
type, getmetatable, Field, NSTime, Proto,
register_postdissector, get_version.
The first sender in a stream is its client. state.stream holds,
for each stream index, the client’s address and port and its latest
request, a table with the request’s frame number and time.
state.reply maps the frame number of each reply to the request it
answers; that is the request’s own table, not a copy, which is why
every request gets a new table. The state is updated only the first
time a frame is dissected, while pinfo.visited is false; later
passes, in any order, read it back from state.reply.
udprtt.init() clears both tables when a capture file is opened or
dissected again.
Times are NSTime values with nanosecond precision, not numbers:
f_time().value is the time of the frame, the difference of two
NSTime values is exact, and the ProtoField.relative_time field
shows it like udp.time_delta. pinfo.abs_ts is a Lua number of
seconds since the epoch, too coarse for differences of a few
microseconds. NSTime(2, 0) builds the timeout of two seconds, so
that it compares with an NSTime difference.
Field.new(…) is called at file scope on purpose: extractors
can only be constructed at script load, before any dissector or tap
callback runs. Inside the dissector callback the script references
f_stream and f_time (and so do the watch examples below), but
never calls Field.new again.
Drop it into your personal plugins directory (the exact path on your install is also shown under → → → ). The canonical locations are:
~/.local/lib/wireshark/plugins/
%APPDATA%\Wireshark\plugins\
Then:
Ctrl+Shift+L) so
Wireshark picks up the new file. The same shortcut also works
from the Reload Lua Plugins toolbar action inside the Lua
Debugger dialog once it’s open.
udprtt.lua to open
it in the editor.
udprtt.dissector to toggle
a breakpoint (a red circle appears).
Inspect the paused state:
tvb, pinfo, tree, stream, id,
now),
Upvalues, and Globals for the current frame.
pinfo.src (via the Variables context menu
or the + button in the Watch section header,
Ctrl+Shift+W).
F10), Step Into (F11), or
Continue (F5) to advance. The Watch tree updates on every
pause; values that changed since the previous pause are drawn in a
bold accent color and briefly flash.