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 91 f.magic = ProtoField.uint8("mavlink_proto.magic", "Magic value / version", base.HEX) 92 f.length = ProtoField.uint8("mavlink_proto.length", "Payload length") 93 f.sequence = ProtoField.uint8("mavlink_proto.sequence", "Packet sequence") 94 f.sysid = ProtoField.uint8("mavlink_proto.sysid", "System id", base.HEX) 95 f.compid = ProtoField.uint8("mavlink_proto.compid", "Component id", base.HEX) 96 f.msgid = ProtoField.uint24("mavlink_proto.msgid", "Message id", base.HEX) 97 f.crc = ProtoField.uint16("mavlink_proto.crc", "Message CRC", base.HEX) 98 f.payload = ProtoField.uint8("mavlink_proto.payload", "Payload", base.DEC, messageName) 99 f.rawheader = ProtoField.bytes("mavlink_proto.rawheader", "Unparsable header fragment") 100 f.rawpayload = ProtoField.bytes("mavlink_proto.rawpayload", "Unparsable payload") 112 [${msgid}] = '${msgname}', 113 """, {
'msgid':msg.id,
'msgname':msg.name})
127 count = f.array_length
if f.array_length>0
else 1
130 if mtype ==
'char' and count > 1:
134 for i
in range(0,count):
136 array_text =
'[' +
str(i) +
']' 137 index_text =
'_' +
str(i)
144 f.${fmsg}_${fname}${findex} = ProtoField.${ftype}("mavlink_proto.${fmsg}_${fname}${findex}", "${fname}${farray} (${ftype})") 145 """, {
'fmsg':msg.name,
'ftype':ltype,
'fname':f.name,
'findex':index_text,
'farray':array_text})
147 t.write(outf,
'\n\n')
155 count = field.array_length
if field.array_length>0
else 1
164 for i
in range(0,count):
166 index_text =
'_' +
str(i)
172 tree:add_le(f.${fmsg}_${fname}${findex}, 0) 173 elseif (offset + ${fbytes} <= limit) then 174 tree:add_le(f.${fmsg}_${fname}${findex}, buffer(offset, ${fbytes})) 175 offset = offset + ${fbytes} 176 elseif (offset < limit) then 177 tree:add_le(f.${fmsg}_${fname}${findex}, buffer(offset, limit - offset)) 181 tree:add_le(f.${fmsg}_${fname}${findex}, 0) 184 """, {
'fname':field.name,
'ftype':mtype,
'fmsg': msg.name,
'fbytes':size,
'findex':index_text})
191 -- dissect payload of message type ${msgname} 192 function payload_fns.payload_${msgid}(buffer, tree, msgid, offset, limit) 193 local truncated = false 194 """, {
'msgid':msg.id,
'msgname':msg.name})
196 for f
in msg.ordered_fields:
212 -- dissector function 213 function mavlink_proto.dissector(buffer,pinfo,tree) 217 -- loop through the buffer to extract all the messages in the buffer 218 while (offset < buffer:len()) 220 msgCount = msgCount + 1 221 local subtree = tree:add (mavlink_proto, buffer(), "MAVLink Protocol ("..buffer:len()..")") 223 -- decode protocol version first 224 local version = buffer(offset,1):uint() 225 local protocolString = "" 229 if (version == 0xfe) then 230 protocolString = "MAVLink 1.0" 232 elseif (version == 0xfd) then 233 protocolString = "MAVLink 2.0" 235 elseif (version == 0x55) then 236 protocolString = "MAVLink 0.9" 239 protocolString = "unknown" 240 -- some unknown data found, record the begin offset 241 if (unknownFrameBeginOffset == 0) then 242 unknownFrameBeginOffset = offset 247 if (offset < buffer:len()) then 248 version = buffer(offset,1):uint() 250 -- no magic value found in the whole buffer. print the raw data and exit 251 if (unknownFrameBeginOffset ~= 0) then 252 if (msgCount == 1) then 253 pinfo.cols.info:set("Unknown message") 255 pinfo.cols.info:append(" Unknown message") 257 size = offset - unknownFrameBeginOffset 258 subtree:add(f.rawpayload, buffer(unknownFrameBeginOffset,size)) 259 unknownFrameBeginOffset = 0 266 if (unknownFrameBeginOffset ~= 0) then 267 pinfo.cols.info:append("Unknown message") 268 size = offset - unknownFrameBeginOffset 269 subtree:add(f.rawpayload, buffer(unknownFrameBeginOffset,size)) 270 unknownFrameBeginOffset = 0 275 -- some Wireshark decoration 276 pinfo.cols.protocol = protocolString 278 -- HEADER ---------------------------------------- 283 if (version == 0xfe) then 284 if (buffer:len() - 2 - offset > 6) then 286 local header = subtree:add("Header") 287 header:add(f.magic, buffer(offset,1), version) 290 length = buffer(offset,1) 291 header:add(f.length, length) 294 local sequence = buffer(offset,1) 295 header:add(f.sequence, sequence) 298 local sysid = buffer(offset,1) 299 header:add(f.sysid, sysid) 302 local compid = buffer(offset,1) 303 header:add(f.compid, compid) 306 pinfo.cols.src = "System: "..tostring(sysid:uint())..', Component: '..tostring(compid:uint()) 308 msgid = buffer(offset,1):uint() 309 header:add(f.msgid, buffer(offset,1), msgid) 312 -- handle truncated header 313 local hsize = buffer:len() - 2 - offset 314 subtree:add(f.rawheader, buffer(offset, hsize)) 315 offset = offset + hsize 317 elseif (version == 0xfd) then 318 if (buffer:len() - 2 - offset > 10) then 320 local header = subtree:add("Header") 321 header:add(f.magic, buffer(offset,1), version) 323 length = buffer(offset,1) 324 header:add(f.length, length) 326 local sequence = buffer(offset,1) 327 header:add(f.sequence, sequence) 329 local sysid = buffer(offset,1) 330 header:add(f.sysid, sysid) 332 local compid = buffer(offset,1) 333 header:add(f.compid, compid) 335 pinfo.cols.src = "System: "..tostring(sysid:uint())..', Component: '..tostring(compid:uint()) 336 msgid = buffer(offset,3):le_uint() 337 header:add(f.msgid, buffer(offset,3), msgid) 340 -- handle truncated header 341 local hsize = buffer:len() - 2 - offset 342 subtree:add(f.rawheader, buffer(offset, hsize)) 343 offset = offset + hsize 348 -- BODY ---------------------------------------- 350 -- dynamically call the type-specific payload dissector 352 local dissect_payload_fn = "payload_"..tostring(msgnr) 353 local fn = payload_fns[dissect_payload_fn] 354 local limit = buffer:len() - 2 357 length = length:uint() 362 if (offset + length < limit) then 363 limit = offset + length 367 pinfo.cols.info:append ("Unknown message type ") 368 subtree:add_expert_info(PI_MALFORMED, PI_ERROR, "Unknown message type") 369 size = buffer:len() - 2 - offset 370 subtree:add(f.rawpayload, buffer(offset,size)) 371 offset = offset + size 373 local payload = subtree:add(f.payload, msgid) 374 pinfo.cols.dst:set(messageName[msgid]) 375 if (msgCount == 1) then 376 -- first message should over wirte the TCP/UDP info 377 pinfo.cols.info = messageName[msgid] 379 pinfo.cols.info:append(" "..messageName[msgid]) 381 fn(buffer, payload, msgid, offset, limit) 385 -- CRC ---------------------------------------- 386 local crc = buffer(offset,2) 387 subtree:add_le(f.crc, crc) 399 print(
"Generating epilog")
402 -- bind protocol dissector to USER0 linktype 404 wtap_encap = DissectorTable.get("wtap_encap") 405 wtap_encap:add(wtap.USER0, mavlink_proto) 407 -- bind protocol dissector to port 14550 409 local udp_dissector_table = DissectorTable.get("udp.port") 410 udp_dissector_table:add(14550, mavlink_proto) 414 '''generate complete python implemenation''' 415 if basename.endswith(
'.lua'):
418 filename = basename +
'.lua' 424 msgs.extend(x.message)
426 filelist.append(os.path.basename(x.filename))
429 if xml[0].little_endian:
433 for f
in m.ordered_fields:
435 m.order_map = [ 0 ] * len(m.fieldnames)
436 for i
in range(0, len(m.fieldnames)):
437 m.order_map[i] = m.ordered_fieldnames.index(m.fieldnames[i])
439 print(
"Generating %s" % filename)
440 outf = open(filename,
"w")
459 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)