Skip to content

Commit 5311c5e

Browse files
authored
Merge pull request #1847 from gpotter2/fixlatency
Improve latency tests scapy 2.4.2
2 parents 2b66232 + 186aa17 commit 5311c5e

File tree

6 files changed

+119
-127
lines changed

6 files changed

+119
-127
lines changed

scapy/arch/pcapdnet.py

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, monito
440440
# However, the way it is handled is very poor, and result in a jerky packet stream. # noqa: E501
441441
# To fix this, we set 100 and the implementation under windows is slightly different, as # noqa: E501
442442
# everything is always received as non-blocking
443-
self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501
443+
self.ins = open_pcap(iface, MTU, self.promisc, 100,
444+
monitor=monitor)
444445
try:
445446
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
446447
except Exception:
@@ -469,11 +470,9 @@ def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilt
469470
promisc = 0
470471
self.promisc = promisc
471472
# See L2pcapListenSocket for infos about this line
472-
self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501
473-
# We need to have a different interface open because of an
474-
# access violation in Npcap that occurs in multi-threading
475-
# (see https://github.com/nmap/nmap/issues/982)
476-
self.outs = open_pcap(iface, MTU, self.promisc, 100)
473+
self.ins = open_pcap(iface, MTU, self.promisc, 100,
474+
monitor=monitor)
475+
self.outs = self.ins
477476
try:
478477
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
479478
except Exception:
@@ -503,16 +502,6 @@ def send(self, x):
503502
x.sent_time = time.time()
504503
return self.outs.send(sx)
505504

506-
def close(self):
507-
if not self.closed:
508-
ins = getattr(self, "ins", None)
509-
out = getattr(self, "out", None)
510-
if ins:
511-
self.ins.close()
512-
if out and out != ins:
513-
self.outs.close()
514-
self.closed = True
515-
516505
class L3pcapSocket(L2pcapSocket):
517506
desc = "read/write packets at layer 3 using only libpcap"
518507
# def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): # noqa: E501

scapy/layers/sixlowpan.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ def post_dissect(self, data):
481481
# The Next Header field is compressed and the next header is
482482
# encoded using LOWPAN_NHC
483483

484+
packet.nh = 0x11 # UDP
484485
udp = UDP()
485486
if self.header_compression and \
486487
self.header_compression & 0x4 == 0x0:

scapy/sendrecv.py

Lines changed: 63 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,14 @@ def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, hsent, timessent, stopev
7979
except Exception:
8080
log_runtime.exception("--- Error sending packets")
8181
if timeout is not None:
82-
stopevent.wait(timeout)
83-
stopevent.set()
82+
def _timeout(stopevent):
83+
stopevent.wait(timeout)
84+
stopevent.set()
85+
thread = threading.Thread(
86+
target=_timeout, args=(stopevent,)
87+
)
88+
thread.setDaemon(True)
89+
thread.start()
8490

8591

