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
42 from threading import Thread
50 CTRL_CMD_INITIALIZED = 0
56 CTRL_CMD_STATUSBAR = 6
57 CTRL_CMD_INFORMATION = 7
75 button_disabled = False
78 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
80 class ArgumentParser(argparse.ArgumentParser):
81 def _get_action_from_name(self, name):
82 """Given a name, get the Action instance registered with this parser.
83 If only it were made available in the ArgumentError object. It is
84 passed as it's first arg...
86 container = self._actions
89 for action in container:
90 if '/'.join(action.option_strings) == name:
92 elif action.metavar == name:
94 elif action.dest == name:
97 def error(self, message):
98 exc = sys.exc_info()[1]
100 exc.argument = self._get_action_from_name(exc.argument_name)
102 super(ArgumentParser, self).error(message)
104 #### EXTCAP FUNCTIONALITY
106 """@brief Extcap configuration
107 This method prints the extcap configuration, which will be picked up by the
108 interface in Wireshark to present a interface specific configuration for
111 def extcap_config(interface, option):
115 args.append ( (0, '--delay', 'Time delay', 'Time delay between packages', 'integer', '{range=1,15}{default=5}') )
116 args.append ( (1, '--message', 'Message', 'Package message content', 'string', '{required=true}{placeholder=Please enter a message here ...}') )
117 args.append ( (2, '--verify', 'Verify', 'Verify package content', 'boolflag', '{default=yes}') )
118 args.append ( (3, '--remote', 'Remote Channel', 'Remote Channel Selector', 'selector', '{reload=true}{placeholder=Load interfaces ...}'))
119 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}'))
120 args.append ( (5, '--ltest', 'Long Test', 'Long Test Value', 'long', '{default=123123123123123123}{group=Numeric Values}'))
121 args.append ( (6, '--d1test', 'Double 1 Test', 'Long Test Value', 'double', '{default=123.456}{group=Numeric Values}'))
122 args.append ( (7, '--d2test', 'Double 2 Test', 'Long Test Value', 'double', '{default= 123,456}{group=Numeric Values}'))
123 args.append ( (8, '--password', 'Password', 'Package message password', 'password', '') )
124 args.append ( (9, '--ts', 'Start Time', 'Capture start time', 'timestamp', '{group=Time / Log}') )
125 args.append ( (10, '--logfile', 'Log File Test', 'The Log File Test', 'fileselect', '{group=Time / Log}') )
126 args.append ( (11, '--radio', 'Radio Test', 'Radio Test Value', 'radio', '{group=Selection}') )
127 args.append ( (12, '--multi', 'MultiCheck Test', 'MultiCheck Test Value', 'multicheck', '{group=Selection}') )
129 if ( option == "remote" ):
130 values.append ( (3, "if1", "Remote Interface 1", "false" ) )
131 values.append ( (3, "if2", "Remote Interface 2", "true" ) )
132 values.append ( (3, "if3", "Remote Interface 3", "false" ) )
133 values.append ( (3, "if4", "Remote Interface 4", "false" ) )
135 if ( option == "radio" ):
136 values.append ( (11, "r1", "Radio Option 1", "false" ) )
137 values.append ( (11, "r2", "Radio Option 2", "false" ) )
138 values.append ( (11, "r3", "Radio Option 3", "true" ) )
141 if ( len(option) <= 0 ):
143 print ("arg {number=%d}{call=%s}{display=%s}{tooltip=%s}{type=%s}%s" % arg)
145 values.append ( (3, "if1", "Remote1", "true" ) )
146 values.append ( (3, "if2", "Remote2", "false" ) )
148 values.append ( (11, "r1", "Radio1", "false" ) )
149 values.append ( (11, "r2", "Radio2", "true" ) )
151 values.append ( (12, "m1", "MultiCheck1", "false" ) )
152 values.append ( (12, "m2", "MultiCheck2", "false" ) )
155 print ("value {arg=%d}{value=%s}{display=%s}{default=%s}" % value)
158 def extcap_version():
159 print ("extcap {version=1.0}{help=http://www.wireshark.org}{display=Example extcap interface}")
161 def extcap_interfaces():
162 print ("extcap {version=1.0}{help=http://www.wireshark.org}{display=Example extcap interface}")
163 print ("interface {value=example1}{display=Example interface 1 for extcap}")
164 print ("interface {value=example2}{display=Example interface 2 for extcap}")
165 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)
166 print ("control {number=%d}{type=selector}{display=Time delay}{tooltip=Time delay between packages}" % CTRL_ARG_DELAY)
167 print ("control {number=%d}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}" % CTRL_ARG_VERIFY)
168 print ("control {number=%d}{type=button}{display=Turn on}{tooltip=Turn on or off}" % CTRL_ARG_BUTTON)
169 print ("control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Show help}" % CTRL_ARG_HELP)
170 print ("control {number=%d}{type=button}{role=restore}{display=Restore}{tooltip=Restore default values}" % CTRL_ARG_RESTORE)
171 print ("control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}" % CTRL_ARG_LOGGER)
172 print ("value {control=%d}{value=1}{display=1}" % CTRL_ARG_DELAY)
173 print ("value {control=%d}{value=2}{display=2}" % CTRL_ARG_DELAY)
174 print ("value {control=%d}{value=3}{display=3}" % CTRL_ARG_DELAY)
175 print ("value {control=%d}{value=4}{display=4}" % CTRL_ARG_DELAY)
176 print ("value {control=%d}{value=5}{display=5}{default=true}" % CTRL_ARG_DELAY)
177 print ("value {control=%d}{value=60}{display=60}" % CTRL_ARG_DELAY)
180 def extcap_dlts(interface):
181 if ( interface == '1' ):
182 print ("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}")
183 elif ( interface == '2' ):
184 print ("dlt {number=148}{name=USER1}{display=Demo Implementation for Extcap}")
186 def validate_capture_filter(capture_filter):
187 if capture_filter != "filter" and capture_filter != "valid":
188 print("Illegal capture filter")
192 ### FAKE DATA GENERATOR
194 Extcap capture routine
195 This routine simulates a capture by any kind of user defined device. The parameters
196 are user specified and must be handled by the extcap.
198 The data captured inside this routine is fake, so change this routine to present
199 your own input data, or call your own capture program via Popen for example. See
205 return int(n) & 0xFFFFFFFF
207 def pcap_fake_header():
210 header += struct.pack('<L', int ('a1b2c3d4', 16 ))
211 header += struct.pack('<H', unsigned(2) ) # Pcap Major Version
212 header += struct.pack('<H', unsigned(4) ) # Pcap Minor Version
213 header += struct.pack('<I', int(0)) # Timezone
214 header += struct.pack('<I', int(0)) # Accurancy of timestamps
215 header += struct.pack('<L', int ('0000ffff', 16 )) # Max Length of capture frame
216 header += struct.pack('<L', unsigned(1)) # Ethernet
219 # Calculates and returns the IP checksum based on the given IP Header
220 def ip_checksum(iph):
222 words = splitN(''.join(iph.split()),4)
225 csum += int(word, base=16)
227 csum = csum & 0xFFFF ^ 0xFFFF
230 def pcap_fake_package ( message, fake_ip ):
233 #length = 14 bytes [ eth ] + 20 bytes [ ip ] + messagelength
235 caplength = len(message) + 14 + 20
236 timestamp = int(time.time())
238 pcap += struct.pack('<L', unsigned(timestamp ) ) # timestamp seconds
239 pcap += struct.pack('<L', 0x00 ) # timestamp nanoseconds
240 pcap += struct.pack('<L', unsigned(caplength ) ) # length captured
241 pcap += struct.pack('<L', unsigned(caplength ) ) # length in frame
244 pcap += struct.pack('h', 0 ) # source mac
245 pcap += struct.pack('h', 0 ) # source mac
246 pcap += struct.pack('h', 0 ) # source mac
247 pcap += struct.pack('h', 0 ) # dest mac
248 pcap += struct.pack('h', 0 ) # dest mac
249 pcap += struct.pack('h', 0 ) # dest mac
250 pcap += struct.pack('<h', unsigned(8 )) # protocol (ip)
253 pcap += struct.pack('b', int ( '45', 16 )) # IP version
254 pcap += struct.pack('b', int ( '0', 16 )) #
255 pcap += struct.pack('>H', unsigned(len(message)+20) ) # length of data + payload
256 pcap += struct.pack('<H', int ( '0', 16 )) # Identification
257 pcap += struct.pack('b', int ( '40', 16 )) # Don't fragment
258 pcap += struct.pack('b', int ( '0', 16 )) # Fragment Offset
259 pcap += struct.pack('b', int ( '40', 16 ))
260 pcap += struct.pack('B', 0xFE ) # Protocol (2 = unspecified)
261 pcap += struct.pack('<H', int ( '0000', 16 )) # Checksum
263 parts = fake_ip.split('.')
264 ipadr = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
265 pcap += struct.pack('>L', ipadr ) # Source IP
266 pcap += struct.pack('>L', int ( '7F000001', 16 )) # Dest IP
271 def control_read(fn):
274 sp, _, length, arg, typ = struct.unpack('>sBHBB', header)
276 payload = fn.read(length - 2)
279 return arg, typ, payload
281 return None, None, None
283 def control_read_thread(control_in, fn_out):
284 global initialized, message, delay, verify, button, button_disabled
285 with open(control_in, 'rb', 0 ) as fn:
288 arg, typ, payload = control_read(fn)
290 if typ == CTRL_CMD_INITIALIZED:
292 elif arg == CTRL_ARG_MESSAGE:
294 log = "Message = " + payload
295 elif arg == CTRL_ARG_DELAY:
296 delay = float(payload)
297 log = "Time delay = " + payload
298 elif arg == CTRL_ARG_VERIFY:
299 # Only read this after initialized
301 verify = (payload[0] != '\0')
302 log = "Verify = " + str(verify)
303 control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_STATUSBAR, "Verify changed")
304 elif arg == CTRL_ARG_BUTTON:
305 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_DISABLE, "")
306 button_disabled = True
308 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn on")
310 log = "Button turned off"
312 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn off")
314 log = "Button turned on"
317 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log + "\n")
319 def control_write(fn, arg, typ, payload):
321 packet += struct.pack('>sBHBB', b'T', 0, len(payload) + 2, arg, typ)
322 if sys.version_info[0] >= 3 and isinstance(payload, str):
323 packet += payload.encode('utf-8')
328 def control_write_defaults(fn_out):
329 global initialized, message, delay, verify
331 while not initialized:
332 time.sleep(.1) # Wait for initial control values
334 # Write startup configuration to Toolbar controls
335 control_write(fn_out, CTRL_ARG_MESSAGE, CTRL_CMD_SET, message)
336 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_SET, str(int(delay)))
337 control_write(fn_out, CTRL_ARG_VERIFY, CTRL_CMD_SET, struct.pack('B', verify))
339 for i in range(1,16):
341 item += str(i) + struct.pack('B', 0) + str(i) + " sec"
342 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_ADD, item)
344 control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_REMOVE, str(60))
346 def extcap_capture(interface, fifo, control_in, control_out, in_delay, in_verify, in_message, remote, fake_ip):
347 global message, delay, verify, button_disabled
348 delay = in_delay if in_delay != 0 else 5
354 with open(fifo, 'wb', 0 ) as fh:
355 fh.write (pcap_fake_header())
357 if control_out != None:
358 fn_out = open(control_out, 'wb', 0)
359 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_SET, "Log started at " + time.strftime("%c") + "\n")
361 if control_in != None:
362 # Start reading thread
363 thread = Thread(target = control_read_thread, args = (control_in, fn_out))
367 control_write_defaults(fn_out)
371 log = "Received packet #" + str(counter) + "\n"
372 control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log)
373 counter = counter + 1
375 if button_disabled == True:
376 control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_ENABLE, "")
377 control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_INFORMATION, "Turn action finished.")
378 button_disabled = False
380 out = ("%s|%04X%s|%s" % ( remote.strip(), len(message), message, verify )).encode("utf8")
381 fh.write (pcap_fake_package(out, fake_ip))
388 def extcap_close_fifo(fifo):
389 # This is apparently needed to workaround an issue on Windows/macOS
390 # where the message cannot be read. (really?)
391 fh = open(fifo, 'wb', 0 )
397 print ( "Usage: %s <--extcap-interfaces | --extcap-dlts | --extcap-interface | --extcap-config | --capture | --extcap-capture-filter | --fifo>" % sys.argv[0] )
399 if __name__ == '__main__':
409 parser = ArgumentParser(
410 prog="Extcap Example",
411 description="Extcap example program for python"
415 parser.add_argument("--capture", help="Start the capture routine", action="store_true" )
416 parser.add_argument("--extcap-interfaces", help="Provide a list of interfaces to capture from", action="store_true")
417 parser.add_argument("--extcap-interface", help="Provide the interface to capture from")
418 parser.add_argument("--extcap-dlts", help="Provide a list of dlts for the given interface", action="store_true")
419 parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true")
420 parser.add_argument("--extcap-capture-filter", help="Used together with capture to provide a capture filter")
421 parser.add_argument("--fifo", help="Use together with capture to provide the fifo to dump data to")
422 parser.add_argument("--extcap-control-in", help="Used to get control messages from toolbar")
423 parser.add_argument("--extcap-control-out", help="Used to send control messages to toolbar")
424 parser.add_argument("--extcap-version", help="Shows the version of this utility", nargs='?', default="")
425 parser.add_argument("--extcap-reload-option", help="Reload elements for the given option")
427 # Interface Arguments
428 parser.add_argument("--verify", help="Demonstrates a verification bool flag", action="store_true" )
429 parser.add_argument("--delay", help="Demonstrates an integer variable", type=int, default=0, choices=[0, 1, 2, 3, 4, 5, 6] )
430 parser.add_argument("--remote", help="Demonstrates a selector choice", default="if1", choices=["if1", "if2", "if3", "if4"] )
431 parser.add_argument("--message", help="Demonstrates string variable", nargs='?', default="" )
432 parser.add_argument("--fake_ip", help="Add a fake sender IP adress", nargs='?', default="127.0.0.1" )
433 parser.add_argument("--ts", help="Capture start time", action="store_true" )
436 args, unknown = parser.parse_known_args()
437 except argparse.ArgumentError as exc:
438 print( "%s: %s" % ( exc.argument.dest, exc.message ), file=sys.stderr)
442 if (arg == "--fifo" or arg == "--extcap-fifo") :
444 elif ( fifo_found == 1 ):
447 extcap_close_fifo(fifo)
450 if ( len(sys.argv) <= 1 ):
451 parser.exit("No arguments given!")
453 if ( args.extcap_version and not args.extcap_interfaces ):
457 if ( args.extcap_interfaces == False and args.extcap_interface == None ):
458 parser.exit("An interface must be provided or the selection must be displayed")
459 if ( args.extcap_capture_filter and not args.capture ):
460 validate_capture_filter(args.extcap_capture_filter)
463 if ( args.extcap_interfaces == True or args.extcap_interface == None ):
467 if ( len(unknown) > 1 ):
468 print("Extcap Example %d unknown arguments given" % len(unknown) )
470 m = re.match ( 'example(\d+)', args.extcap_interface )
472 sys.exit(ERROR_INTERFACE)
473 interface = m.group(1)
475 message = args.message
476 if ( args.message == None or len(args.message) == 0 ):
477 message = "Extcap Test"
479 fake_ip = args.fake_ip
480 if ( args.fake_ip == None or len(args.fake_ip) < 7 or len(args.fake_ip.split('.')) != 4 ):
481 fake_ip = "127.0.0.1"
485 if ( args.extcap_reload_option and len(args.extcap_reload_option) > 0 ):
486 option = args.extcap_reload_option
488 if args.extcap_config:
489 extcap_config(interface, option)
490 elif args.extcap_dlts:
491 extcap_dlts(interface)
493 if args.fifo is None:
495 # The following code demonstrates error management with extcap
497 print("Value for delay [%d] too high" % args.delay, file=sys.stderr)
498 extcap_close_fifo(args.fifo)
499 sys.exit(ERROR_DELAY)
502 extcap_capture(interface, args.fifo, args.extcap_control_in, args.extcap_control_out, args.delay, args.verify, message, args.remote, fake_ip)
503 except KeyboardInterrupt:
507 sys.exit(ERROR_USAGE)