diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py index cebb6609cbb..c336b5d85d6 100644 --- a/scapy/arch/pcapdnet.py +++ b/scapy/arch/pcapdnet.py @@ -440,7 +440,8 @@ def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, monito # 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 + self.ins = open_pcap(iface, MTU, self.promisc, 100, + monitor=monitor) try: ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1)) except Exception: @@ -469,11 +470,9 @@ def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilt promisc = 0 self.promisc = promisc # See L2pcapListenSocket for infos about this line - self.ins = open_pcap(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) + self.ins = open_pcap(iface, MTU, self.promisc, 100, + monitor=monitor) + self.outs = self.ins try: ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1)) except Exception: @@ -503,16 +502,6 @@ def send(self, x): x.sent_time = time.time() return self.outs.send(sx) - def close(self): - if not self.closed: - ins = getattr(self, "ins", None) - out = getattr(self, "out", None) - if ins: - self.ins.close() - if out and out != ins: - self.outs.close() - self.closed = True - class L3pcapSocket(L2pcapSocket): desc = "read/write packets at layer 3 using only libpcap" # def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): # noqa: E501 diff --git a/scapy/layers/sixlowpan.py b/scapy/layers/sixlowpan.py index 6fb29008a5f..c395e15df78 100644 --- a/scapy/layers/sixlowpan.py +++ b/scapy/layers/sixlowpan.py @@ -481,6 +481,7 @@ def post_dissect(self, data): # The Next Header field is compressed and the next header is # encoded using LOWPAN_NHC + packet.nh = 0x11 # UDP udp = UDP() if self.header_compression and \ self.header_compression & 0x4 == 0x0: diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index f204371d9c1..2c6a713ecbf 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -79,8 +79,14 @@ def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, hsent, timessent, stopev except Exception: log_runtime.exception("--- Error sending packets") if timeout is not None: - stopevent.wait(timeout) - stopevent.set() + def _timeout(stopevent): + stopevent.wait(timeout) + stopevent.set() + thread = threading.Thread( + target=_timeout, args=(stopevent,) + ) + thread.setDaemon(True) + thread.start() def _sndrcv_rcv(pks, hsent, stopevent, nbrecv, notans, verbose, chainCC, @@ -142,33 +148,38 @@ def _get_pkt(): return (hsent, ans, nbrecv, notans) -def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, - retry=0, multi=False, rcv_pks=None, store_unanswered=True, - process=None, prebuild=False): - """Scapy raw function to send a packet and receive its answer. - WARNING: This is an internal function. Using sr/srp/sr1/srp is - more appropriate in many cases. - +_DOC_SNDRCV_PARAMS = """ pks: SuperSocket instance to send/receive packets pkt: the packet to send - rcv_pks: if set, will be used instead of pks to receive packets. packets will still # noqa: E501 - be sent through pks + rcv_pks: if set, will be used instead of pks to receive packets. + packets will still be sent through pks nofilter: put 1 to avoid use of BPF filters retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered # noqa: E501 + if negative, how many times to retry when no more packets + are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus - store_unanswered: whether to store not-answered packets or not. Default True. # noqa: E501 - setting it to False will increase speed, and will return None # noqa: E501 - as the unans list. + store_unanswered: whether to store not-answered packets or not. + setting it to False will increase speed, and will return + None as the unans list. process: if specified, only result from process(pkt) will be stored. the function should follow the following format: lambda sent, received: (func(sent), func2(received)) if the packet is unanswered, `received` will be None. - if `store_unanswered` is False, the function won't be called on un-answered packets. # noqa: E501 - prebuild: pre-build the packets before starting to send them. Default to False. Automatically used # noqa: E501 - when a generator is passed as the packet + if `store_unanswered` is False, the function won't be called on + un-answered packets. + prebuild: pre-build the packets before starting to send them. Automatically + enabled when a generator is passed as the packet + """ + + +def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, + retry=0, multi=False, rcv_pks=None, store_unanswered=True, + process=None, prebuild=False): + """Scapy raw function to send a packet and receive its answer. + WARNING: This is an internal function. Using sr/srp/sr1/srp is + more appropriate in many cases. """ if verbose is None: verbose = conf.verb @@ -204,18 +215,13 @@ def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, hsent = {} timessent = {} if listable else None - thread = threading.Thread( - target=_sndrcv_snd, - args=(pks, timeout, inter, verbose, tobesent, hsent, timessent, stopevent), # noqa: E501 - ) - thread.setDaemon(True) - thread.start() + _sndrcv_snd(pks, timeout, inter, verbose, + tobesent, hsent, timessent, stopevent) hsent, newans, nbrecv, notans = _sndrcv_rcv( - (rcv_pks or pks), hsent, stopevent, nbrecv, notans, verbose, chainCC, multi, # noqa: E501 - _storage_policy=_storage_policy, + (rcv_pks or pks), hsent, stopevent, nbrecv, notans, verbose, + chainCC, multi, _storage_policy=_storage_policy, ) - thread.join() ans.extend(newans) @@ -457,24 +463,9 @@ def _parse_tcpreplay_result(stdout, stderr, argv): @conf.commands.register def sr(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): - """Send and receive packets at layer 3 -nofilter: put 1 to avoid use of BPF filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered # noqa: E501 -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: listen answers only on the given interface -store_unanswered: whether to store not-answered packets or not. Default True. - setting it to False will increase speed, and will return None - as the unans list. -process: if specified, only result from process(pkt) will be stored. - the function should follow the following format: - lambda sent, received: (func(sent), func2(received)) - if the packet is unanswered, `received` will be None. - if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501 - s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) # noqa: E501 + """Send and receive packets at layer 3""" + s = conf.L3socket(promisc=promisc, filter=filter, + iface=iface, nofilter=nofilter) result = sndrcv(s, x, *args, **kargs) s.close() return result @@ -482,24 +473,9 @@ def sr(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): @conf.commands.register def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): - """Send packets at layer 3 and return only the first answer -nofilter: put 1 to avoid use of BPF filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered # noqa: E501 -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: listen answers only on the given interface -store_unanswered: whether to store not-answered packets or not. Default True. - setting it to False will increase speed, and will return None - as the unans list. -process: if specified, only result from process(pkt) will be stored. - the function should follow the following format: - lambda sent, received: (func(sent), func2(received)) - if the packet is unanswered, `received` will be None. - if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501 - s = conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) # noqa: E501 + """Send packets at layer 3 and return only the first answer""" + s = conf.L3socket(promisc=promisc, filter=filter, + nofilter=nofilter, iface=iface) ans, _ = sndrcv(s, x, *args, **kargs) s.close() if len(ans) > 0: @@ -509,27 +485,13 @@ def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs): @conf.commands.register -def srp(x, promisc=None, iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args, **kargs): # noqa: E501 - """Send and receive packets at layer 2 -nofilter: put 1 to avoid use of BPF filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered # noqa: E501 -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: work only on the given interface -store_unanswered: whether to store not-answered packets or not. Default True. - setting it to False will increase speed, and will return None - as the unans list. -process: if specified, only result from process(pkt) will be stored. - the function should follow the following format: - lambda sent, received: (func(sent), func2(received)) - if the packet is unanswered, `received` will be None. - if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501 +def srp(x, promisc=None, iface=None, iface_hint=None, filter=None, + nofilter=0, type=ETH_P_ALL, *args, **kargs): + """Send and receive packets at layer 2""" if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] - s = conf.L2socket(promisc=promisc, iface=iface, filter=filter, nofilter=nofilter, type=type) # noqa: E501 + s = conf.L2socket(promisc=promisc, iface=iface, + filter=filter, nofilter=nofilter, type=type) result = sndrcv(s, x, *args, **kargs) s.close() return result @@ -537,33 +499,26 @@ def srp(x, promisc=None, iface=None, iface_hint=None, filter=None, nofilter=0, t @conf.commands.register def srp1(*args, **kargs): - """Send and receive packets at layer 2 and return only the first answer -nofilter: put 1 to avoid use of BPF filters -retry: if positive, how many times to resend unanswered packets - if negative, how many times to retry when no more packets are answered # noqa: E501 -timeout: how much time to wait after the last packet has been sent -verbose: set verbosity level -multi: whether to accept multiple answers for the same stimulus -filter: provide a BPF filter -iface: work only on the given interface -store_unanswered: whether to store not-answered packets or not. Default True. - setting it to False will increase speed, and will return None - as the unans list. -process: if specified, only result from process(pkt) will be stored. - the function should follow the following format: - lambda sent, received: (func(sent), func2(received)) - if the packet is unanswered, `received` will be None. - if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501 + """Send and receive packets at layer 2 and return only the first answer""" ans, _ = srp(*args, **kargs) if len(ans) > 0: return ans[0][1] else: return None + +# Append doc +for sr_func in [srp, srp1, sr, sr1]: + sr_func.__doc__ += _DOC_SNDRCV_PARAMS + + # SEND/RECV LOOP METHODS -def __sr_loop(srfunc, pkts, prn=lambda x: x[1].summary(), prnfail=lambda x: x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): # noqa: E501 +def __sr_loop(srfunc, pkts, prn=lambda x: x[1].summary(), + prnfail=lambda x: x.summary(), + inter=1, timeout=None, count=None, verbose=None, store=1, + *args, **kargs): n = 0 r = 0 ct = conf.color_theme @@ -632,7 +587,8 @@ def srploop(pkts, *args, **kargs): # SEND/RECV FLOOD METHODS -def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, store_unanswered=True, process=None, timeout=None): # noqa: E501 +def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, + store_unanswered=True, process=None, timeout=None): if not verbose: verbose = conf.verb listable = (isinstance(pkt, Packet) and pkt.__iterlen__() == 1) or isinstance(pkt, list) # noqa: E501 @@ -674,7 +630,8 @@ def _timeout(timeout): # We don't use _sndrcv_snd verbose (it messes the logs up as in a thread that ends after receiving) # noqa: E501 thread = threading.Thread( target=_sndrcv_snd, - args=(pks, None, inter, False, infinite_gen, hsent, timessent, stopevent), # noqa: E501 + args=(pks, None, inter, False, + infinite_gen, hsent, timessent, stopevent) ) thread.setDaemon(True) thread.start() @@ -893,7 +850,8 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, try: if started_callback: started_callback() - while sniff_sockets: + continue_sniff = True + while sniff_sockets and continue_sniff: if timeout is not None: remain = stoptime - time.time() if remain <= 0: @@ -923,10 +881,10 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, # on_packet_received handles the prn/storage session.on_packet_received(p) if stop_filter and stop_filter(p): - sniff_sockets = [] + continue_sniff = False break if 0 < count <= c: - sniff_sockets = [] + continue_sniff = False break except KeyboardInterrupt: pass diff --git a/test/benchmark/common.py b/test/benchmark/common.py new file mode 100644 index 00000000000..d95777f2772 --- /dev/null +++ b/test/benchmark/common.py @@ -0,0 +1,15 @@ +# This file is part of Scapy +# See http://www.secdev.org/projects/scapy for more information +# Copyright (C) Guillaume Valadon +# This program is published under a GPLv2 license + +import os +import sys + +scapy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +sys.path.append(scapy_path) + +from scapy.all import * + +print("Scapy %s - Benchmarks" % VERSION) +print("Python %s" % sys.version.replace("\n", "")) diff --git a/test/benchmark/dissection_and_build.py b/test/benchmark/dissection_and_build.py index b46b42e2108..b29c3ef3143 100644 --- a/test/benchmark/dissection_and_build.py +++ b/test/benchmark/dissection_and_build.py @@ -3,14 +3,9 @@ # Copyright (C) Guillaume Valadon # This program is published under a GPLv2 license +from common import * import time -from scapy.all import * -from scapy.modules.six.moves import range - -if WINDOWS: - route_add_loopback() - N = 10000 raw_packet = b'E\x00\x00(\x00\x01\x00\x00@\x11|\xc2\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x14\x00Z\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' diff --git a/test/benchmark/latency_router.py b/test/benchmark/latency_router.py new file mode 100644 index 00000000000..84db9ca2579 --- /dev/null +++ b/test/benchmark/latency_router.py @@ -0,0 +1,34 @@ +# This file is part of Scapy +# See http://www.secdev.org/projects/scapy for more information +# Copyright (C) Gabriel Potter +# This program is published under a GPLv2 license + + +# https://github.com/secdev/scapy/issues/1791 + +from common import * + +# Router IP +dest = conf.route.route("0.0.0.0")[2] + +send_tcp = False +send_icmp = True + +pkts = [] +for i in range(1,50): + a = IP(dst=dest) / TCP(flags="S", seq=i, sport=65000, dport=55556) + b = IP(dst=dest)/ICMP() + if send_tcp: + pkts.append(a) + if send_icmp: + pkts.append(b) + +ans, unans = sr(pkts, filter="host {0}".format(dest), inter=0, timeout=1, prebuild=True, store_unanswered=False) + +print("scapy version: {}".format(conf.version)) + +for pkt in ans: + sent = pkt[0] + received = pkt[1] + res = (received.time - sent.sent_time) + print("%s %s : %s" % (received.time, sent.sent_time, res))