8692
def _sndrcv_rcv(pks, hsent, stopevent, nbrecv, notans, verbose, chainCC,
@@ -142,33 +148,38 @@ def _get_pkt():
142148
return (hsent, ans, nbrecv, notans)
143149

144150

145-
def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False,
146-
retry=0, multi=False, rcv_pks=None, store_unanswered=True,
147-
process=None, prebuild=False):
148-
"""Scapy raw function to send a packet and receive its answer.
149-
WARNING: This is an internal function. Using sr/srp/sr1/srp is
150-
more appropriate in many cases.
151-
151+
_DOC_SNDRCV_PARAMS = """
152152
pks: SuperSocket instance to send/receive packets
153153
pkt: the packet to send
154-
rcv_pks: if set, will be used instead of pks to receive packets. packets will still # noqa: E501
155-
be sent through pks
154+
rcv_pks: if set, will be used instead of pks to receive packets.
155+
packets will still be sent through pks
156156
nofilter: put 1 to avoid use of BPF filters
157157
retry: if positive, how many times to resend unanswered packets
158-
if negative, how many times to retry when no more packets are answered # noqa: E501
158+
if negative, how many times to retry when no more packets
159+
are answered
159160
timeout: how much time to wait after the last packet has been sent
160161
verbose: set verbosity level
161162
multi: whether to accept multiple answers for the same stimulus
162-
store_unanswered: whether to store not-answered packets or not. Default True. # noqa: E501
163-
setting it to False will increase speed, and will return None # noqa: E501
164-
as the unans list.
163+
store_unanswered: whether to store not-answered packets or not.
164+
setting it to False will increase speed, and will return
165+
None as the unans list.
165166
process: if specified, only result from process(pkt) will be stored.
166167
the function should follow the following format:
167168
lambda sent, received: (func(sent), func2(received))
168169
if the packet is unanswered, `received` will be None.
169-
if `store_unanswered` is False, the function won't be called on un-answered packets. # noqa: E501
170-
prebuild: pre-build the packets before starting to send them. Default to False. Automatically used # noqa: E501
171-
when a generator is passed as the packet
170+
if `store_unanswered` is False, the function won't be called on
171+
un-answered packets.
172+
prebuild: pre-build the packets before starting to send them. Automatically
173+
enabled when a generator is passed as the packet
174+
"""
175+
176+
177+
def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False,
178+
retry=0, multi=False, rcv_pks=None, store_unanswered=True,
179+
process=None, prebuild=False):
180+
"""Scapy raw function to send a packet and receive its answer.
181+
WARNING: This is an internal function. Using sr/srp/sr1/srp is
182+
more appropriate in many cases.
172183
"""
173184
if verbose is None:
174185
verbose = conf.verb
@@ -204,18 +215,13 @@ def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False,
204215
hsent = {}
205216
timessent = {} if listable else None
206217

207-
thread = threading.Thread(
208-
target=_sndrcv_snd,
209-
args=(pks, timeout, inter, verbose, tobesent, hsent, timessent, stopevent), # noqa: E501
210-
)
211-
thread.setDaemon(True)
212-
thread.start()
218+
_sndrcv_snd(pks, timeout, inter, verbose,
219+
tobesent, hsent, timessent, stopevent)
213220

214221
hsent, newans, nbrecv, notans = _sndrcv_rcv(
215-
(rcv_pks or pks), hsent, stopevent, nbrecv, notans, verbose, chainCC, multi, # noqa: E501
216-
_storage_policy=_storage_policy,
222+
(rcv_pks or pks), hsent, stopevent, nbrecv, notans, verbose,
223+
chainCC, multi, _storage_policy=_storage_policy,
217224
)
218-
thread.join()
219225

220226
ans.extend(newans)
221227

@@ -457,49 +463,19 @@ def _parse_tcpreplay_result(stdout, stderr, argv):
457463

458464
@conf.commands.register
459465
def sr(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs):
460-
"""Send and receive packets at layer 3
461-
nofilter: put 1 to avoid use of BPF filters
462-
retry: if positive, how many times to resend unanswered packets
463-
if negative, how many times to retry when no more packets are answered # noqa: E501
464-
timeout: how much time to wait after the last packet has been sent
465-
verbose: set verbosity level
466-
multi: whether to accept multiple answers for the same stimulus
467-
filter: provide a BPF filter
468-
iface: listen answers only on the given interface
469-
store_unanswered: whether to store not-answered packets or not. Default True.
470-
setting it to False will increase speed, and will return None
471-
as the unans list.
472-
process: if specified, only result from process(pkt) will be stored.
473-
the function should follow the following format:
474-
lambda sent, received: (func(sent), func2(received))
475-
if the packet is unanswered, `received` will be None.
476-
if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501
477-
s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) # noqa: E501
466+
"""Send and receive packets at layer 3"""
467+
s = conf.L3socket(promisc=promisc, filter=filter,
468+
iface=iface, nofilter=nofilter)
478469
result = sndrcv(s, x, *args, **kargs)
479470
s.close()
480471
return result
481472

482473

