在分析抓包时,有时需要横跨多个包计算,比如计算所有RTP包的timestamp与之前RTP包的timestamp的差值,这样可以一目了然看出timestamp是否有异常波动。
理想方式是把这个计算出来的差值直接放到RTP信息后面,但wireshark lua目前没有提供可以在某协议dissector解析完后调用自己lua写的dissector的方法,除了http body可借助一些特殊的table实现,可参考《如何在wireshark里用lua脚本编写dissector解析HTTP BODY (after TCP reassembled) 》(http://blog.csdn.net/jasonhwang/article/details/5526383),其它协议必须借助替换tcp或udp端口,在自己的dissector里先调用标准的dissector,再调用自己的代码的所谓chained dissectors方式。一旦协议端口变化,这种方式就很不方便。
我这边用一个tap加postdissector的方式实现计算RTP timestamp相邻包增量值的目的。程序的基本方案是:
- 首先用tap注册到解析所有rtp包,在分析过程中,按rtp不同流的方式,各自保存上一个rtp包的timestamp值,然后与当前rtp包算出差值后再把信息记录到一个table(map以seq为键值)里;
- 然后用户可以通过Tools->Lua->RTP Timestamp Delta菜单打开一个文本窗口浏览所有rtp包的timestamp与上一个rtp timestamp的差值统计表;
- 用户也可以点击某个rtp包,触发wireshark调用postdissector去显示当前rtp包timestamp与上一个包的差值(从tap统计的结果变量里直接去取)。
这个例子使用价值可能不高,但可以学到以下几方面知识:
- tap是可以在打开wireshark时就可被调用的,不像wireshark lua例子里的tap一样,只在打开TextWindow时才可对之后的抓包起作用;
- postdissector不是在解析完一个包后立即执行,而是在点击某个packet记录后才被调用;
- 一个抓包里多个RTP的请求(RTSP Interleaved模式)可以用外加table方式或取某变量参数的所有值。
代码如下:
-- Display RTP timestamp delta between current RTP pack and last RTP pack.
-- Author: Huang Qiangxiong (qiangxiong.huang@gmail.com)
-- change log:
-- 2012-03-01
-- Just can play.
------------------------------------------------------------------------------------------------
do
-------------
-- tap for saving each RTP timestamp delta value in rtp variables, you can also
-- check the all delta values by menu: tap->RTP Timestamp Delta
-------------
-- for saving all rtp streams info
local rtp_streams = {}
local f_rtp_ssrc = Field.new("rtp.ssrc")
local f_rtp_timestamp = Field.new("rtp.timestamp")
local f_rtp_pt = Field.new("rtp.p_type")
local f_rtp_seq = Field.new("rtp.seq")
local f_rtp_marker = Field.new("rtp.marker")
local myrtptap = Listener.new(tap, "rtp");
function getkey(ssrc, pt, pinfo)
return ssrc .. pt .. tostring(pinfo.src) .. pinfo.src_port .. tostring(pinfo.dst) .. pinfo.dst_port
end
-- this function will be called once for each packet
function myrtptap.packet(pinfo,tvb)
-- using table because one packet may contains
-- more than one RTP packets
local rtp_ssrc = { f_rtp_ssrc() }
local rtp_pt = { f_rtp_pt() }
local rtp_timestamp = { f_rtp_timestamp() }
local rtp_seq = { f_rtp_seq() }
local rtp_marker = { f_rtp_marker() }
for i,ssrc_f in ipairs(rtp_ssrc) do
local ssrc = ssrc_f.value
local pt = rtp_pt[i].value
local cur_timestamp = rtp_timestamp[i].value
local seq = rtp_seq[i].value
local marker = rtp_marker[i].value
local key = getkey(ssrc, pt, pinfo)
local rtp_stream = rtp_streams[key]
if not rtp_stream then
rtp_stream = {}
rtp_stream.pk_infos = {}
rtp_stream.ssrc = ssrc
rtp_stream.pt = pt
rtp_stream.last_timestamp = cur_timestamp
rtp_streams[key] = rtp_stream
end
-- add current rtp packet info to rtp stream
local rtp_info = {}
rtp_info.pkno = pinfo.number
rtp_info.timestamp = cur_timestamp
rtp_info.seq = seq
rtp_info.marker = marker
rtp_info.delta = cur_timestamp - rtp_stream.last_timestamp
rtp_stream.pk_infos[seq] = rtp_info
rtp_stream.last_timestamp = cur_timestamp
end
end
-- this function will be called whenever a reset is needed
-- e.g. when reloading the capture file
function myrtptap.reset()
rtp_streams = {}
end
local function menuable_myrtptap()
-- Declare the window we will use
local tw = TextWindow.new("Display RTP Timestamp Delta")
local text = "All RTP Stream Info:\n"
for key,rtp_stream in pairs(rtp_streams) do
text = text .. "\nRTP Stream (ssrc=" .. rtp_stream.ssrc .. " payload_type=" .. rtp_stream.pt .. " rtp_packets=" .. #rtp_stream.pk_infos .. "):\n"
-- print infos order by seq
local keys = {}
for seq in pairs(rtp_stream.pk_infos) do
table.insert(keys, seq)
end
table.sort(keys)
for i,seq in ipairs(keys) do
local rtp_info = rtp_stream.pk_infos[seq]
local marker = ""
if rtp_info.marker then marker = "set" end
text = text .. "\tpack_no: " .. rtp_info.pkno .. " \tseq: " .. seq .. " \ttimestamp: " .. rtp_info.timestamp .. " \tdelta: " .. rtp_info.delta .. "\tmarker: " .. marker .. "\n"
end
end
tw:append(text)
end
register_menu("Lua/RTP Timestamp Delta", menuable_myrtptap, MENU_TOOLS_UNSORTED)
------------
-- post dissector for showing timestamp delta value in tree item, which get data from variables generated by tap
------------
local my_rtp_timestamp_delta_proto = Proto("my_rtp_timestamp_delta", "RTP Timestamp Delta [HQX's plugins]")
---- my dissector
function my_rtp_timestamp_delta_proto.dissector(tvb, pinfo, tree)
local rtp_ssrc = { f_rtp_ssrc() }
local rtp_pt = { f_rtp_pt() }
local rtp_seq = { f_rtp_seq() }
local subtree = nil
for i,ssrc_f in ipairs(rtp_ssrc) do
local ssrc = ssrc_f.value
local pt = rtp_pt[i].value
local seq = rtp_seq[i].value
local key = getkey(ssrc, pt, pinfo)
local stream = rtp_streams[key]
local rtp_info = stream.pk_infos[seq]
if rtp_info then
if not subtree then subtree = tree:add(my_rtp_timestamp_delta_proto) end
subtree:add("PT=" .. stream.pt .. ", SSRC=" .. stream.ssrc .. ", Seq=" .. seq .. ", Timestamp=" .. rtp_info.timestamp .. ", Delta of timestamp=" .. rtp_info.delta)
end
end
end
-- register this dissector
register_postdissector(my_rtp_timestamp_delta_proto)
end