Revert "TODO tools/pidl/lib/Parse/Pidl/Wireshark/NDR.pm \@VALUEREF\@"
[wireshark-sm.git] / doc / extcap_example.py
blob60ec7e930cef7f7f07be870420e90b9909e151eb
1 #!/usr/bin/env python3
3 # Copyright 2014 Roland Knall <rknall [AT] gmail.com>
5 # Wireshark - Network traffic analyzer
6 # By Gerald Combs <gerald@wireshark.org>
7 # Copyright 1998 Gerald Combs
9 # SPDX-License-Identifier: GPL-2.0-or-later
12 r"""
13 This is a generic example, which produces pcap packages every n seconds, and
14 is configurable via extcap options.
16 @note
18 To use this script on Windows, please generate an extcap_example.bat inside
19 the extcap folder, with the following content:
21 -------
22 @echo off
23 C:\Windows\py.exe C:\Path\to\extcap_example.py %*
24 -------
26 Windows is not able to execute Python scripts directly, which also goes for all
27 other script-based formats beside VBScript
30 """
32 from __future__ import print_function
34 import sys
35 import re
36 import argparse
37 import time
38 import struct
39 from threading import Thread
41 ERROR_USAGE = 0
42 ERROR_ARG = 1
43 ERROR_INTERFACE = 2
44 ERROR_FIFO = 3
45 ERROR_DELAY = 4
47 CTRL_CMD_INITIALIZED = 0
48 CTRL_CMD_SET = 1
49 CTRL_CMD_ADD = 2
50 CTRL_CMD_REMOVE = 3
51 CTRL_CMD_ENABLE = 4
52 CTRL_CMD_DISABLE = 5
53 CTRL_CMD_STATUSBAR = 6
54 CTRL_CMD_INFORMATION = 7
55 CTRL_CMD_WARNING = 8
56 CTRL_CMD_ERROR = 9
58 CTRL_ARG_MESSAGE = 0
59 CTRL_ARG_DELAY = 1
60 CTRL_ARG_VERIFY = 2
61 CTRL_ARG_BUTTON = 3
62 CTRL_ARG_HELP = 4
63 CTRL_ARG_RESTORE = 5
64 CTRL_ARG_LOGGER = 6
65 CTRL_ARG_NONE = 255
67 initialized = False
68 message = ''
69 delay = 0.0
70 verify = False
71 button = False
72 button_disabled = False
74 """
75 This code has been taken from http://stackoverflow.com/questions/5943249/python-argparse-and-controlling-overriding-the-exit-status-code - originally developed by Rob Cowie http://stackoverflow.com/users/46690/rob-cowie
76 """
77 class ArgumentParser(argparse.ArgumentParser):
78 def _get_action_from_name(self, name):
79 """Given a name, get the Action instance registered with this parser.
80 If only it were made available in the ArgumentError object. It is
81 passed as its first arg...
82 """
83 container = self._actions
84 if name is None:
85 return None
86 for action in container:
87 if '/'.join(action.option_strings) == name:
88 return action
89 elif action.metavar == name:
90 return action
91 elif action.dest == name:
92 return action
94 def error(self, message):
95 exc = sys.exc_info()[1]
96 if exc:
97 exc.argument = self._get_action_from_name(exc.argument_name)
98 raise exc
99 super(ArgumentParser, self).error(message)
101 #### EXTCAP FUNCTIONALITY
103 """@brief Extcap configuration
104 This method prints the extcap configuration, which will be picked up by the
105 interface in Wireshark to present a interface specific configuration for
106 this extcap plugin
108 def extcap_config(interface, option):
109 args = []
110 values = []
111 multi_values = []
113 args.append((0, '--delay', 'Time delay', 'Time delay between packages', 'integer', '{range=1,15}{default=5}'))
114 args.append((1, '--message', 'Message', 'Package message content', 'string', '{required=true}{placeholder=Please enter a message here ...}'))
115 args.append((2, '--verify', 'Verify', 'Verify package content', 'boolflag', '{default=yes}'))
116 args.append((3, '--remote', 'Remote Channel', 'Remote Channel Selector', 'selector', '{reload=true}{placeholder=Load interfaces ...}'))
117 args.append((4, '--fake_ip', 'Fake IP Address', 'Use this ip address as sender', 'string', '{save=false}{validation=\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b}'))
118 args.append((5, '--ltest', 'Long Test', 'Long Test Value', 'long', '{default=123123123123123123}{group=Numeric Values}'))
119 args.append((6, '--d1test', 'Double 1 Test', 'Long Test Value', 'double', '{default=123.456}{group=Numeric Values}'))
120 args.append((7, '--d2test', 'Double 2 Test', 'Long Test Value', 'double', '{default= 123,456}{group=Numeric Values}'))
121 args.append((8, '--password', 'Password', 'Package message password', 'password', ''))
122 args.append((9, '--ts', 'Start Time', 'Capture start time', 'timestamp', '{group=Time / Log}'))
123 args.append((10, '--logfile', 'Log File Test', 'The Log File Test', 'fileselect', '{group=Time / Log}'))
124 args.append((11, '--radio', 'Radio Test', 'Radio Test Value', 'radio', '{group=Selection}'))
125 args.append((12, '--multi', 'MultiCheck Test', 'MultiCheck Test Value', 'multicheck', '{group=Selection}'))
127 if option == "remote":
128 values.append((3, "if1", "Remote Interface 1", "false"))
129 values.append((3, "if2", "Remote Interface 2", "true"))
130 values.append((3, "if3", "Remote Interface 3", "false"))
131 values.append((3, "if4", "Remote Interface 4", "false"))
133 if option == "radio":
134 values.append((11, "r1", "Radio Option 1", "false"))
135 values.append((11, "r2", "Radio Option 2", "false"))
136 values.append((11, "r3", "Radio Option 3", "true"))
139 if len(option) <= 0:
140 for arg in args:
141 print("arg {number=%d}{call=%s}{display=%s}{tooltip=%s}{type=%s}%s" % arg)
143 values.append((3, "if1", "Remote1", "true"))
144 values.append((3, "if2", "Remote2", "false"))
146 values.append((11, "r1", "Radio1", "false"))
147 values.append((11, "r2", "Radio2", "true"))
149 if len(option) <= 0:
150 multi_values.append(((12, "m1", "Checkable Parent 1", "false", "true"), None))
151 multi_values.append(((12, "m1c1", "Checkable Child 1", "false", "true"), "m1"))
152 multi_values.append(((12, "m1c1g1", "Uncheckable Grandchild", "false", "false"), "m1c1"))
153 multi_values.append(((12, "m1c2", "Checkable Child 2", "false", "true"), "m1"))
154 multi_values.append(((12, "m2", "Checkable Parent 2", "false", "true"), None))
155 multi_values.append(((12, "m2c1", "Checkable Child 1", "false", "true"), "m2"))
156 multi_values.append(((12, "m2c1g1", "Checkable Grandchild", "false", "true"), "m2c1"))
157 multi_values.append(((12, "m2c2", "Uncheckable Child 2", "false", "false"), "m2"))
158 multi_values.append(((12, "m2c2g1", "Uncheckable Grandchild", "false", "false"), "m2c2"))
160 for value in values:
161 print("value {arg=%d}{value=%s}{display=%s}{default=%s}" % value)
163 for (value, parent) in multi_values:
164 sentence = "value {arg=%d}{value=%s}{display=%s}{default=%s}{enabled=%s}" % value
165 extra = "{parent=%s}" % parent if parent else ""
166 print("".join((sentence, extra)))
169 def extcap_version():
170 print("extcap {version=1.0}{help=https://www.wireshark.org}{display=Example extcap interface}")
172 def extcap_interfaces():
173 print("extcap {version=1.0}{help=https://www.wireshark.org}{display=Example extcap interface}")
174 print("interface {value=example1}{display=Example interface 1 for extcap}")
175 print("interface {value=example2}{display=Example interface 2 for extcap}")
176 print("control {number=%d}{type=string}{display=Message}{tooltip=Package message content. Must start with a capital letter.}{placeholder=Enter package message content here ...}{validation=^[A-Z]+}" % CTRL_ARG_MESSAGE)
177 print("control {number=%d}{type=selector}{display=Time delay}{tooltip=Time delay between packages}" % CTRL_ARG_DELAY)
178 print("control {number=%d}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}" % CTRL_ARG_VERIFY)
179 print("control {number=%d}{type=button}{display=Turn on}{tooltip=Turn on or off}" % CTRL_ARG_BUTTON)
180 print("control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Show help}" % CTRL_ARG_HELP)
181 print("control {number=%d}{type=button}{role=restore}{display=Restore}{tooltip=Restore default values}" % CTRL_ARG_RESTORE)
182 print("control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}" % CTRL_ARG_LOGGER)
183 print("value {control=%d}{value=1}{display=1}" % CTRL_ARG_DELAY)
184 print("value {control=%d}{value=2}{display=2}" % CTRL_ARG_DELAY)
185 print("value {control=%d}{value=3}{display=3}" % CTRL_ARG_DELAY)
186 print("value {control=%d}{value=4}{display=4}" % CTRL_ARG_DELAY)
187 print("value {control=%d}{value=5}{display=5}{default=true}" % CTRL_ARG_DELAY)
188 print("value {control=%d}{value=60}{display=60}" % CTRL_ARG_DELAY)
191 def extcap_dlts(interface):
192 if interface == '1':
193 print("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}")
194 elif interface == '2':
195 print("dlt {number=148}{name=USER1}{display=Demo Implementation for Extcap}")
197 def validate_capture_filter(capture_filter):
198 if capture_filter != "filter" and capture_filter != "valid":
199 print("Illegal capture filter")
203 ### FAKE DATA GENERATOR
205 Extcap capture routine
206 This routine simulates a capture by any kind of user defined device. The parameters
207 are user specified and must be handled by the extcap.
209 The data captured inside this routine is fake, so change this routine to present
210 your own input data, or call your own capture program via Popen for example. See
212 for more details.
215 def unsigned(n):
216 return int(n) & 0xFFFFFFFF
218 def pcap_fake_header():
220 header = bytearray()
221 header += struct.pack('<L', int('a1b2c3d4', 16))
222 header += struct.pack('<H', unsigned(2)) # Pcap Major Version
223 header += struct.pack('<H', unsigned(4)) # Pcap Minor Version
224 header += struct.pack('<I', int(0)) # Timezone
225 header += struct.pack('<I', int(0)) # Accuracy of timestamps
226 header += struct.pack('<L', int('0000ffff', 16)) # Max Length of capture frame
227 header += struct.pack('<L', unsigned(1)) # Ethernet
228 return header
230 # Calculates and returns the IP checksum based on the given IP Header
231 def ip_checksum(iph):
232 #split into bytes
233 words = splitN(''.join(iph.split()), 4) # TODO splitN() func undefined, this code will fail
234 csum = 0
235 for word in words:
236 csum += int(word, base=16)
237 csum += (csum >> 16)
238 csum = csum & 0xFFFF ^ 0xFFFF
239 return csum
241 iterateCounter = 0
243 def pcap_fake_package(message, fake_ip):
244 global iterateCounter
245 pcap = bytearray()
246 #length = 14 bytes [ eth ] + 20 bytes [ ip ] + messagelength
248 caplength = len(message) + 14 + 20
249 timestamp = int(time.time())
251 pcap += struct.pack('<L', unsigned(timestamp)) # timestamp seconds
252 pcap += struct.pack('<L', 0x00) # timestamp nanoseconds
253 pcap += struct.pack('<L', unsigned(caplength)) # length captured
254 pcap += struct.pack('<L', unsigned(caplength)) # length in frame
256 # ETH
257 destValue = '2900'
258 srcValue = '3400'
259 if (iterateCounter % 2 == 0):
260 x = srcValue
261 srcValue = destValue
262 destValue = x
264 pcap += struct.pack('h', int(destValue, 16)) # dest mac
265 pcap += struct.pack('h', int(destValue, 16)) # dest mac
266 pcap += struct.pack('h', int(destValue, 16)) # dest mac
267 pcap += struct.pack('h', int(srcValue, 16)) # source mac
268 pcap += struct.pack('h', int(srcValue, 16)) # source mac
269 pcap += struct.pack('h', int(srcValue, 16)) # source mac
270 pcap += struct.pack('<h', unsigned(8)) # protocol (ip)
271 iterateCounter += 1
273 # IP
274 pcap += struct.pack('b', int('45', 16)) # IP version
275 pcap += struct.pack('b', int('0', 16)) #
276 pcap += struct.pack('>H', unsigned(len(message)+20)) # length of data + payload
277 pcap += struct.pack('<H', int('0', 16)) # Identification
278 pcap += struct.pack('b', int('40', 16)) # Don't fragment
279 pcap += struct.pack('b', int('0', 16)) # Fragment Offset
280 pcap += struct.pack('b', int('40', 16))
281 pcap += struct.pack('B', 0xFE) # Protocol (2 = unspecified)
282 pcap += struct.pack('<H', int('0000', 16)) # Checksum
284 parts = fake_ip.split('.')
285 ipadr = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
286 pcap += struct.pack('>L', ipadr) # Source IP
287 pcap += struct.pack('>L', int('7F000001', 16)) # Dest IP
289 pcap += message
291 return pcap
293 def control_read(fn):
294 try:
295 header = fn.read(6)
296 sp, _, length, arg, typ = struct.unpack('>sBHBB', header)
297 if length > 2:
298 payload = fn.read(length - 2).decode('utf-8', 'replace')
299 else:
300 payload = ''
301 return arg, typ, payload
302 except Exception:
303 return None, None, None
305 def control_read_thread(control_in, fn_out):
306 global initialized, message, delay, verify, button, button_disabled
307 with open(control_in, 'rb', 0) as fn:
308 arg = 0
309 while arg is not None:
310 arg, typ, payload = control_read(fn)
311 log = ''
312 if typ == CTRL_CMD_INITIALIZED:
313 initialized = True
314 elif arg == CTRL_ARG_MESSAGE:
315 message = payload
316 log = "Message = " + payload
317 elif arg == CTRL_ARG_DELAY:
318 delay = float(payload)
319 log = "Time delay = " + payload
320 elif arg == CTRL_ARG_VERIFY:
321 # Only read this after initialized
322 if initialized:
323 verify = (payload[0] != '\0')
324 log = "Verify = " + str(verify)
325 control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_STATUSBAR, "Verify changed")
326 elif arg == CTRL_ARG_BUTTON:
327 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_DISABLE, "")
328 button_disabled = True
329 if button:
330 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn on")
331 button = False
332 log = "Button turned off"
333 else:
334 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn off")
335 button = True
336 log = "Button turned on"
338 if len(log) > 0:
339 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log + "\n")
341 def control_write(fn, arg, typ, payload):
342 packet = bytearray()
343 packet += struct.pack('>sBHBB', b'T', 0, len(payload) + 2, arg, typ)
344 if sys.version_info[0] >= 3 and isinstance(payload, str):
345 packet += payload.encode('utf-8')
346 else:
347 packet += payload
348 fn.write(packet)
350 def control_write_defaults(fn_out):
351 global initialized, message, delay, verify
353 while not initialized:
354 time.sleep(.1) # Wait for initial control values
356 # Write startup configuration to Toolbar controls
357 control_write(fn_out, CTRL_ARG_MESSAGE, CTRL_CMD_SET, message)
358 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_SET, str(int(delay)))
359 control_write(fn_out, CTRL_ARG_VERIFY, CTRL_CMD_SET, struct.pack('B', verify))
361 for i in range(1, 16):
362 item = '%d\x00%d sec' % (i, i)
363 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_ADD, item)
365 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_REMOVE, str(60))
367 def extcap_capture(interface, fifo, control_in, control_out, in_delay, in_verify, in_message, remote, fake_ip):
368 global message, delay, verify, button_disabled
369 delay = in_delay if in_delay != 0 else 5
370 message = in_message
371 verify = in_verify
372 counter = 1
373 fn_out = None
375 data = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
376 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nost
377 rud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
378 aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugi
379 at nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culp
380 a qui officia deserunt mollit anim id est laborum. """
382 with open(fifo, 'wb', 0) as fh:
383 fh.write(pcap_fake_header())
385 if control_out is not None:
386 fn_out = open(control_out, 'wb', 0)
387 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_SET, "Log started at " + time.strftime("%c") + "\n")
389 if control_in is not None:
390 # Start reading thread
391 thread = Thread(target=control_read_thread, args=(control_in, fn_out))
392 thread.start()
394 if fn_out is not None:
395 control_write_defaults(fn_out)
397 dataPackage = int(0)
398 dataTotal = int(len(data) / 20) + 1
400 while True:
401 if fn_out is not None:
402 log = "Received packet #" + str(counter) + "\n"
403 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log)
404 counter = counter + 1
406 if button_disabled:
407 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_ENABLE, "")
408 control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_INFORMATION, "Turn action finished.")
409 button_disabled = False
411 if (dataPackage * 20 > len(data)):
412 dataPackage = 0
413 dataSub = data[dataPackage * 20:(dataPackage + 1) * 20]
414 dataPackage += 1
416 out = ("%c%s%c%c%c%s%c%s%c" % (len(remote), remote.strip(), dataPackage, dataTotal, len(dataSub), dataSub.strip(), len(message), message.strip(), verify)).encode("utf8")
417 fh.write(pcap_fake_package(out, fake_ip))
418 time.sleep(delay)
420 thread.join()
421 if fn_out is not None:
422 fn_out.close()
424 def extcap_close_fifo(fifo):
425 # This is apparently needed to workaround an issue on Windows/macOS
426 # where the message cannot be read. (really?)
427 fh = open(fifo, 'wb', 0)
428 fh.close()
430 ####
432 def usage():
433 print("Usage: %s <--extcap-interfaces | --extcap-dlts | --extcap-interface | --extcap-config | --capture | --extcap-capture-filter | --fifo>" % sys.argv[0] )
435 if __name__ == '__main__':
436 interface = ""
437 option = ""
439 # Capture options
440 delay = 0
441 message = ""
442 fake_ip = ""
443 ts = 0
445 parser = ArgumentParser(
446 prog="Extcap Example",
447 description="Extcap example program for Python"
450 # Extcap Arguments
451 parser.add_argument("--capture", help="Start the capture routine", action="store_true" )
452 parser.add_argument("--extcap-interfaces", help="Provide a list of interfaces to capture from", action="store_true")
453 parser.add_argument("--extcap-interface", help="Provide the interface to capture from")
454 parser.add_argument("--extcap-dlts", help="Provide a list of dlts for the given interface", action="store_true")
455 parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true")
456 parser.add_argument("--extcap-capture-filter", help="Used together with capture to provide a capture filter")
457 parser.add_argument("--fifo", help="Use together with capture to provide the fifo to dump data to")
458 parser.add_argument("--extcap-control-in", help="Used to get control messages from toolbar")
459 parser.add_argument("--extcap-control-out", help="Used to send control messages to toolbar")
460 parser.add_argument("--extcap-version", help="Shows the version of this utility", nargs='?', default="")
461 parser.add_argument("--extcap-reload-option", help="Reload elements for the given option")
463 # Interface Arguments
464 parser.add_argument("--verify", help="Demonstrates a verification bool flag", action="store_true" )
465 parser.add_argument("--delay", help="Demonstrates an integer variable", type=int, default=0, choices=[0, 1, 2, 3, 4, 5, 6] )
466 parser.add_argument("--remote", help="Demonstrates a selector choice", default="if1", choices=["if1", "if2", "if3", "if4"] )
467 parser.add_argument("--message", help="Demonstrates string variable", nargs='?', default="" )
468 parser.add_argument("--fake_ip", help="Add a fake sender IP address", nargs='?', default="127.0.0.1" )
469 parser.add_argument("--ts", help="Capture start time", action="store_true" )
471 try:
472 args, unknown = parser.parse_known_args()
473 except argparse.ArgumentError as exc:
474 print("%s: %s" % (exc.argument.dest, exc.message), file=sys.stderr)
475 fifo_found = 0
476 fifo = ""
477 for arg in sys.argv:
478 if arg == "--fifo" or arg == "--extcap-fifo":
479 fifo_found = 1
480 elif fifo_found == 1:
481 fifo = arg
482 break
483 extcap_close_fifo(fifo)
484 sys.exit(ERROR_ARG)
486 if len(sys.argv) <= 1:
487 parser.exit("No arguments given!")
489 if args.extcap_version and not args.extcap_interfaces:
490 extcap_version()
491 sys.exit(0)
493 if not args.extcap_interfaces and args.extcap_interface is None:
494 parser.exit("An interface must be provided or the selection must be displayed")
495 if args.extcap_capture_filter and not args.capture:
496 validate_capture_filter(args.extcap_capture_filter)
497 sys.exit(0)
499 if args.extcap_interfaces or args.extcap_interface is None:
500 extcap_interfaces()
501 sys.exit(0)
503 if len(unknown) > 1:
504 print("Extcap Example %d unknown arguments given" % len(unknown))
506 m = re.match(r'example(\d+)', args.extcap_interface)
507 if not m:
508 sys.exit(ERROR_INTERFACE)
509 interface = m.group(1)
511 message = args.message
512 if args.message is None or len(args.message) == 0:
513 message = "Extcap Test"
515 fake_ip = args.fake_ip
516 if args.fake_ip is None or len(args.fake_ip) < 7 or len(args.fake_ip.split('.')) != 4:
517 fake_ip = "127.0.0.1"
519 ts = args.ts
521 if args.extcap_reload_option and len(args.extcap_reload_option) > 0:
522 option = args.extcap_reload_option
524 if args.extcap_config:
525 extcap_config(interface, option)
526 elif args.extcap_dlts:
527 extcap_dlts(interface)
528 elif args.capture:
529 if args.fifo is None:
530 sys.exit(ERROR_FIFO)
531 # The following code demonstrates error management with extcap
532 if args.delay > 5:
533 print("Value for delay [%d] too high" % args.delay, file=sys.stderr)
534 extcap_close_fifo(args.fifo)
535 sys.exit(ERROR_DELAY)
537 try:
538 extcap_capture(interface, args.fifo, args.extcap_control_in, args.extcap_control_out, args.delay, args.verify, message, args.remote, fake_ip)
539 except KeyboardInterrupt:
540 pass
541 else:
542 usage()
543 sys.exit(ERROR_USAGE)