483474
@conf.commands.register
484475
def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs):
485-
"""Send packets at layer 3 and return only the first answer
486-
nofilter: put 1 to avoid use of BPF filters
487-
retry: if positive, how many times to resend unanswered packets
488-
if negative, how many times to retry when no more packets are answered # noqa: E501
489-
timeout: how much time to wait after the last packet has been sent
490-
verbose: set verbosity level
491-
multi: whether to accept multiple answers for the same stimulus
492-
filter: provide a BPF filter
493-
iface: listen answers only on the given interface
494-
store_unanswered: whether to store not-answered packets or not. Default True.
495-
setting it to False will increase speed, and will return None
496-
as the unans list.
497-
process: if specified, only result from process(pkt) will be stored.
498-
the function should follow the following format:
499-
lambda sent, received: (func(sent), func2(received))
500-
if the packet is unanswered, `received` will be None.
501-
if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501
502-
s = conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) # noqa: E501
476+
"""Send packets at layer 3 and return only the first answer"""
477+
s = conf.L3socket(promisc=promisc, filter=filter,
478+
nofilter=nofilter, iface=iface)
503479
ans, _ = sndrcv(s, x, *args, **kargs)
504480
s.close()
505481
if len(ans) > 0:
@@ -509,61 +485,40 @@ def sr1(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kargs):
509485

510486

511487
@conf.commands.register
512-
def srp(x, promisc=None, iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args, **kargs): # noqa: E501
513-
"""Send and receive packets at layer 2
514-
nofilter: put 1 to avoid use of BPF filters
515-
retry: if positive, how many times to resend unanswered packets
516-
if negative, how many times to retry when no more packets are answered # noqa: E501
517-
timeout: how much time to wait after the last packet has been sent
518-
verbose: set verbosity level
519-
multi: whether to accept multiple answers for the same stimulus
520-
filter: provide a BPF filter
521-
iface: work only on the given interface
522-
store_unanswered: whether to store not-answered packets or not. Default True.
523-
setting it to False will increase speed, and will return None
524-
as the unans list.
525-
process: if specified, only result from process(pkt) will be stored.
526-
the function should follow the following format:
527-
lambda sent, received: (func(sent), func2(received))
528-
if the packet is unanswered, `received` will be None.
529-
if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501
488+
def srp(x, promisc=None, iface=None, iface_hint=None, filter=None,
489+
nofilter=0, type=ETH_P_ALL, *args, **kargs):
490+
"""Send and receive packets at layer 2"""
530491
if iface is None and iface_hint is not None:
531492
iface = conf.route.route(iface_hint)[0]
532-
s = conf.L2socket(promisc=promisc, iface=iface, filter=filter, nofilter=nofilter, type=type) # noqa: E501
493+
s = conf.L2socket(promisc=promisc, iface=iface,
494+
filter=filter, nofilter=nofilter, type=type)
533495
result = sndrcv(s, x, *args, **kargs)
534496
s.close()
535497
return result
536498

537499

538500
@conf.commands.register
539501
def srp1(*args, **kargs):
540-
"""Send and receive packets at layer 2 and return only the first answer
541-
nofilter: put 1 to avoid use of BPF filters
542-
retry: if positive, how many times to resend unanswered packets
543-
if negative, how many times to retry when no more packets are answered # noqa: E501
544-
timeout: how much time to wait after the last packet has been sent
545-
verbose: set verbosity level
546-
multi: whether to accept multiple answers for the same stimulus
547-
filter: provide a BPF filter
548-
iface: work only on the given interface
549-
store_unanswered: whether to store not-answered packets or not. Default True.
550-
setting it to False will increase speed, and will return None
551-
as the unans list.
552-
process: if specified, only result from process(pkt) will be stored.
553-
the function should follow the following format:
554-
lambda sent, received: (func(sent), func2(received))
555-
if the packet is unanswered, `received` will be None.
556-
if `store_unanswered` is False, the function won't be called on un-answered packets.""" # noqa: E501
502+
"""Send and receive packets at layer 2 and return only the first answer"""
557503
ans, _ = srp(*args, **kargs)
558504
if len(ans) > 0:
559505
return ans[0][1]
560506
else:
561507
return None
562508

509+
510+
# Append doc
511+
for sr_func in [srp, srp1, sr, sr1]:
512+
sr_func.__doc__ += _DOC_SNDRCV_PARAMS
513+
514+
563515
# SEND/RECV LOOP METHODS
564516

565517

