From 1d2ebbf111bc2bdee165f699b9ad4049d8ccd2ae Mon Sep 17 00:00:00 2001 From: Amedeo Date: Sun, 28 Feb 2016 21:24:14 +0100 Subject: [PATCH] Fixed bug in bridge_and_sniff Sniff() optionally accepts an interface or a list of interfaces Added "direction" attribute to pkt when received by L2ListenSocket Patch PEP08'ified Do not force to print interface Updated usage.rst with multi-interfaces sniffing Fixed missing pic in usage.rst tethereal -> tshark --- doc/scapy/usage.rst | 9 +++-- scapy/arch/linux.py | 16 ++++++++- scapy/sendrecv.py | 83 +++++++++++++++++++++++++++------------------ 3 files changed, 72 insertions(+), 36 deletions(-) diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst index c85dd16ed..919fff198 100644 --- a/doc/scapy/usage.rst +++ b/doc/scapy/usage.rst @@ -79,7 +79,7 @@ The ``/`` operator has been used as a composition operator between two layers. W > -.. image:: graphics/fieldsmanagement.* +.. image:: graphics/fieldsmanagement.png :scale: 90 Each packet can be build or dissected (note: in Python ``_`` (underscore) is the latest result):: @@ -520,7 +520,7 @@ Sniffing .. index:: single: sniff() -We can easily capture some packets or even clone tcpdump or tethereal. If no interface is given, sniffing will happen on every interfaces:: +We can easily capture some packets or even clone tcpdump or tshark. Either one interface or a list of interfaces to sniff on can be provided. If no interface is given, sniffing will happen on every interface:: >>> sniff(filter="icmp and host 66.35.250.151", count=2) @@ -607,6 +607,11 @@ We can easily capture some packets or even clone tcpdump or tethereal. If no int load = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567' ---[ Padding ]--- load = '\n_\x00\x0b' + >>> sniff(iface=["eth1","eth2"], prn=lambda x: x.sniffed_on+": "+x.summary()) + eth3: Ether / IP / ICMP 192.168.5.21 > 66.35.250.151 echo-request 0 / Raw + eth3: Ether / IP / ICMP 66.35.250.151 > 192.168.5.21 echo-reply 0 / Raw + eth2: Ether / IP / ICMP 192.168.5.22 > 66.35.250.152 echo-request 0 / Raw + eth2: Ether / IP / ICMP 66.35.250.152 > 192.168.5.22 echo-reply 0 / Raw For even more control over displayed information we can use the ``sprintf()`` function:: diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py index db8b5c727..f5776a1ae 100644 --- a/scapy/arch/linux.py +++ b/scapy/arch/linux.py @@ -65,6 +65,17 @@ SOL_SOCKET = 1 RTF_UP = 0x0001 # Route usable RTF_REJECT = 0x0200 +# From if_packet.h +PACKET_HOST = 0 # To us +PACKET_BROADCAST = 1 # To all +PACKET_MULTICAST = 2 # To group +PACKET_OTHERHOST = 3 # To someone else +PACKET_OUTGOING = 4 # Outgoing of any type +PACKET_LOOPBACK = 5 # MC/BRD frame looped back +PACKET_USER = 6 # To user space +PACKET_KERNEL = 7 # To kernel space +PACKET_FASTROUTE = 6 # Fastrouted frame +# Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space LOOPBACK_NAME="lo" @@ -508,7 +519,9 @@ class L2ListenSocket(SuperSocket): cls = conf.l3types[sa_ll[1]] else: cls = conf.default_l2 - warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) + warning("Unable to guess type (interface=%s protocol=%#x " + "family=%i). Using %s" % (sa_ll[0], sa_ll[1], sa_ll[3], + cls.name)) try: pkt = cls(pkt) @@ -519,6 +532,7 @@ class L2ListenSocket(SuperSocket): raise pkt = conf.raw_layer(pkt) pkt.time = get_last_packet_timestamp(self.ins) + pkt.direction = sa_ll[2] return pkt def send(self, x): diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index ee7aaca9d..2c7218b6f 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -538,10 +538,12 @@ iface: listen answers only on the given interface""" @conf.commands.register -def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, - opened_socket=None, stop_filter=None, *arg, **karg): +def sniff(count=0, store=1, offline=None, prn=None, lfilter=None, + L2socket=None, timeout=None, opened_socket=None, + stop_filter=None, iface=None, *arg, **karg): """Sniff packets -sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets +sniff([count=0,] [prn=None,] [store=1,] [offline=None,] +[lfilter=None,] + L2ListenSocket args) -> list of packets count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them @@ -558,47 +560,58 @@ opened_socket: provide an object ready to use .recv() on stop_filter: python function applied to each packet to determine if we have to stop the capture after this packet ex: stop_filter = lambda x: x.haslayer(TCP) +iface: interface or list of interfaces (default: None for sniffing on all +interfaces) """ c = 0 - + label = {} + ls = [] if opened_socket is not None: - s = opened_socket + ls = [opened_socket] else: if offline is None: if L2socket is None: L2socket = conf.L2listen - s = L2socket(type=ETH_P_ALL, *arg, **karg) + if type(iface) is list: + for i in iface: + s = L2socket(type=ETH_P_ALL, iface=i, *arg, **karg) + label[s] = i + ls.append(s) + else: + ls = [L2socket(type=ETH_P_ALL, iface=iface, *arg, **karg)] else: - s = PcapReader(offline) + ls = [PcapReader(offline)] lst = [] if timeout is not None: stoptime = time.time()+timeout remain = None try: - while 1: + stop_event = 0 + while not stop_event: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break - sel = select([s],[],[],remain) - if s in sel[0]: - p = s.recv(MTU) - if p is None: - continue - if lfilter and not lfilter(p): - continue - if store: - lst.append(p) - c += 1 - if prn: - r = prn(p) - if r is not None: - print r - if stop_filter and stop_filter(p): - break - if count > 0 and c >= count: - break + sel = select(ls, [], [], remain) + for s in sel[0]: + p = s.recv() + if p is not None: + if lfilter and not lfilter(p): + continue + if s in label: + p.sniffed_on = label[s] + if store: + lst.append(p) + c += 1 + if prn: + r = prn(p) + if r is not None: + print r + if stop_filter and stop_filter(p): + stop_event = 1 + if count > 0 and c >= count: + stop_event = 1 except KeyboardInterrupt: pass if opened_socket is None: @@ -607,10 +620,12 @@ stop_filter: python function applied to each packet to determine @conf.commands.register -def bridge_and_sniff(if1, if2, count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, +def bridge_and_sniff(if1, if2, count=0, store=1, offline=None, prn=None, + lfilter=None, L2socket=None, timeout=None, stop_filter=None, *args, **kargs): """Forward traffic between two interfaces and sniff packets exchanged -bridge_and_sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2Socket args) -> list of packets +bridge_and_sniff([count=0,] [prn=None,] [store=1,] [offline=None,] +[lfilter=None,] + L2Socket args) -> list of packets count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them @@ -639,12 +654,13 @@ stop_filter: python function applied to each packet to determine stoptime = time.time()+timeout remain = None try: - while True: + stop_event = 0 + while not stop_event: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break - ins,outs,errs = select([s1,s2],[],[], remain) + ins, outs, errs = select([s1, s2], [], [], remain) for s in ins: p = s.recv() if p is not None: @@ -658,11 +674,11 @@ stop_filter: python function applied to each packet to determine if prn: r = prn(p) if r is not None: - print "%s: %s" % (label[s],r) + print r if stop_filter and stop_filter(p): - break + stop_event = 1 if count > 0 and c >= count: - break + stop_event = 1 except KeyboardInterrupt: pass finally: @@ -674,3 +690,4 @@ def tshark(*args,**kargs): """Sniff packets and print them calling pkt.show(), a bit like text wireshark""" sniff(prn=lambda x: x.display(),*args,**kargs) +