From 4583aabe6e451f573bd725e6024c6a18f6ed7b70 Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Mon, 12 Feb 2018 20:51:04 +0100 Subject: [PATCH 1/4] Add multi-threading sniff --- scapy/contrib/cansocket_native.py | 11 +-- scapy/sendrecv.py | 132 +++++++++++++++++++++++------- scapy/supersocket.py | 4 - 3 files changed, 108 insertions(+), 39 deletions(-) diff --git a/scapy/contrib/cansocket_native.py b/scapy/contrib/cansocket_native.py index 5eecf16319f..2efb66f6f9b 100644 --- a/scapy/contrib/cansocket_native.py +++ b/scapy/contrib/cansocket_native.py @@ -62,7 +62,7 @@ def __init__(self, iface=None, receive_own_messages=False, self.ins.bind((iface,)) self.outs = self.ins - def recv(self, x=CAN_FRAME_SIZE): + def recv_raw(self, x=CAN_FRAME_SIZE): try: pkt, sa_ll = self.ins.recvfrom(x) except BlockingIOError: # noqa: F821 @@ -80,12 +80,9 @@ def recv(self, x=CAN_FRAME_SIZE): # required by the underlaying Linux SocketCAN frame format pkt = struct.pack("I12s", pkt)) len = pkt[4] - canpkt = CAN(pkt[:len + 8]) - canpkt.time = get_last_packet_timestamp(self.ins) - if self.remove_padding: - return canpkt - else: - return canpkt / Padding(pkt[len + 8:]) + canpkt = pkt[:len + 8] if self.remove_padding else pkt + ts = get_last_packet_timestamp(self.ins) + return CAN, canpkt, ts def send(self, x): try: diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index fb3543a4d5b..ac57aa92312 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -24,11 +24,11 @@ from scapy.packet import Packet, Gen from scapy.utils import get_temp_file, PcapReader, tcpdump, wrpcap from scapy import plist -from scapy.error import log_runtime, log_interactive +from scapy.error import log_runtime, log_interactive, Scapy_Exception, warning from scapy.base_classes import SetGen from scapy.supersocket import StreamSocket, L3RawSocket, L2ListenTcpdump from scapy.modules import six -from scapy.modules.six.moves import map +from scapy.modules.six.moves import map, queue if conf.route is None: # unused import, only to initialize conf.route import scapy.route @@ -793,7 +793,8 @@ def srp1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kar @conf.commands.register def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, L2socket=None, timeout=None, opened_socket=None, - stop_filter=None, iface=None, started_callback=None, *arg, **karg): + stop_filter=None, iface=None, started_callback=None, + mthread=None, *arg, **karg): """ Sniff packets and return a list of packets. @@ -818,6 +819,8 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, --Ex: stop_filter = lambda x: x.haslayer(TCP) iface: interface or list of interfaces (default: None for sniffing on all interfaces). + mthread: Use separate threads to recieve and dissect packets. Decrease + packet drop rate. monitor: use monitor mode. May not be available on all OS started_callback: called as soon as the sniffer starts sniffing (default: None). @@ -881,6 +884,12 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, else: sniff_sockets[L2socket(type=ETH_P_ALL, iface=iface, *arg, **karg)] = iface + if mthread is None: + mthread = (all(hasattr(x, "recv_raw") for x in sniff_sockets) and not conf.use_bpf) # noqa: E501 + elif mthread and (any(not hasattr(x, "recv_raw") for x in sniff_sockets)): + warning("Multi-threaded mode is not available on one of the provided " + "objects ! (recv_raw does not exist)") + mthread = False lst = [] if timeout is not None: stoptime = time.time() + timeout @@ -913,9 +922,90 @@ def _select(sockets): if exc[0] == errno.EINTR: return [] raise + + class _InteruptSniff(Scapy_Exception): + """Used to interupt sniffing in multi-threading mode""" + pass + + def _process_pkt(p, sniff_sockets, c): + """Function to process a recieved packet""" + if p is None: + try: + if s.promisc: + return c + except AttributeError: + pass + del sniff_sockets[s] + raise _InteruptSniff + if lfilter and not lfilter(p): + return c + p.sniffed_on = sniff_sockets[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): + sniff_sockets = [] + raise _InteruptSniff + if 0 < count <= c: + sniff_sockets = [] + raise _InteruptSniff + return c + + def _async_process(q, stopevent): + """Thread that dissects the packets when multi-threading is on""" + c = 0 + try: + while not stopevent.is_set(): + try: + pkt_a, sniff_sockets = q.get_nowait() + except queue.Empty: + continue + cls, val, ts = pkt_a + try: + pkt = cls(val) + except Exception: + if conf.debug_dissector: + log_runtime.error(exc_info=True) + stopevent.set() + break + pkt = conf.raw_layer(val) + pkt.time = ts + try: + c = _process_pkt(pkt, sniff_sockets, c) + except _InteruptSniff: + stopevent.set() + except KeyboardInterrupt: + stopevent.set() + del q + se = None + if mthread: + # When multithreading is enabled, the packets are + # recieved on the main thread, and dissected in + # another thread. + q = queue.Queue() + se = threading.Event() + + def _append_pkt(p, sniff_sockets, c=None, q=q): + q.put((p, sniff_sockets)) + if se.is_set(): + raise _InteruptSniff + _action_pkt = _append_pkt + _read_next = lambda s: s.recv_raw() + t = threading.Thread(target=_async_process, args=(q, se)) + t.deamon = True + t.start() + else: + # Multi-threading disabled + _action_pkt = _process_pkt + _read_next = lambda s: s.recv() try: if started_callback: started_callback() + c = 0 while sniff_sockets: if timeout is not None: remain = stoptime - time.time() @@ -924,35 +1014,21 @@ def _select(sockets): ins = _select(sniff_sockets) for s in ins: try: - p = s.recv() + p = _read_next(s) except read_allowed_exceptions: continue - if p is None: - try: - if s.promisc: - continue - except AttributeError: - pass - del sniff_sockets[s] - break - if lfilter and not lfilter(p): - continue - p.sniffed_on = sniff_sockets[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): - sniff_sockets = [] - break - if 0 < count <= c: - sniff_sockets = [] - break + c = _action_pkt(p, sniff_sockets, c) except KeyboardInterrupt: pass + except _InteruptSniff: + pass + finally: + if mthread: + try: + se.set() + except: + pass + t.join() if opened_socket is None: for s in sniff_sockets: s.close() diff --git a/scapy/supersocket.py b/scapy/supersocket.py index 35ac87a6178..16bf79d7283 100644 --- a/scapy/supersocket.py +++ b/scapy/supersocket.py @@ -47,10 +47,6 @@ def send(self, x): x.sent_time = time.time() return self.outs.send(sx) - def recv_raw(self, x=MTU): - """Returns a tuple containing (cls, pkt_data, time)""" - return conf.raw_layer, self.ins.recv(x), None - def recv(self, x=MTU): cls, val, ts = self.recv_raw(x) if not val or not cls: From 626c6c46476961839eb30be2458c8fb1bd8e41b3 Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Mon, 11 Jun 2018 11:57:03 +0200 Subject: [PATCH 2/4] Disable multithreading on some linux specific tests --- scapy/sendrecv.py | 4 ++-- test/linux.uts | 2 ++ test/sendsniff.uts | 15 ++++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index ac57aa92312..2cb392b056c 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -1058,7 +1058,7 @@ def bridge_and_sniff(if1, if2, xfrm12=None, xfrm21=None, prn=None, L2socket=None See help(sniff) for more. """ - for arg in ['opened_socket', 'offline', 'iface']: + for arg in ['opened_socket', 'offline', 'iface', 'mthread']: if arg in kargs: log_runtime.warning("Argument %s cannot be used in " "bridge_and_sniff() -- ignoring it.", arg) @@ -1115,7 +1115,7 @@ def prn(pkt): return prn_orig(pkt) return sniff(opened_socket={sckt1: if1, sckt2: if2}, prn=prn, - *args, **kargs) + mthread=False, *args, **kargs) @conf.commands.register diff --git a/test/linux.uts b/test/linux.uts index ac46724f960..b20c5e24a08 100644 --- a/test/linux.uts +++ b/test/linux.uts @@ -231,6 +231,7 @@ try: def _sniffer(): sniffed = sniff(iface='veth_scapy_1', store=True, + mthread=False, count=2, lfilter=lambda p: Ether in p and p[Ether].type == 0xbeef, started_callback=_sniffer_started) @@ -287,6 +288,7 @@ frm_count = 0 def _sniffer(): sniffed = sniff(iface='veth_scapy_1', store=True, + mthread=False, count=2, lfilter=lambda p: Ether in p and p[Ether].type == 0xbeef, started_callback=_sniffer_started) diff --git a/test/sendsniff.uts b/test/sendsniff.uts index 4f76bddfc6f..e4dc1569f86 100644 --- a/test/sendsniff.uts +++ b/test/sendsniff.uts @@ -27,7 +27,8 @@ else: t_sniff = Thread( target=sniff, kwargs={"iface": "tap1", "count": 5, "prn": Packet.summary, - "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"} + "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4", + "mthread": False} ) t_sniff.start() @@ -52,7 +53,8 @@ t_sniff.join() t_sniff = Thread( target=sniff, kwargs={"iface": "tap1", "count": 5, "prn": Packet.summary, - "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5"} + "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5", + "mthread": False} ) t_sniff.start() @@ -114,7 +116,8 @@ else: t_sniff = Thread( target=sniff, kwargs={"iface": "tun1", "count": 5, "prn": Packet.summary, - "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"} + "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4", + "mthread": False} ) t_sniff.start() @@ -141,7 +144,8 @@ t_sniff.join() t_sniff = Thread( target=sniff, kwargs={"iface": "tun1", "count": 5, "prn": Packet.summary, - "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5"} + "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5", + "mthread": False} ) t_sniff.start() @@ -273,7 +277,8 @@ def answer_arp_leak(pkt): t_answer = Thread( target=sniff, kwargs={"prn": answer_arp_leak, "timeout": 10, "store": False, - "opened_socket": tap0} + "opened_socket": tap0, + "mthread": False} ) t_answer.start() From 8202b1fd30c176b6baa7a509cc918ff12bff600e Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Sat, 7 Jul 2018 12:59:04 +0200 Subject: [PATCH 3/4] Implement recv_raw in Pcap readers - Allow the recv() function to accept an argument from a previously received recv_raw() - Remove duplicated code from pcapdnet.py --- scapy/arch/linux.py | 24 ++++---- scapy/arch/pcapdnet.py | 123 ++++++++++++++++++++--------------------- scapy/sendrecv.py | 23 +++----- scapy/supersocket.py | 11 +++- scapy/utils.py | 75 ++++++++++++++----------- 5 files changed, 136 insertions(+), 120 deletions(-) diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py index 261b07fa7de..53942f888ff 100644 --- a/scapy/arch/linux.py +++ b/scapy/arch/linux.py @@ -467,8 +467,8 @@ def recv_raw(self, x=MTU): ts = get_last_packet_timestamp(self.ins) return cls, pkt, ts - def recv(self, x=MTU): - pkt = SuperSocket.recv(self, x) + def recv(self, **kwargs): + pkt = SuperSocket.recv(self, **kwargs) if pkt and self.lvl == 2: pkt = pkt.payload return pkt @@ -613,14 +613,18 @@ def recv_raw(self, x=MTU): cls.name) ts = get_last_packet_timestamp(self.ins) - # direction = sa_ll[2] - return cls, pkt, ts # , direction - - def recv(self, x=MTU): - # cls, pkt, ts, direction = self.recv_raw() - # [Dissection stuff] - pkt = SuperSocket.recv(self, x) - # pkt.direction = direction + direction = sa_ll[2] + return cls, pkt, ts, direction + + def recv(self, x=MTU, raw_data=None): + if raw_data is None: + raw_data = self.recv_raw(x) + if raw_data is None: + return None + cls, pkt, ts, direction = raw_data + pkt = SuperSocket.recv(self, x=x, raw_data=(cls, pkt, ts)) + if pkt is not None: + pkt.direction = direction return pkt def send(self, x): diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py index 2bfea0a7fa7..64fd05511f8 100644 --- a/scapy/arch/pcapdnet.py +++ b/scapy/arch/pcapdnet.py @@ -42,19 +42,57 @@ class PcapTimeoutElapsed(Scapy_Exception): class _L2pcapdnetSocket(SuperSocket, SelectableObject): - def check_recv(self): - return True - - def recv_raw(self, x=MTU): - """Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501 + def __init__(self, promisc=None, iface=None): + self.type = type + self.outs = None + if iface is None: + iface = conf.iface + self.iface = iface + if promisc is None: + promisc = conf.sniff_promisc + self.promisc = promisc + + def _process_loaded_streams(self, type, filter, nofilter): + """This is an internal function, used while initializing pcap + sockets""" + # Guess cls ll = self.ins.datalink() if ll in conf.l2types: - cls = conf.l2types[ll] + self.cls = conf.l2types[ll] else: - cls = conf.default_l2 + self.cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s", # noqa: E501 self.iface, ll, cls.name) + # Enable immediate mode: reads return immediately upon packet reception + try: + ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1)) + except: + pass + # Apply init filter & rules + if nofilter: + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501 + filter = "ether proto %i" % type + else: + filter = None + else: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501 + else: + filter = "not (%s)" % conf.except_filter + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501 + if filter: + filter = "(ether proto %i) and (%s)" % (type, filter) + else: + filter = "ether proto %i" % type + if filter: + self.ins.setfilter(filter) + + def check_recv(self): + return True + def recv_raw(self, x=MTU): + """Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501 pkt = None while pkt is None: pkt = self.ins.next() @@ -64,7 +102,7 @@ def recv_raw(self, x=MTU): raise PcapTimeoutElapsed # To understand this behavior, have a look at L2pcapListenSocket's note # noqa: E501 if pkt is None: return None, None, None - return cls, pkt, ts + return self.cls, pkt, ts def nonblock_recv(self): """Receives and dissect a packet in non-blocking mode. @@ -397,37 +435,22 @@ def __del__(self): class L2pcapListenSocket(_L2pcapdnetSocket): desc = "read packets at layer 2 using libpcap" - def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, monitor=None): # noqa: E501 - self.type = type - self.outs = None - self.iface = iface - if iface is None: - iface = conf.iface - if promisc is None: - promisc = conf.sniff_promisc - self.promisc = promisc + def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, # noqa: E501 + monitor=None): # noqa: E501 + _L2pcapdnetSocket.__init__(self, promisc=promisc, iface=iface) # Note: Timeout with Winpcap/Npcap # The 4th argument of open_pcap corresponds to timeout. In an ideal world, we would # noqa: E501 # set it to 0 ==> blocking pcap_next_ex. # However, the way it is handled is very poor, and result in a jerky packet stream. # noqa: E501 # To fix this, we set 100 and the implementation under windows is slightly different, as # noqa: E501 # everything is always received as non-blocking - self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501 - try: - ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1)) - except: - pass - if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given # noqa: E501 - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501 - else: - filter = "not (%s)" % conf.except_filter - if filter: - self.ins.setfilter(filter) + self.ins = open_pcap(self.iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501 + # Post init routine + self._process_loaded_streams(type, filter, nofilter) def close(self): self.ins.close() + self.closed = True def send(self, x): raise Scapy_Exception("Can't send anything with L2pcapListenSocket") # noqa: E501 @@ -439,40 +462,16 @@ class L2pcapSocket(_L2pcapdnetSocket): def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, # noqa: E501 monitor=None): - if iface is None: - iface = conf.iface - self.iface = iface - if promisc is None: - promisc = 0 - self.promisc = promisc + _L2pcapdnetSocket.__init__(self, promisc=promisc, iface=iface) # See L2pcapListenSocket for infos about this line - self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501 + self.ins = open_pcap(self.iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501 # We need to have a different interface open because of an # access violation in Npcap that occurs in multi-threading # (see https://github.com/nmap/nmap/issues/982) - self.outs = open_pcap(iface, MTU, self.promisc, 100) - try: - ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1)) - except: - pass - if nofilter: - if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501 - filter = "ether proto %i" % type - else: - filter = None - else: - if conf.except_filter: - if filter: - filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501 - else: - filter = "not (%s)" % conf.except_filter - if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501 - if filter: - filter = "(ether proto %i) and (%s)" % (type, filter) - else: - filter = "ether proto %i" % type - if filter: - self.ins.setfilter(filter) + self.outs = open_pcap(self.iface, MTU, self.promisc, 100, monitor=monitor) + # Post init routine + self._process_loaded_streams(type, filter, nofilter) + def send(self, x): sx = raw(x) @@ -493,8 +492,8 @@ class L3pcapSocket(L2pcapSocket): # def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): # noqa: E501 # L2pcapSocket.__init__(self, iface, type, filter, nofilter) - def recv(self, x=MTU): - r = L2pcapSocket.recv(self, x) + def recv(self, **kwargs): + r = L2pcapSocket.recv(self, **kwargs) if r: return r.payload else: diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index 2cb392b056c..ccbbc5ff341 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -103,7 +103,7 @@ def _sndrcv_rcv(pks, hsent, stopevent, nbrecv, notans, verbose, chainCC, def _get_pkt(): from scapy.arch.pcapdnet import PcapTimeoutElapsed try: - return pks.recv(MTU) + return pks.recv(x=MTU) except PcapTimeoutElapsed: return None elif conf.use_bpf: @@ -927,7 +927,7 @@ class _InteruptSniff(Scapy_Exception): """Used to interupt sniffing in multi-threading mode""" pass - def _process_pkt(p, sniff_sockets, c): + def _process_pkt(p, sniff_sockets, c, dissector=None): """Function to process a recieved packet""" if p is None: try: @@ -961,19 +961,14 @@ def _async_process(q, stopevent): try: while not stopevent.is_set(): try: - pkt_a, sniff_sockets = q.get_nowait() + pkt_a, dissector, sniff_sockets = q.get_nowait() except queue.Empty: continue - cls, val, ts = pkt_a try: - pkt = cls(val) + pkt = dissector(raw_data=pkt_a) except Exception: - if conf.debug_dissector: - log_runtime.error(exc_info=True) - stopevent.set() - break - pkt = conf.raw_layer(val) - pkt.time = ts + log_runtime.error(exc_info=True) + stopevent.set() try: c = _process_pkt(pkt, sniff_sockets, c) except _InteruptSniff: @@ -989,8 +984,8 @@ def _async_process(q, stopevent): q = queue.Queue() se = threading.Event() - def _append_pkt(p, sniff_sockets, c=None, q=q): - q.put((p, sniff_sockets)) + def _append_pkt(p, sniff_sockets, c=None, q=q, dissector=None): + q.put((p, dissector, sniff_sockets)) if se.is_set(): raise _InteruptSniff _action_pkt = _append_pkt @@ -1017,7 +1012,7 @@ def _append_pkt(p, sniff_sockets, c=None, q=q): p = _read_next(s) except read_allowed_exceptions: continue - c = _action_pkt(p, sniff_sockets, c) + c = _action_pkt(p, sniff_sockets, c, dissector=s.recv) except KeyboardInterrupt: pass except _InteruptSniff: diff --git a/scapy/supersocket.py b/scapy/supersocket.py index 16bf79d7283..5a663d90cb7 100644 --- a/scapy/supersocket.py +++ b/scapy/supersocket.py @@ -47,8 +47,15 @@ def send(self, x): x.sent_time = time.time() return self.outs.send(sx) - def recv(self, x=MTU): - cls, val, ts = self.recv_raw(x) + def recv(self, x=MTU, raw_data=None): + """Receive a packet, and process it. + + params: + - x: maximum packet size (default: MTU) + - raw_data: data received from .recv_raw() (default: None) + if None, will be automatically fetched. + """ + cls, val, ts = raw_data if raw_data is not None else self.recv_raw(x) if not val or not cls: return try: diff --git a/scapy/utils.py b/scapy/utils.py index 9c224b94da1..43fd0074058 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1003,31 +1003,34 @@ def __init__(self, filename, fdesc, magic): warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype, self.linktype)) # noqa: E501 self.LLcls = conf.raw_layer - def read_packet(self, size=MTU): - rp = super(PcapReader, self).read_packet(size=size) + def recv_raw(self, x=MTU): + rp = super(PcapReader, self).read_packet(size=x) if rp is None: return None s, pkt_info = rp - - try: - p = self.LLcls(s) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - p = conf.raw_layer(s) - p.time = pkt_info.sec + (0.000000001 if self.nano else 0.000001) * pkt_info.usec # noqa: E501 - p.wirelen = pkt_info.wirelen - return p + ts = pkt_info.sec + (0.000000001 if self.nano else 0.000001) * pkt_info.usec # noqa: E501 + wirelen = pkt_info.wirelen + return self.LLcls, s, ts, wirelen def read_all(self, count=-1): res = RawPcapReader.read_all(self, count) from scapy import plist return plist.PacketList(res, name=os.path.basename(self.filename)) - def recv(self, size=MTU): - return self.read_packet(size=size) + def recv(self, x=MTU, raw_data=None): + if raw_data is None: + raw_data = self.recv_raw(x) + if raw_data is None: + return None + cls, pkt, ts, wirelen = raw_data + from scapy.supersocket import SuperSocket + pkt = SuperSocket.recv(self, x=x, raw_data=(cls, pkt, ts)) + if pkt is not None: + pkt.wirelen = wirelen + return pkt + + def read_packet(self, x=MTU): + return self.recv(x=x) class RawPcapNgReader(RawPcapReader): @@ -1169,31 +1172,39 @@ class PcapNgReader(RawPcapNgReader): def __init__(self, filename, fdesc, magic): RawPcapNgReader.__init__(self, filename, fdesc, magic) - def read_packet(self, size=MTU): - rp = super(PcapNgReader, self).read_packet(size=size) + def recv_raw(self, x=MTU): + rp = super(PcapNgReader, self).read_packet(size=x) if rp is None: - return None + return None, None, None, None s, (linktype, tsresol, tshigh, tslow, wirelen) = rp - try: - p = conf.l2types[linktype](s) - except KeyboardInterrupt: - raise - except: - if conf.debug_dissector: - raise - p = conf.raw_layer(s) + cls = conf.l2types.get(linktype, None) + ts = None + if cls is None: + warning("PcapNgReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (linktype, linktype)) # noqa: E501 + cls = conf.raw_layer if tshigh is not None: - p.time = float((tshigh << 32) + tslow) / tsresol - p.wirelen = wirelen - return p + ts = float((tshigh << 32) + tslow) / tsresol + return cls, s, ts, wirelen def read_all(self, count=-1): res = RawPcapNgReader.read_all(self, count) from scapy import plist return plist.PacketList(res, name=os.path.basename(self.filename)) - def recv(self, size=MTU): - return self.read_packet() + def recv(self, x=MTU, raw_data=None): + if raw_data is None: + raw_data = self.recv_raw(x) + if raw_data is None: + return None + cls, pkt, ts, wirelen = raw_data + from scapy.supersocket import SuperSocket + pkt = SuperSocket.recv(self, x=x, raw_data=(cls, pkt, ts)) + if pkt is not None: + pkt.wirelen = wirelen + return pkt + + def read_packet(self, size=MTU): + return self.recv(x=size) class RawPcapWriter: From bfddfac1e42db9aa03545ff0706e6aa10789ef2c Mon Sep 17 00:00:00 2001 From: gpotter2 Date: Sat, 7 Jul 2018 13:02:00 +0200 Subject: [PATCH 4/4] Update rdpcap doc --- scapy/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scapy/utils.py b/scapy/utils.py index 43fd0074058..5dec8a4506f 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -835,8 +835,11 @@ def wrpcap(filename, pkt, *args, **kargs): def rdpcap(filename, count=-1): """Read a pcap or pcapng file and return a packet list -count: read only packets + params: + - count: read only packets + For a more efficient function, consider using `sniff(offline=...)` + See help(sniff) for more informations. """ with PcapReader(filename) as fdesc: return fdesc.read_all(count=count)