00001
00002 '''
00003 parse a MAVLink protocol XML file and generate a Wireshark LUA dissector
00004
00005 Copyright Holger Steinhaus 2012
00006 Released under GNU GPL version 3 or later
00007
00008 Instructions for use:
00009 1. python -m pymavlink.generator.mavgen --lang=wlua mymavlink.xml -o ~/.wireshark/plugins/mymavlink.lua
00010 2. convert binary stream int .pcap file format (see ../examples/mav2pcap.py)
00011 3. open the pcap file in Wireshark
00012 '''
00013
00014 import sys, textwrap, os, re
00015 from . import mavparse, mavtemplate
00016
00017 t = mavtemplate.MAVTemplate()
00018
00019
00020 def lua_type(mavlink_type):
00021
00022 if (mavlink_type=='char'):
00023 lua_t = 'uint8'
00024 else:
00025 lua_t = mavlink_type.replace('_t', '')
00026 return lua_t
00027
00028 def type_size(mavlink_type):
00029
00030 re_int = re.compile('^(u?)int(8|16|32|64)_t$')
00031 int_parts = re_int.findall(mavlink_type)
00032 if len(int_parts):
00033 return int(int_parts[0][1])/8
00034 elif mavlink_type == 'float':
00035 return 4
00036 elif mavlink_type == 'double':
00037 return 8
00038 elif mavlink_type == 'char':
00039 return 1
00040 else:
00041 raise Exception('unsupported MAVLink type - please fix me')
00042
00043
00044 def mavfmt(field):
00045 '''work out the struct format for a type'''
00046 map = {
00047 'float' : 'f',
00048 'double' : 'd',
00049 'char' : 'c',
00050 'int8_t' : 'b',
00051 'uint8_t' : 'B',
00052 'uint8_t_mavlink_version' : 'B',
00053 'int16_t' : 'h',
00054 'uint16_t' : 'H',
00055 'int32_t' : 'i',
00056 'uint32_t' : 'I',
00057 'int64_t' : 'q',
00058 'uint64_t' : 'Q',
00059 }
00060
00061 if field.array_length:
00062 if field.type in ['char', 'int8_t', 'uint8_t']:
00063 return str(field.array_length)+'s'
00064 return str(field.array_length)+map[field.type]
00065 return map[field.type]
00066
00067
00068 def generate_preamble(outf):
00069 print("Generating preamble")
00070 t.write(outf,
00071 """
00072 -- Wireshark dissector for the MAVLink protocol (please see http://qgroundcontrol.org/mavlink/start for details)
00073
00074 mavlink_proto = Proto("mavlink_proto", "MAVLink protocol")
00075 f = mavlink_proto.fields
00076
00077 payload_fns = {}
00078
00079 """ )
00080
00081
00082 def generate_body_fields(outf):
00083 t.write(outf,
00084 """
00085 f.magic = ProtoField.uint8("mavlink_proto.magic", "Magic value / version", base.HEX)
00086 f.length = ProtoField.uint8("mavlink_proto.length", "Payload length")
00087 f.sequence = ProtoField.uint8("mavlink_proto.sequence", "Packet sequence")
00088 f.sysid = ProtoField.uint8("mavlink_proto.sysid", "System id", base.HEX)
00089 f.compid = ProtoField.uint8("mavlink_proto.compid", "Component id", base.HEX)
00090 f.msgid = ProtoField.uint8("mavlink_proto.msgid", "Message id", base.HEX)
00091 f.crc = ProtoField.uint16("mavlink_proto.crc", "Message CRC", base.HEX)
00092 f.payload = ProtoField.uint8("mavlink_proto.crc", "Payload", base.DEC, messageName)
00093 f.rawheader = ProtoField.bytes("mavlink_proto.rawheader", "Unparsable header fragment")
00094 f.rawpayload = ProtoField.bytes("mavlink_proto.rawpayload", "Unparsable payload")
00095
00096 """)
00097
00098
00099 def generate_msg_table(outf, msgs):
00100 t.write(outf, """
00101 messageName = {
00102 """)
00103 for msg in msgs:
00104 assert isinstance(msg, mavparse.MAVType)
00105 t.write(outf, """
00106 [${msgid}] = '${msgname}',
00107 """, {'msgid':msg.id, 'msgname':msg.name})
00108
00109 t.write(outf, """
00110 }
00111
00112 """)
00113
00114
00115 def generate_msg_fields(outf, msg):
00116 assert isinstance(msg, mavparse.MAVType)
00117 for f in msg.fields:
00118 assert isinstance(f, mavparse.MAVField)
00119 mtype = f.type
00120 ltype = lua_type(mtype)
00121 count = f.array_length if f.array_length>0 else 1
00122
00123
00124 if mtype == 'char' and count > 1:
00125 count = 1
00126 ltype = 'string'
00127
00128 for i in range(0,count):
00129 if count>1:
00130 array_text = '[' + str(i) + ']'
00131 index_text = '_' + str(i)
00132 else:
00133 array_text = ''
00134 index_text = ''
00135
00136 t.write(outf,
00137 """
00138 f.${fmsg}_${fname}${findex} = ProtoField.${ftype}("mavlink_proto.${fmsg}_${fname}${findex}", "${fname}${farray} (${ftype})")
00139 """, {'fmsg':msg.name, 'ftype':ltype, 'fname':f.name, 'findex':index_text, 'farray':array_text})
00140
00141 t.write(outf, '\n\n')
00142
00143 def generate_field_dissector(outf, msg, field):
00144 assert isinstance(field, mavparse.MAVField)
00145
00146 mtype = field.type
00147 size = type_size(mtype)
00148 ltype = lua_type(mtype)
00149 count = field.array_length if field.array_length>0 else 1
00150
00151
00152 if mtype == 'char':
00153 size = count
00154 count = 1
00155
00156
00157
00158 for i in range(0,count):
00159 if count>1:
00160 index_text = '_' + str(i)
00161 else:
00162 index_text = ''
00163 t.write(outf,
00164 """
00165 tree:add_le(f.${fmsg}_${fname}${findex}, buffer(offset, ${fbytes}))
00166 offset = offset + ${fbytes}
00167
00168 """, {'fname':field.name, 'ftype':mtype, 'fmsg': msg.name, 'fbytes':size, 'findex':index_text})
00169
00170
00171 def generate_payload_dissector(outf, msg):
00172 assert isinstance(msg, mavparse.MAVType)
00173 t.write(outf,
00174 """
00175 -- dissect payload of message type ${msgname}
00176 function payload_fns.payload_${msgid}(buffer, tree, msgid, offset)
00177 """, {'msgid':msg.id, 'msgname':msg.name})
00178
00179 for f in msg.ordered_fields:
00180 generate_field_dissector(outf, msg, f)
00181
00182
00183 t.write(outf,
00184 """
00185 return offset
00186 end
00187
00188
00189 """)
00190
00191
00192 def generate_packet_dis(outf):
00193 t.write(outf,
00194 """
00195 -- dissector function
00196 function mavlink_proto.dissector(buffer,pinfo,tree)
00197 local offset = 0
00198
00199 local subtree = tree:add (mavlink_proto, buffer(), "MAVLink Protocol ("..buffer:len()..")")
00200
00201 -- decode protocol version first
00202 local version = buffer(offset,1):uint()
00203 local protocolString = ""
00204
00205 if (version == 0xfe) then
00206 protocolString = "MAVLink 1.0"
00207 elseif (version == 0x55) then
00208 protocolString = "MAVLink 0.9"
00209 else
00210 protocolString = "unknown"
00211 end
00212
00213 -- some Wireshark decoration
00214 pinfo.cols.protocol = protocolString
00215
00216 -- HEADER ----------------------------------------
00217
00218 local msgid
00219 if (buffer:len() - 2 - offset > 6) then
00220 -- normal header
00221 local header = subtree:add("Header")
00222 header:add(f.magic,version)
00223 offset = offset + 1
00224
00225 local length = buffer(offset,1)
00226 header:add(f.length, length)
00227 offset = offset + 1
00228
00229 local sequence = buffer(offset,1)
00230 header:add(f.sequence, sequence)
00231 offset = offset + 1
00232
00233 local sysid = buffer(offset,1)
00234 header:add(f.sysid, sysid)
00235 offset = offset + 1
00236
00237 local compid = buffer(offset,1)
00238 header:add(f.compid, compid)
00239 offset = offset + 1
00240
00241 pinfo.cols.src = "System: "..tostring(sysid:uint())..', Component: '..tostring(compid:uint())
00242
00243 msgid = buffer(offset,1)
00244 header:add(f.msgid, msgid)
00245 offset = offset + 1
00246 else
00247 -- handle truncated header
00248 local hsize = buffer:len() - 2 - offset
00249 subtree:add(f.rawheader, buffer(offset, hsize))
00250 offset = offset + hsize
00251 end
00252
00253
00254 -- BODY ----------------------------------------
00255
00256 -- dynamically call the type-specific payload dissector
00257 local msgnr = msgid:uint()
00258 local dissect_payload_fn = "payload_"..tostring(msgnr)
00259 local fn = payload_fns[dissect_payload_fn]
00260
00261 if (fn == nil) then
00262 pinfo.cols.info:append ("Unkown message type ")
00263 subtree:add_expert_info(PI_MALFORMED, PI_ERROR, "Unkown message type")
00264 size = buffer:len() - 2 - offset
00265 subtree:add(f.rawpayload, buffer(offset,size))
00266 offset = offset + size
00267 else
00268 local payload = subtree:add(f.payload, msgid)
00269 pinfo.cols.dst:set(messageName[msgid:uint()])
00270 pinfo.cols.info = messageName[msgid:uint()]
00271 offset = fn(buffer, payload, msgid, offset)
00272 end
00273
00274 -- CRC ----------------------------------------
00275 local crc = buffer(offset,2)
00276 subtree:add_le(f.crc, crc)
00277 offset = offset + 2
00278
00279 end
00280
00281
00282 """)
00283
00284
00285
00286 def generate_epilog(outf):
00287 print("Generating epilog")
00288 t.write(outf,
00289 """
00290 -- bind protocol dissector to USER0 linktype
00291
00292 wtap_encap = DissectorTable.get("wtap_encap")
00293 wtap_encap:add(wtap.USER0, mavlink_proto)
00294
00295 -- bind protocol dissector to port 14550
00296
00297 local udp_dissector_table = DissectorTable.get("udp.port")
00298 udp_dissector_table:add(14550, mavlink_proto)
00299 """)
00300
00301 def generate(basename, xml):
00302 '''generate complete python implemenation'''
00303 if basename.endswith('.lua'):
00304 filename = basename
00305 else:
00306 filename = basename + '.lua'
00307
00308 msgs = []
00309 enums = []
00310 filelist = []
00311 for x in xml:
00312 msgs.extend(x.message)
00313 enums.extend(x.enum)
00314 filelist.append(os.path.basename(x.filename))
00315
00316 for m in msgs:
00317 if xml[0].little_endian:
00318 m.fmtstr = '<'
00319 else:
00320 m.fmtstr = '>'
00321 for f in m.ordered_fields:
00322 m.fmtstr += mavfmt(f)
00323 m.order_map = [ 0 ] * len(m.fieldnames)
00324 for i in range(0, len(m.fieldnames)):
00325 m.order_map[i] = m.ordered_fieldnames.index(m.fieldnames[i])
00326
00327 print("Generating %s" % filename)
00328 outf = open(filename, "w")
00329 generate_preamble(outf)
00330 generate_msg_table(outf, msgs)
00331 generate_body_fields(outf)
00332
00333 for m in msgs:
00334 generate_msg_fields(outf, m)
00335
00336 for m in msgs:
00337 generate_payload_dissector(outf, m)
00338
00339 generate_packet_dis(outf)
00340
00341
00342
00343
00344
00345 generate_epilog(outf)
00346 outf.close()
00347 print("Generated %s OK" % filename)
00348