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 C:\Windows\py.exe C:\Path\to\extcap_example.py %*
26 Windows is not able to execute Python scripts directly, which also goes for all
27 other script-based formats beside VBScript
32 from __future__
import print_function
39 from threading
import Thread
47 CTRL_CMD_INITIALIZED
= 0
53 CTRL_CMD_STATUSBAR
= 6
54 CTRL_CMD_INFORMATION
= 7
72 button_disabled
= False
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
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...
83 container
= self
._actions
86 for action
in container
:
87 if '/'.join(action
.option_strings
) == name
:
89 elif action
.metavar
== name
:
91 elif action
.dest
== name
:
94 def error(self
, message
):
95 exc
= sys
.exc_info()[1]
97 exc
.argument
= self
._get
_action
_from
_name
(exc
.argument_name
)
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
108 def extcap_config(interface
, option
):
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"))
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"))
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"))
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
):
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
216 return int(n
) & 0xFFFFFFFF
218 def pcap_fake_header():
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
230 # Calculates and returns the IP checksum based on the given IP Header
231 def ip_checksum(iph
):
233 words
= splitN(''.join(iph
.split()), 4) # TODO splitN() func undefined, this code will fail
236 csum
+= int(word
, base
=16)
238 csum
= csum
& 0xFFFF ^
0xFFFF
243 def pcap_fake_package(message
, fake_ip
):
244 global iterateCounter
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
259 if (iterateCounter
% 2 == 0):
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)
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
293 def control_read(fn
):
296 sp
, _
, length
, arg
, typ
= struct
.unpack('>sBHBB', header
)
298 payload
= fn
.read(length
- 2).decode('utf-8', 'replace')
301 return arg
, typ
, payload
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
:
309 while arg
is not None:
310 arg
, typ
, payload
= control_read(fn
)
312 if typ
== CTRL_CMD_INITIALIZED
:
314 elif arg
== CTRL_ARG_MESSAGE
:
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
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
330 control_write(fn_out
, CTRL_ARG_BUTTON
, CTRL_CMD_SET
, "Turn on")
332 log
= "Button turned off"
334 control_write(fn_out
, CTRL_ARG_BUTTON
, CTRL_CMD_SET
, "Turn off")
336 log
= "Button turned on"
339 control_write(fn_out
, CTRL_ARG_LOGGER
, CTRL_CMD_ADD
, log
+ "\n")
341 def control_write(fn
, arg
, typ
, payload
):
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')
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
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
))
394 if fn_out
is not None:
395 control_write_defaults(fn_out
)
398 dataTotal
= int(len(data
) / 20) + 1
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
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
)):
413 dataSub
= data
[dataPackage
* 20:(dataPackage
+ 1) * 20]
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
))
421 if fn_out
is not None:
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)
433 print("Usage: %s <--extcap-interfaces | --extcap-dlts | --extcap-interface | --extcap-config | --capture | --extcap-capture-filter | --fifo>" % sys
.argv
[0] )
435 if __name__
== '__main__':
445 parser
= ArgumentParser(
446 prog
="Extcap Example",
447 description
="Extcap example program for Python"
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" )
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
)
478 if arg
== "--fifo" or arg
== "--extcap-fifo":
480 elif fifo_found
== 1:
483 extcap_close_fifo(fifo
)
486 if len(sys
.argv
) <= 1:
487 parser
.exit("No arguments given!")
489 if args
.extcap_version
and not args
.extcap_interfaces
:
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
)
499 if args
.extcap_interfaces
or args
.extcap_interface
is None:
504 print("Extcap Example %d unknown arguments given" % len(unknown
))
506 m
= re
.match(r
'example(\d+)', args
.extcap_interface
)
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"
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
)
529 if args
.fifo
is None:
531 # The following code demonstrates error management with extcap
533 print("Value for delay [%d] too high" % args
.delay
, file=sys
.stderr
)
534 extcap_close_fifo(args
.fifo
)
535 sys
.exit(ERROR_DELAY
)
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:
543 sys
.exit(ERROR_USAGE
)