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
13 This is a generic example, which produces pcap packages every n seconds, and
14 is configurable via extcap options.
18 To use this script on Windows, please generate an extcap_example.bat inside
19 the extcap folder, with the following content:
23 <Path to python interpreter> <Path to script file> %*
26 Windows is not able to execute Python scripts directly, which also goes for all
27 other script-based formates beside VBScript
32 from __future__ import print_function
41 from threading import Thread
49 CTRL_CMD_INITIALIZED = 0
55 CTRL_CMD_STATUSBAR = 6
56 CTRL_CMD_INFORMATION = 7
74 button_disabled = False
77 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
79 class ArgumentParser(argparse.ArgumentParser):
80 def _get_action_from_name(self, name):
81 """Given a name, get the Action instance registered with this parser.
82 If only it were made available in the ArgumentError object. It is
83 passed as it's first arg...
85 container = self._actions
88 for action in container:
89 if '/'.join(action.option_strings) == name:
91 elif action.metavar == name:
93 elif action.dest == name:
96 def error(self, message):
97 exc = sys.exc_info()[1]
99 exc.argument = self._get_action_from_name(exc.argument_name)
101 super(ArgumentParser, self).error(message)
103 #### EXTCAP FUNCTIONALITY
105 """@brief Extcap configuration
106 This method prints the extcap configuration, which will be picked up by the
107 interface in Wireshark to present a interface specific configuration for
110 def extcap_config(interface):
114 args.append ( (0, '--delay', 'Time delay', 'Time delay between packages', 'integer', '{range=1,15}{default=5}') )
115 args.append ( (1, '--message', 'Message', 'Package message content', 'string', '{required=true}{placeholder=Please enter a message here ...}') )
116 args.append ( (2, '--verify', 'Verify', 'Verify package content', 'boolflag', '{default=yes}') )
117 args.append ( (3, '--remote', 'Remote Channel', 'Remote Channel Selector', 'selector', ''))
118 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}'))
119 args.append ( (5, '--ltest', 'Long Test', 'Long Test Value', 'long', '{default=123123123123123123}'))
120 args.append ( (6, '--d1test', 'Double 1 Test', 'Long Test Value', 'double', '{default=123.456}'))
121 args.append ( (7, '--d2test', 'Double 2 Test', 'Long Test Value', 'double', '{default= 123,456}'))
122 args.append ( (8, '--password', 'Password', 'Package message password', 'password', '') )
123 args.append ( (9, '--ts', 'Start Time', 'Capture start time', 'timestamp', '') )
124 args.append ( (10, '--logfile', 'Log File Test', 'The Log File Test', 'fileselect', '') )
125 args.append ( (11, '--radio', 'Radio Test', 'Radio Test Value', 'radio', '') )
126 args.append ( (12, '--multi', 'MultiCheck Test', 'MultiCheck Test Value', 'multicheck', '') )
128 values.append ( (3, "if1", "Remote1", "true" ) )
129 values.append ( (3, "if2", "Remote2", "false" ) )
131 values.append ( (11, "r1", "Radio1", "false" ) )
132 values.append ( (11, "r2", "Radio2", "true" ) )
134 values.append ( (12, "m1", "MultiCheck1", "false" ) )
135 values.append ( (12, "m2", "MultiCheck2", "false" ) )
138 print ("arg {number=%d}{call=%s}{display=%s}{tooltip=%s}{type=%s}%s" % arg)
141 print ("value {arg=%d}{value=%s}{display=%s}{default=%s}" % value)
144 def extcap_interfaces():
145 print ("extcap {version=1.0}{help=http://www.wireshark.org}{display=Example extcap interface}")
146 print ("interface {value=example1}{display=Example interface 1 for extcap}")
147 print ("interface {value=example2}{display=Example interface 2 for extcap}")
148 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)
149 print ("control {number=%d}{type=selector}{display=Time delay}{tooltip=Time delay between packages}" % CTRL_ARG_DELAY)
150 print ("control {number=%d}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}" % CTRL_ARG_VERIFY)
151 print ("control {number=%d}{type=button}{display=Turn on}{tooltip=Turn on or off}" % CTRL_ARG_BUTTON)
152 print ("control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Show help}" % CTRL_ARG_HELP)
153 print ("control {number=%d}{type=button}{role=restore}{display=Restore}{tooltip=Restore default values}" % CTRL_ARG_RESTORE)
154 print ("control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}" % CTRL_ARG_LOGGER)
155 print ("value {control=%d}{value=1}{display=1}" % CTRL_ARG_DELAY)
156 print ("value {control=%d}{value=2}{display=2}" % CTRL_ARG_DELAY)
157 print ("value {control=%d}{value=3}{display=3}" % CTRL_ARG_DELAY)
158 print ("value {control=%d}{value=4}{display=4}" % CTRL_ARG_DELAY)
159 print ("value {control=%d}{value=5}{display=5}{default=true}" % CTRL_ARG_DELAY)
160 print ("value {control=%d}{value=60}{display=60}" % CTRL_ARG_DELAY)
163 def extcap_dlts(interface):
164 if ( interface == '1' ):
165 print ("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}")
166 elif ( interface == '2' ):
167 print ("dlt {number=148}{name=USER1}{display=Demo Implementation for Extcap}")
169 def validate_capture_filter(capture_filter):
170 if capture_filter != "filter" and capture_filter != "valid":
171 print("Illegal capture filter")
175 ### FAKE DATA GENERATOR
177 Extcap capture routine
178 This routine simulates a capture by any kind of user defined device. The parameters
179 are user specified and must be handled by the extcap.
181 The data captured inside this routine is fake, so change this routine to present
182 your own input data, or call your own capture program via Popen for example. See
188 return int(n) & 0xFFFFFFFF
190 def pcap_fake_header():
193 header += struct.pack('<L', int ('a1b2c3d4', 16 ))
194 header += struct.pack('<H', unsigned(2) ) # Pcap Major Version
195 header += struct.pack('<H', unsigned(4) ) # Pcap Minor Version
196 header += struct.pack('<I', int(0)) # Timezone
197 header += struct.pack('<I', int(0)) # Accurancy of timestamps
198 header += struct.pack('<L', int ('0000ffff', 16 )) # Max Length of capture frame
199 header += struct.pack('<L', unsigned(1)) # Ethernet
202 # Calculates and returns the IP checksum based on the given IP Header
203 def ip_checksum(iph):
205 words = splitN(''.join(iph.split()),4)
208 csum += int(word, base=16)
210 csum = csum & 0xFFFF ^ 0xFFFF
213 def pcap_fake_package ( message, fake_ip ):
216 #length = 14 bytes [ eth ] + 20 bytes [ ip ] + messagelength
218 caplength = len(message) + 14 + 20
219 timestamp = int(time.time())
221 pcap += struct.pack('<L', unsigned(timestamp ) ) # timestamp seconds
222 pcap += struct.pack('<L', 0x00 ) # timestamp nanoseconds
223 pcap += struct.pack('<L', unsigned(caplength ) ) # length captured
224 pcap += struct.pack('<L', unsigned(caplength ) ) # length in frame
227 pcap += struct.pack('h', 0 ) # source mac
228 pcap += struct.pack('h', 0 ) # source mac
229 pcap += struct.pack('h', 0 ) # source mac
230 pcap += struct.pack('h', 0 ) # dest mac
231 pcap += struct.pack('h', 0 ) # dest mac
232 pcap += struct.pack('h', 0 ) # dest mac
233 pcap += struct.pack('<h', unsigned(8 )) # protocol (ip)
236 pcap += struct.pack('b', int ( '45', 16 )) # IP version
237 pcap += struct.pack('b', int ( '0', 16 )) #
238 pcap += struct.pack('>H', unsigned(len(message)+20) ) # length of data + payload
239 pcap += struct.pack('<H', int ( '0', 16 )) # Identification
240 pcap += struct.pack('b', int ( '40', 16 )) # Don't fragment
241 pcap += struct.pack('b', int ( '0', 16 )) # Fragment Offset
242 pcap += struct.pack('b', int ( '40', 16 ))
243 pcap += struct.pack('B', 0xFE ) # Protocol (2 = unspecified)
244 pcap += struct.pack('<H', int ( '0000', 16 )) # Checksum
246 parts = fake_ip.split('.')
247 ipadr = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
248 pcap += struct.pack('>L', ipadr ) # Source IP
249 pcap += struct.pack('>L', int ( '7F000001', 16 )) # Dest IP
254 def control_read(fn):
257 sp, _, length, arg, typ = struct.unpack('>sBHBB', header)
259 payload = fn.read(length - 2)
262 return arg, typ, payload
264 return None, None, None
266 def control_read_thread(control_in, fn_out):
267 global initialized, message, delay, verify, button, button_disabled
268 with open(control_in, 'rb', 0 ) as fn:
271 arg, typ, payload = control_read(fn)
273 if typ == CTRL_CMD_INITIALIZED:
275 elif arg == CTRL_ARG_MESSAGE:
277 log = "Message = " + payload
278 elif arg == CTRL_ARG_DELAY:
279 delay = float(payload)
280 log = "Time delay = " + payload
281 elif arg == CTRL_ARG_VERIFY:
282 # Only read this after initialized
284 verify = (payload[0] != '\0')
285 log = "Verify = " + str(verify)
286 control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_STATUSBAR, "Verify changed")
287 elif arg == CTRL_ARG_BUTTON:
288 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_DISABLE, "")
289 button_disabled = True
291 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn on")
293 log = "Button turned off"
295 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn off")
297 log = "Button turned on"
300 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log + "\n")
302 def control_write(fn, arg, typ, payload):
304 packet += struct.pack('>sBHBB', 'T', 0, len(payload) + 2, arg, typ)
308 def control_write_defaults(fn_out):
309 global initialized, message, delay, verify
311 while not initialized:
312 time.sleep(.1) # Wait for initial control values
314 # Write startup configuration to Toolbar controls
315 control_write(fn_out, CTRL_ARG_MESSAGE, CTRL_CMD_SET, message)
316 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_SET, str(int(delay)))
317 control_write(fn_out, CTRL_ARG_VERIFY, CTRL_CMD_SET, struct.pack('B', verify))
319 for i in range(1,16):
321 item += str(i) + struct.pack('B', 0) + str(i) + " sec"
322 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_ADD, item)
324 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_REMOVE, str(60))
326 def extcap_capture(interface, fifo, control_in, control_out, in_delay, in_verify, in_message, remote, fake_ip):
327 global message, delay, verify, button_disabled
328 delay = in_delay if in_delay != 0 else 5
334 with open(fifo, 'wb', 0 ) as fh:
335 fh.write (pcap_fake_header())
337 if control_out != None:
338 fn_out = open(control_out, 'wb', 0)
339 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_SET, "Log started at " + time.strftime("%c") + "\n")
341 if control_in != None:
342 # Start reading thread
343 thread = Thread(target = control_read_thread, args = (control_in, fn_out))
347 control_write_defaults(fn_out)
351 log = "Received packet #" + str(counter) + "\n"
352 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log)
353 counter = counter + 1
355 if button_disabled == True:
356 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_ENABLE, "")
357 control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_INFORMATION, "Turn action finished.")
358 button_disabled = False
360 out = ("%s|%04X%s|%s" % ( remote.strip(), len(message), message, verify )).encode("utf8")
361 fh.write (pcap_fake_package(out, fake_ip))
368 def extcap_close_fifo(fifo):
369 # This is apparently needed to workaround an issue on Windows/macOS
370 # where the message cannot be read. (really?)
371 fh = open(fifo, 'wb', 0 )
377 print ( "Usage: %s <--extcap-interfaces | --extcap-dlts | --extcap-interface | --extcap-config | --capture | --extcap-capture-filter | --fifo>" % sys.argv[0] )
379 if __name__ == '__main__':
388 parser = ArgumentParser(
389 prog="Extcap Example",
390 description="Extcap example program for python"
394 parser.add_argument("--capture", help="Start the capture routine", action="store_true" )
395 parser.add_argument("--extcap-interfaces", help="Provide a list of interfaces to capture from", action="store_true")
396 parser.add_argument("--extcap-interface", help="Provide the interface to capture from")
397 parser.add_argument("--extcap-dlts", help="Provide a list of dlts for the given interface", action="store_true")
398 parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true")
399 parser.add_argument("--extcap-capture-filter", help="Used together with capture to provide a capture filter")
400 parser.add_argument("--fifo", help="Use together with capture to provide the fifo to dump data to")
401 parser.add_argument("--extcap-control-in", help="Used to get control messages from toolbar")
402 parser.add_argument("--extcap-control-out", help="Used to send control messages to toolbar")
404 # Interface Arguments
405 parser.add_argument("--verify", help="Demonstrates a verification bool flag", action="store_true" )
406 parser.add_argument("--delay", help="Demonstrates an integer variable", type=int, default=0, choices=[0, 1, 2, 3, 4, 5, 6] )
407 parser.add_argument("--remote", help="Demonstrates a selector choice", default="if1", choices=["if1", "if2"] )
408 parser.add_argument("--message", help="Demonstrates string variable", nargs='?', default="" )
409 parser.add_argument("--fake_ip", help="Add a fake sender IP adress", nargs='?', default="127.0.0.1" )
410 parser.add_argument("--ts", help="Capture start time", action="store_true" )
413 args, unknown = parser.parse_known_args()
414 except argparse.ArgumentError as exc:
415 print( "%s: %s" % ( exc.argument.dest, exc.message ), file=sys.stderr)
419 if (arg == "--fifo" or arg == "--extcap-fifo") :
421 elif ( fifo_found == 1 ):
424 extcap_close_fifo(fifo)
427 if ( len(sys.argv) <= 1 ):
428 parser.exit("No arguments given!")
430 if ( args.extcap_interfaces == False and args.extcap_interface == None ):
431 parser.exit("An interface must be provided or the selection must be displayed")
432 if ( args.extcap_capture_filter and not args.capture ):
433 validate_capture_filter(args.extcap_capture_filter)
436 if ( args.extcap_interfaces == True or args.extcap_interface == None ):
440 if ( len(unknown) > 1 ):
441 print("Extcap Example %d unknown arguments given" % len(unknown) )
443 m = re.match ( 'example(\d+)', args.extcap_interface )
445 sys.exit(ERROR_INTERFACE)
446 interface = m.group(1)
448 message = args.message
449 if ( args.message == None or len(args.message) == 0 ):
450 message = "Extcap Test"
452 fake_ip = args.fake_ip
453 if ( args.fake_ip == None or len(args.fake_ip) < 7 or len(args.fake_ip.split('.')) != 4 ):
454 fake_ip = "127.0.0.1"
458 if args.extcap_config:
459 extcap_config(interface)
460 elif args.extcap_dlts:
461 extcap_dlts(interface)
463 if args.fifo is None:
465 # The following code demonstrates error management with extcap
467 print("Value for delay [%d] too high" % args.delay, file=sys.stderr)
468 extcap_close_fifo(args.fifo)
469 sys.exit(ERROR_DELAY)
472 extcap_capture(interface, args.fifo, args.extcap_control_in, args.extcap_control_out, args.delay, args.verify, message, args.remote, fake_ip)
473 except KeyboardInterrupt:
477 sys.exit(ERROR_USAGE)