3 parse a MAVLink protocol XML file and generate a Wireshark LUA dissector 5 Copyright Holger Steinhaus 2012 6 Released under GNU GPL version 3 or later 9 1. python -m pymavlink.tools.mavgen --lang=WLua mymavlink.xml -o ~/.wireshark/plugins/mymavlink.lua 10 2. convert binary stream int .pcap file format (see ../examples/mav2pcap.py) 11 3. open the pcap file in Wireshark 13 from __future__
import print_function
15 from builtins
import range
19 from .
import mavparse, mavtemplate
26 if (mavlink_type==
'char'):
29 lua_t = mavlink_type.replace(
'_t',
'')
34 re_int = re.compile(
'^(u?)int(8|16|32|64)_t$')
35 int_parts = re_int.findall(mavlink_type)
37 return (
int(int_parts[0][1]) // 8)
38 elif mavlink_type ==
'float':
40 elif mavlink_type ==
'double':
42 elif mavlink_type ==
'char':
45 raise Exception(
'unsupported MAVLink type - please fix me')
49 '''work out the struct format for a type''' 56 'uint8_t_mavlink_version' :
'B',
65 if field.array_length:
66 if field.type
in [
'char',
'int8_t',
'uint8_t']:
67 return str(field.array_length)+
's' 68 return str(field.array_length)+map[field.type]
69 return map[field.type]
73 print(
"Generating preamble")
76 -- Wireshark dissector for the MAVLink protocol (please see http://qgroundcontrol.org/mavlink/start for details) 78 unknownFrameBeginOffset = 0 79 local bit = require "bit32" 80 mavlink_proto = Proto("mavlink_proto", "MAVLink protocol") 81 f = mavlink_proto.fields 83 -- from http://lua-users.org/wiki/TimeZone 84 local function get_timezone() 86 return os.difftime(now, os.time(os.date("!*t", now))) 88 local signature_time_ref = get_timezone() + os.time{year=2015, month=1, day=1, hour=0} 98 f.magic = ProtoField.uint8("mavlink_proto.magic", "Magic value / version", base.HEX) 99 f.length = ProtoField.uint8("mavlink_proto.length", "Payload length") 100 f.incompatibility_flag = ProtoField.uint8("mavlink_proto.incompatibility_flag", "Incompatibility flag") 101 f.compatibility_flag = ProtoField.uint8("mavlink_proto.compatibility_flag", "Compatibility flag") 102 f.sequence = ProtoField.uint8("mavlink_proto.sequence", "Packet sequence") 103 f.sysid = ProtoField.uint8("mavlink_proto.sysid", "System id", base.HEX) 104 f.compid = ProtoField.uint8("mavlink_proto.compid", "Component id", base.HEX) 105 f.msgid = ProtoField.uint24("mavlink_proto.msgid", "Message id", base.HEX) 106 f.payload = ProtoField.uint8("mavlink_proto.payload", "Payload", base.DEC, messageName) 107 f.crc = ProtoField.uint16("mavlink_proto.crc", "Message CRC", base.HEX) 108 f.signature_link = ProtoField.uint8("mavlink_proto.signature_link", "Link id", base.DEC) 109 f.signature_time = ProtoField.absolute_time("mavlink_proto.signature_time", "Time") 110 f.signature_signature = ProtoField.bytes("mavlink_proto.signature_signature", "Signature") 111 f.rawheader = ProtoField.bytes("mavlink_proto.rawheader", "Unparsable header fragment") 112 f.rawpayload = ProtoField.bytes("mavlink_proto.rawpayload", "Unparsable payload") 124 [${msgid}] = '${msgname}', 125 """, {
'msgid':msg.id,
'msgname':msg.name})
139 count = f.array_length
if f.array_length>0
else 1
142 if mtype ==
'char' and count > 1:
146 for i
in range(0,count):
148 array_text =
'[' +
str(i) +
']' 149 index_text =
'_' +
str(i)
156 f.${fmsg}_${fname}${findex} = ProtoField.${ftype}("mavlink_proto.${fmsg}_${fname}${findex}", "${fname}${farray} (${ftype})") 157 """, {
'fmsg':msg.name,
'ftype':ltype,
'fname':f.name,
'findex':index_text,
'farray':array_text})
159 t.write(outf,
'\n\n')
167 count = field.array_length
if field.array_length>0
else 1
176 for i
in range(0,count):
178 index_text =
'_' +
str(i)
184 tree:add_le(f.${fmsg}_${fname}${findex}, 0) 185 elseif (offset + ${fbytes} <= limit) then 186 tree:add_le(f.${fmsg}_${fname}${findex}, buffer(offset, ${fbytes})) 187 offset = offset + ${fbytes} 188 elseif (offset < limit) then 189 tree:add_le(f.${fmsg}_${fname}${findex}, buffer(offset, limit - offset)) 193 tree:add_le(f.${fmsg}_${fname}${findex}, 0) 196 """, {
'fname':field.name,
'ftype':mtype,
'fmsg': msg.name,
'fbytes':size,
'findex':index_text})
203 -- dissect payload of message type ${msgname} 204 function payload_fns.payload_${msgid}(buffer, tree, msgid, offset, limit) 205 local truncated = false 206 """, {
'msgid':msg.id,
'msgname':msg.name})
208 for f
in msg.ordered_fields:
224 -- dissector function 225 function mavlink_proto.dissector(buffer,pinfo,tree) 229 -- loop through the buffer to extract all the messages in the buffer 230 while (offset < buffer:len()) 232 msgCount = msgCount + 1 233 local subtree = tree:add (mavlink_proto, buffer(), "MAVLink Protocol ("..buffer:len()..")") 235 -- decode protocol version first 236 local version = buffer(offset,1):uint() 237 local protocolString = "" 241 if (version == 0xfe) then 242 protocolString = "MAVLink 1.0" 244 elseif (version == 0xfd) then 245 protocolString = "MAVLink 2.0" 247 elseif (version == 0x55) then 248 protocolString = "MAVLink 0.9" 251 protocolString = "unknown" 252 -- some unknown data found, record the begin offset 253 if (unknownFrameBeginOffset == 0) then 254 unknownFrameBeginOffset = offset 259 if (offset < buffer:len()) then 260 version = buffer(offset,1):uint() 262 -- no magic value found in the whole buffer. print the raw data and exit 263 if (unknownFrameBeginOffset ~= 0) then 264 if (msgCount == 1) then 265 pinfo.cols.info:set("Unknown message") 267 pinfo.cols.info:append(" Unknown message") 269 size = offset - unknownFrameBeginOffset 270 subtree:add(f.rawpayload, buffer(unknownFrameBeginOffset,size)) 271 unknownFrameBeginOffset = 0 278 if (unknownFrameBeginOffset ~= 0) then 279 pinfo.cols.info:append("Unknown message") 280 size = offset - unknownFrameBeginOffset 281 subtree:add(f.rawpayload, buffer(unknownFrameBeginOffset,size)) 282 unknownFrameBeginOffset = 0 287 -- some Wireshark decoration 288 pinfo.cols.protocol = protocolString 290 -- HEADER ---------------------------------------- 294 local incompatibility_flag 296 if (version == 0xfe) then 297 if (buffer:len() - 2 - offset > 6) then 299 local header = subtree:add("Header") 300 header:add(f.magic, buffer(offset,1), version) 303 length = buffer(offset,1) 304 header:add(f.length, length) 307 local sequence = buffer(offset,1) 308 header:add(f.sequence, sequence) 311 local sysid = buffer(offset,1) 312 header:add(f.sysid, sysid) 315 local compid = buffer(offset,1) 316 header:add(f.compid, compid) 319 pinfo.cols.src = "System: "..tostring(sysid:uint())..', Component: '..tostring(compid:uint()) 321 msgid = buffer(offset,1):uint() 322 header:add(f.msgid, buffer(offset,1), msgid) 325 -- handle truncated header 326 local hsize = buffer:len() - 2 - offset 327 subtree:add(f.rawheader, buffer(offset, hsize)) 328 offset = offset + hsize 330 elseif (version == 0xfd) then 331 if (buffer:len() - 2 - offset > 10) then 333 local header = subtree:add("Header") 334 header:add(f.magic, buffer(offset,1), version) 336 length = buffer(offset,1) 337 header:add(f.length, length) 339 incompatibility_flag = buffer(offset,1):uint() 340 header:add(f.incompatibility_flag, buffer(offset,1), incompatibility_flag) 342 local compatibility_flag = buffer(offset,1) 343 header:add(f.compatibility_flag, compatibility_flag) 345 local sequence = buffer(offset,1) 346 header:add(f.sequence, sequence) 348 local sysid = buffer(offset,1) 349 header:add(f.sysid, sysid) 351 local compid = buffer(offset,1) 352 header:add(f.compid, compid) 354 pinfo.cols.src = "System: "..tostring(sysid:uint())..', Component: '..tostring(compid:uint()) 355 msgid = buffer(offset,3):le_uint() 356 header:add(f.msgid, buffer(offset,3), msgid) 359 -- handle truncated header 360 local hsize = buffer:len() - 2 - offset 361 subtree:add(f.rawheader, buffer(offset, hsize)) 362 offset = offset + hsize 367 -- BODY ---------------------------------------- 369 -- dynamically call the type-specific payload dissector 371 local dissect_payload_fn = "payload_"..tostring(msgnr) 372 local fn = payload_fns[dissect_payload_fn] 373 local limit = buffer:len() - 2 376 length = length:uint() 381 if (offset + length < limit) then 382 limit = offset + length 386 pinfo.cols.info:append ("Unknown message type ") 387 subtree:add_expert_info(PI_MALFORMED, PI_ERROR, "Unknown message type") 388 size = buffer:len() - 2 - offset 389 subtree:add(f.rawpayload, buffer(offset,size)) 390 offset = offset + size 392 local payload = subtree:add(f.payload, msgid) 393 pinfo.cols.dst:set(messageName[msgid]) 394 if (msgCount == 1) then 395 -- first message should over write the TCP/UDP info 396 pinfo.cols.info = messageName[msgid] 398 pinfo.cols.info:append(" "..messageName[msgid]) 400 fn(buffer, payload, msgid, offset, limit) 404 -- CRC ---------------------------------------- 406 local crc = buffer(offset,2) 407 subtree:add_le(f.crc, crc) 410 -- SIGNATURE ---------------------------------- 412 if (version == 0xfd and incompatibility_flag == 0x01) then 413 local signature = subtree:add("Signature") 415 local link = buffer(offset,1) 416 signature:add(f.signature_link, link) 419 local signature_time = buffer(offset,6):le_uint64() 420 local time_secs = signature_time / 100000 421 local time_nsecs = (signature_time - (time_secs * 100000)) * 10000 422 signature:add(f.signature_time, buffer(offset,6), NSTime.new(signature_time_ref + time_secs:tonumber(), time_nsecs:tonumber())) 425 local signature_signature = buffer(offset,6) 426 signature:add(f.signature_signature, signature_signature) 439 print(
"Generating epilog")
442 -- bind protocol dissector to USER0 linktype 444 wtap_encap = DissectorTable.get("wtap_encap") 445 wtap_encap:add(wtap.USER0, mavlink_proto) 447 -- bind protocol dissector to port 14550 449 local udp_dissector_table = DissectorTable.get("udp.port") 450 udp_dissector_table:add(14550, mavlink_proto) 454 '''generate complete python implemenation''' 455 if basename.endswith(
'.lua'):
458 filename = basename +
'.lua' 464 msgs.extend(x.message)
466 filelist.append(os.path.basename(x.filename))
469 if xml[0].little_endian:
473 for f
in m.ordered_fields:
475 m.order_map = [ 0 ] * len(m.fieldnames)
476 for i
in range(0, len(m.fieldnames)):
477 m.order_map[i] = m.ordered_fieldnames.index(m.fieldnames[i])
479 print(
"Generating %s" % filename)
480 outf = open(filename,
"w")
499 print(
"Generated %s OK" % filename)
def generate_field_dissector(outf, msg, field)
def generate_packet_dis(outf)
def generate_body_fields(outf)
def generate_msg_table(outf, msgs)
def generate_epilog(outf)
def lua_type(mavlink_type)
def type_size(mavlink_type)
def generate_preamble(outf)
def generate_msg_fields(outf, msg)
def generate_payload_dissector(outf, msg)
def generate(basename, xml)