From 598bdae8c8f0a8c1786bb84718c55d38191d2bdb Mon Sep 17 00:00:00 2001 From: Pierre LALET <pierre.lalet@cea.fr> Date: Fri, 23 Dec 2016 17:13:22 +0100 Subject: [PATCH] Introduce tcpdump() function One can now use tcpdump or tshark to dissect packets from Scapy. This can help, for example, to check Scapy's dissectors --- .travis/test.sh | 3 +- appveyor.yml | 4 +- scapy/arch/windows/__init__.py | 1 + scapy/config.py | 1 + scapy/utils.py | 70 ++++++++++++++++++++++++++++++++++ test/regression.uts | 16 ++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) diff --git a/.travis/test.sh b/.travis/test.sh index b82d16c8..f9cf530a 100644 --- a/.travis/test.sh +++ b/.travis/test.sh @@ -39,8 +39,9 @@ then fi fi -# Do we have tcpdump? +# Do we have tcpdump or thsark? which tcpdump >/dev/null 2>&1 || UT_FLAGS+=" -K tcpdump" +which tshark >/dev/null 2>&1 || UT_FLAGS+=" -K tshark" # Dump Environment (so that we can check PATH, UT_FLAGS, etc.) set diff --git a/appveyor.yml b/appveyor.yml index 9b8537ef..f290b714 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ install: # Installing WinPcap directly does not work, # see http://help.appveyor.com/discussions/problems/2280-winpcap-installation-issue # - choco install -y nmap - - choco install -y winpcap + - choco install -y winpcap wireshark - ps: wget http://www.winpcap.org/windump/install/bin/windump_3_9_5/WinDump.exe -UseBasicParsing -OutFile C:\Windows\System32\windump.exe - refreshenv @@ -24,7 +24,7 @@ install: test_script: # Set environment variables - set PYTHONPATH=%APPVEYOR_BUILD_FOLDER% - - set PATH=%APPVEYOR_BUILD_FOLDER%;%PATH% + - set PATH="%APPVEYOR_BUILD_FOLDER%;C:\Program Files\Wireshark\;%PATH%" # Main unit tests - "%PYTHON%\\python bin\\UTscapy -f text -t test\\regression.uts -F -K automaton -K mock_read_routes6_bsd || exit /b 42" diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py index 5ff08a02..ddb5487f 100755 --- a/scapy/arch/windows/__init__.py +++ b/scapy/arch/windows/__init__.py @@ -187,6 +187,7 @@ class WinProgPath(ConfClass): psreader = win_find_exe("gsview32.exe", "Ghostgum/gsview") dot = win_find_exe("dot", "ATT/Graphviz/bin") tcpdump = win_find_exe("windump") + tshark = win_find_exe("tshark") tcpreplay = win_find_exe("tcpreplay") display = _default hexedit = win_find_exe("hexer") diff --git a/scapy/config.py b/scapy/config.py index 568dada8..b97ee0bd 100755 --- a/scapy/config.py +++ b/scapy/config.py @@ -64,6 +64,7 @@ class ProgPath(ConfClass): tcpdump = "tcpdump" tcpreplay = "tcpreplay" hexedit = "hexer" + tshark = "tshark" wireshark = "wireshark" ifconfig = "ifconfig" diff --git a/scapy/utils.py b/scapy/utils.py index af8eda4b..3f6bb4b7 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1088,6 +1088,76 @@ def wireshark(pktlist): wrpcap(f, pktlist) subprocess.Popen([conf.prog.wireshark, "-r", f]) +@conf.commands.register +def tcpdump(pktlist, dump=False, getfd=False, args=None, + prog=None): + """Run tcpdump or tshark on a list of packets + +pktlist: a Packet instance, a PacketList instance or a list of Packet + instances. Can also be a filename (as a string) or an open + file-like object that must be a file format readable by + tshark (Pcap, PcapNg, etc.) + +dump: when set to True, returns a string instead of displaying it. +getfd: when set to True, returns a file-like object to read data + from tcpdump or tshark from. +args: arguments (as a list) to pass to tshark (example for tshark: + args=["-T", "json"]). Defaults to ["-n"]. +prog: program to use (defaults to tcpdump, will work with tshark) + +Examples: + +>>> tcpdump([IP()/TCP(), IP()/UDP()]) +reading from file -, link-type RAW (Raw IP) +16:46:00.474515 IP 127.0.0.1.20 > 127.0.0.1.80: Flags [S], seq 0, win 8192, length 0 +16:46:00.475019 IP 127.0.0.1.53 > 127.0.0.1.53: [|domain] + +>>> tcpdump([IP()/TCP(), IP()/UDP()], prog=conf.prog.tshark) + 1 0.000000 127.0.0.1 -> 127.0.0.1 TCP 40 20->80 [SYN] Seq=0 Win=8192 Len=0 + 2 0.000459 127.0.0.1 -> 127.0.0.1 UDP 28 53->53 Len=0 + +To get a JSON representation of a tshark-parsed PacketList(), one can: +>>> import json, pprint +>>> json_data = json.load(tcpdump(IP(src="217.25.178.5", dst="45.33.32.156"), +... prog=conf.prog.tshark, args=["-T", "json"], +... getfd=True)) +>>> pprint.pprint(json_data) +[{u'_index': u'packets-2016-12-23', + u'_score': None, + u'_source': {u'layers': {u'frame': {u'frame.cap_len': u'20', + u'frame.encap_type': u'7', +[...] + u'frame.time_relative': u'0.000000000'}, + u'ip': {u'ip.addr': u'45.33.32.156', + u'ip.checksum': u'0x0000a20d', +[...] + u'ip.ttl': u'64', + u'ip.version': u'4'}, + u'raw': u'Raw packet data'}}, + u'_type': u'pcap_file'}] +>>> json_data[0]['_source']['layers']['ip']['ip.ttl'] +u'64' + + """ + proc = subprocess.Popen( + [conf.prog.tcpdump if prog is None else prog, "-r", + pktlist if isinstance(pktlist, basestring) else "-"] + + (["-n"] if args is None else args), + stdin=subprocess.PIPE, + stdout=subprocess.PIPE if dump or getfd else None, + ) + try: + proc.stdin.writelines(iter(lambda: pktlist.read(1048576), "")) + except AttributeError: + wrpcap(proc.stdin, pktlist) + else: + proc.stdin.close() + if dump: + return "".join(iter(lambda: proc.stdout.read(1048576), "")) + if getfd: + return proc.stdout + proc.wait() + @conf.commands.register def hexedit(x): x = str(x) diff --git a/test/regression.uts b/test/regression.uts index 7c379a50..b6826195 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -4706,6 +4706,22 @@ assert isinstance(pkt, Padding) and pkt.load == '\xeay$\xf6' pkt = pkt.payload assert isinstance(pkt, NoPayload) += Check tcpdump() +~ tcpdump +* No very specific tests because we do not want to depend on tcpdump output +pcapfile = cStringIO.StringIO('\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00') +data = tcpdump(pcapfile, dump=True, args=['-n']).split('\n') +print data +assert 'IP 127.0.0.1.20 > 127.0.0.1.80:' in data[0] +assert 'IP 127.0.0.1.53 > 127.0.0.1.53:' in data[1] +assert 'IP 127.0.0.1 > 127.0.0.1:' in data[2] + += Check tcpdump() command with tshark +~ tshark +pcapfile = cStringIO.StringIO('\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00') +values = [tuple(int(val) for val in line[:-1].split('\t')) for line in tcpdump(pcapfile, prog=conf.prog.tshark, getfd=True, args=['-T', 'fields', '-e', 'ip.ttl', '-e', 'ip.proto'])] +assert values == [(64, 6), (64, 17), (64, 1)] + ############ ############ -- GitLab