566-
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
518+
def __sr_loop(srfunc, pkts, prn=lambda x: x[1].summary(),
519+
prnfail=lambda x: x.summary(),
520+
inter=1, timeout=None, count=None, verbose=None, store=1,
521+
*args, **kargs):
567522
n = 0
568523
r = 0
569524
ct = conf.color_theme
@@ -632,7 +587,8 @@ def srploop(pkts, *args, **kargs):
632587
# SEND/RECV FLOOD METHODS
633588

634589

635-
def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, store_unanswered=True, process=None, timeout=None): # noqa: E501
590+
def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False,
591+
store_unanswered=True, process=None, timeout=None):
636592
if not verbose:
637593
verbose = conf.verb
638594
listable = (isinstance(pkt, Packet) and pkt.__iterlen__() == 1) or isinstance(pkt, list) # noqa: E501
@@ -674,7 +630,8 @@ def _timeout(timeout):
674630
# We don't use _sndrcv_snd verbose (it messes the logs up as in a thread that ends after receiving) # noqa: E501
675631
thread = threading.Thread(
676632
target=_sndrcv_snd,
677-
args=(pks, None, inter, False, infinite_gen, hsent, timessent, stopevent), # noqa: E501
633+
args=(pks, None, inter, False,
634+
infinite_gen, hsent, timessent, stopevent)
678635
)
679636
thread.setDaemon(True)
680637
thread.start()
@@ -893,7 +850,8 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None,
893850
try:
894851
if started_callback:
895852
started_callback()
896-
while sniff_sockets:
853+
continue_sniff = True
854+
while sniff_sockets and continue_sniff:
897855
if timeout is not None:
898856
remain = stoptime - time.time()
899857
if remain <= 0:
@@ -923,10 +881,10 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None,
923881
# on_packet_received handles the prn/storage
924882
session.on_packet_received(p)
925883
if stop_filter and stop_filter(p):
926-
sniff_sockets = []
884+
continue_sniff = False
927885
break
928886
if 0 < count <= c:
929-
sniff_sockets = []
887+
continue_sniff = False
930888
break
931889
except KeyboardInterrupt:
932890
pass

test/benchmark/common.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# This file is part of Scapy
2+
# See http://www.secdev.org/projects/scapy for more information
3+
# Copyright (C) Guillaume Valadon
4+
# This program is published under a GPLv2 license
5+
6+
import os
7+
import sys
8+
9+
scapy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
10+
sys.path.append(scapy_path)
11+
12+
from scapy.all import *
13+
14+
print("Scapy %s - Benchmarks" % VERSION)
15+
print("Python %s" % sys.version.replace("\n", ""))

test/benchmark/dissection_and_build.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,9 @@
33
# Copyright (C) Guillaume Valadon
44
# This program is published under a GPLv2 license
55

6+
from common import *
67
import time
78

8-
from scapy.all import *
9-
from scapy.modules.six.moves import range
10-
11-
if WINDOWS:
12-
route_add_loopback()
13-
149
N = 10000
1510
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'
1611

test/benchmark/latency_router.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This file is part of Scapy
2+
# See http://www.secdev.org/projects/scapy for more information
3+
# Copyright (C) Gabriel Potter
4+
# This program is published under a GPLv2 license
5+
6+
7+
# https://github.com/secdev/scapy/issues/1791
8+
9+
from common import *
10+
11+
# Router IP
12+
dest = conf.route.route("0.0.0.0")[2]
13+
14+
send_tcp = False
15+
send_icmp = True
16+
17+
pkts = []
18+
for i in range(1,50):
19+
a = IP(dst=dest) / TCP(flags="S", seq=i, sport=65000, dport=55556)
20+
b = IP(dst=dest)/ICMP()
21+
if send_tcp:
22+
pkts.append(a)
23+
if send_icmp:
24+
pkts.append(b)
25+
26+
ans, unans = sr(pkts, filter="host {0}".format(dest), inter=0, timeout=1, prebuild=True, store_unanswered=False)
27+
28+
print("scapy version: {}".format(conf.version))
29+
30+
for pkt in ans:
31+
sent = pkt[0]
32+
received = pkt[1]
33+
res = (received.time - sent.sent_time)
34+
print("%s %s : %s" % (received.time, sent.sent_time, res))

0 commit comments

Comments
 (0)