mavgen_wlua.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 '''
3 parse a MAVLink protocol XML file and generate a Wireshark LUA dissector
4 
5 Copyright Holger Steinhaus 2012
6 Released under GNU GPL version 3 or later
7 
8 Instructions for use:
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
12 '''
13 from __future__ import print_function
14 
15 from builtins import range
16 
17 import os
18 import re
19 from . import mavparse, mavtemplate
20 
22 
23 
24 def lua_type(mavlink_type):
25  # qnd typename conversion
26  if (mavlink_type=='char'):
27  lua_t = 'uint8'
28  else:
29  lua_t = mavlink_type.replace('_t', '')
30  return lua_t
31 
32 def type_size(mavlink_type):
33  # infer size of mavlink types
34  re_int = re.compile('^(u?)int(8|16|32|64)_t$')
35  int_parts = re_int.findall(mavlink_type)
36  if len(int_parts):
37  return (int(int_parts[0][1]) // 8)
38  elif mavlink_type == 'float':
39  return 4
40  elif mavlink_type == 'double':
41  return 8
42  elif mavlink_type == 'char':
43  return 1
44  else:
45  raise Exception('unsupported MAVLink type - please fix me')
46 
47 
48 def mavfmt(field):
49  '''work out the struct format for a type'''
50  map = {
51  'float' : 'f',
52  'double' : 'd',
53  'char' : 'c',
54  'int8_t' : 'b',
55  'uint8_t' : 'B',
56  'uint8_t_mavlink_version' : 'B',
57  'int16_t' : 'h',
58  'uint16_t' : 'H',
59  'int32_t' : 'i',
60  'uint32_t' : 'I',
61  'int64_t' : 'q',
62  'uint64_t' : 'Q',
63  }
64 
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]
70 
71 
73  print("Generating preamble")
74  t.write(outf,
75 """
76 -- Wireshark dissector for the MAVLink protocol (please see http://qgroundcontrol.org/mavlink/start for details)
77 
78 unknownFrameBeginOffset = 0
79 local bit = require "bit32"
80 mavlink_proto = Proto("mavlink_proto", "MAVLink protocol")
81 f = mavlink_proto.fields
82 
83 payload_fns = {}
84 
85 """ )
86 
87 
89  t.write(outf,
90 """
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")
101 
102 """)
103 
104 
105 def generate_msg_table(outf, msgs):
106  t.write(outf, """
107 messageName = {
108 """)
109  for msg in msgs:
110  assert isinstance(msg, mavparse.MAVType)
111  t.write(outf, """
112  [${msgid}] = '${msgname}',
113 """, {'msgid':msg.id, 'msgname':msg.name})
114 
115  t.write(outf, """
116 }
117 
118 """)
119 
120 
121 def generate_msg_fields(outf, msg):
122  assert isinstance(msg, mavparse.MAVType)
123  for f in msg.fields:
124  assert isinstance(f, mavparse.MAVField)
125  mtype = f.type
126  ltype = lua_type(mtype)
127  count = f.array_length if f.array_length>0 else 1
128 
129  # string is no array, but string of chars
130  if mtype == 'char' and count > 1:
131  count = 1
132  ltype = 'string'
133 
134  for i in range(0,count):
135  if count>1:
136  array_text = '[' + str(i) + ']'
137  index_text = '_' + str(i)
138  else:
139  array_text = ''
140  index_text = ''
141 
142  t.write(outf,
143 """
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})
146 
147  t.write(outf, '\n\n')
148 
149 def generate_field_dissector(outf, msg, field):
150  assert isinstance(field, mavparse.MAVField)
151 
152  mtype = field.type
153  size = type_size(mtype)
154  ltype = lua_type(mtype)
155  count = field.array_length if field.array_length>0 else 1
156 
157  # string is no array but string of chars
158  if mtype == 'char':
159  size = count
160  count = 1
161 
162  # handle arrays, but not strings
163 
164  for i in range(0,count):
165  if count>1:
166  index_text = '_' + str(i)
167  else:
168  index_text = ''
169  t.write(outf,
170 """
171  if (truncated) then
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))
178  offset = limit
179  truncated = true
180  else
181  tree:add_le(f.${fmsg}_${fname}${findex}, 0)
182  truncated = true
183  end
184 """, {'fname':field.name, 'ftype':mtype, 'fmsg': msg.name, 'fbytes':size, 'findex':index_text})
185 
186 
188  assert isinstance(msg, mavparse.MAVType)
189  t.write(outf,
190 """
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})
195 
196  for f in msg.ordered_fields:
197  generate_field_dissector(outf, msg, f)
198 
199 
200  t.write(outf,
201 """
202  return offset
203 end
204 
205 
206 """)
207 
208 
210  t.write(outf,
211 """
212 -- dissector function
213 function mavlink_proto.dissector(buffer,pinfo,tree)
214  local offset = 0
215  local msgCount = 0
216 
217  -- loop through the buffer to extract all the messages in the buffer
218  while (offset < buffer:len())
219  do
220  msgCount = msgCount + 1
221  local subtree = tree:add (mavlink_proto, buffer(), "MAVLink Protocol ("..buffer:len()..")")
222 
223  -- decode protocol version first
224  local version = buffer(offset,1):uint()
225  local protocolString = ""
226 
227  while (true)
228  do
229  if (version == 0xfe) then
230  protocolString = "MAVLink 1.0"
231  break
232  elseif (version == 0xfd) then
233  protocolString = "MAVLink 2.0"
234  break
235  elseif (version == 0x55) then
236  protocolString = "MAVLink 0.9"
237  break
238  else
239  protocolString = "unknown"
240  -- some unknown data found, record the begin offset
241  if (unknownFrameBeginOffset == 0) then
242  unknownFrameBeginOffset = offset
243  end
244 
245  offset = offset + 1
246 
247  if (offset < buffer:len()) then
248  version = buffer(offset,1):uint()
249  else
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")
254  else
255  pinfo.cols.info:append(" Unknown message")
256  end
257  size = offset - unknownFrameBeginOffset
258  subtree:add(f.rawpayload, buffer(unknownFrameBeginOffset,size))
259  unknownFrameBeginOffset = 0
260  end
261  return
262  end
263  end
264  end
265 
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
271  -- jump to next loop
272  break
273  end
274 
275  -- some Wireshark decoration
276  pinfo.cols.protocol = protocolString
277 
278  -- HEADER ----------------------------------------
279 
280  local msgid
281  local length
282 
283  if (version == 0xfe) then
284  if (buffer:len() - 2 - offset > 6) then
285  -- normal header
286  local header = subtree:add("Header")
287  header:add(f.magic, buffer(offset,1), version)
288  offset = offset + 1
289 
290  length = buffer(offset,1)
291  header:add(f.length, length)
292  offset = offset + 1
293 
294  local sequence = buffer(offset,1)
295  header:add(f.sequence, sequence)
296  offset = offset + 1
297 
298  local sysid = buffer(offset,1)
299  header:add(f.sysid, sysid)
300  offset = offset + 1
301 
302  local compid = buffer(offset,1)
303  header:add(f.compid, compid)
304  offset = offset + 1
305 
306  pinfo.cols.src = "System: "..tostring(sysid:uint())..', Component: '..tostring(compid:uint())
307 
308  msgid = buffer(offset,1):uint()
309  header:add(f.msgid, buffer(offset,1), msgid)
310  offset = offset + 1
311  else
312  -- handle truncated header
313  local hsize = buffer:len() - 2 - offset
314  subtree:add(f.rawheader, buffer(offset, hsize))
315  offset = offset + hsize
316  end
317  elseif (version == 0xfd) then
318  if (buffer:len() - 2 - offset > 10) then
319  -- normal header
320  local header = subtree:add("Header")
321  header:add(f.magic, buffer(offset,1), version)
322  offset = offset + 1
323  length = buffer(offset,1)
324  header:add(f.length, length)
325  offset = offset + 3
326  local sequence = buffer(offset,1)
327  header:add(f.sequence, sequence)
328  offset = offset + 1
329  local sysid = buffer(offset,1)
330  header:add(f.sysid, sysid)
331  offset = offset + 1
332  local compid = buffer(offset,1)
333  header:add(f.compid, compid)
334  offset = offset + 1
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)
338  offset = offset + 3
339  else
340  -- handle truncated header
341  local hsize = buffer:len() - 2 - offset
342  subtree:add(f.rawheader, buffer(offset, hsize))
343  offset = offset + hsize
344  end
345  end
346 
347 
348  -- BODY ----------------------------------------
349 
350  -- dynamically call the type-specific payload dissector
351  local msgnr = msgid
352  local dissect_payload_fn = "payload_"..tostring(msgnr)
353  local fn = payload_fns[dissect_payload_fn]
354  local limit = buffer:len() - 2
355 
356  if (length) then
357  length = length:uint()
358  else
359  length = 0
360  end
361 
362  if (offset + length < limit) then
363  limit = offset + length
364  end
365 
366  if (fn == nil) then
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
372  else
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]
378  else
379  pinfo.cols.info:append(" "..messageName[msgid])
380  end
381  fn(buffer, payload, msgid, offset, limit)
382  offset = limit
383  end
384 
385  -- CRC ----------------------------------------
386  local crc = buffer(offset,2)
387  subtree:add_le(f.crc, crc)
388  offset = offset + 2
389 
390  end
391 end
392 
393 
394 """)
395 
396 
397 
398 def generate_epilog(outf):
399  print("Generating epilog")
400  t.write(outf,
401 """
402 -- bind protocol dissector to USER0 linktype
403 
404 wtap_encap = DissectorTable.get("wtap_encap")
405 wtap_encap:add(wtap.USER0, mavlink_proto)
406 
407 -- bind protocol dissector to port 14550
408 
409 local udp_dissector_table = DissectorTable.get("udp.port")
410 udp_dissector_table:add(14550, mavlink_proto)
411 """)
412 
413 def generate(basename, xml):
414  '''generate complete python implemenation'''
415  if basename.endswith('.lua'):
416  filename = basename
417  else:
418  filename = basename + '.lua'
419 
420  msgs = []
421  enums = []
422  filelist = []
423  for x in xml:
424  msgs.extend(x.message)
425  enums.extend(x.enum)
426  filelist.append(os.path.basename(x.filename))
427 
428  for m in msgs:
429  if xml[0].little_endian:
430  m.fmtstr = '<'
431  else:
432  m.fmtstr = '>'
433  for f in m.ordered_fields:
434  m.fmtstr += mavfmt(f)
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])
438 
439  print("Generating %s" % filename)
440  outf = open(filename, "w")
441  generate_preamble(outf)
442  generate_msg_table(outf, msgs)
444 
445  for m in msgs:
446  generate_msg_fields(outf, m)
447 
448  for m in msgs:
450 
451  generate_packet_dis(outf)
452 # generate_enums(outf, enums)
453 # generate_message_ids(outf, msgs)
454 # generate_classes(outf, msgs)
455 # generate_mavlink_class(outf, msgs, xml[0])
456 # generate_methods(outf, msgs)
457  generate_epilog(outf)
458  outf.close()
459  print("Generated %s OK" % filename)
460 


mavlink
Author(s): Lorenz Meier
autogenerated on Sun Apr 7 2019 02:06:02