Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 5 additions & 16 deletions scapy/arch/pcapdnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was fixed

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:
Expand Down Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplication with supersocket.py


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
Expand Down
1 change: 1 addition & 0 deletions scapy/layers/sixlowpan.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
168 changes: 63 additions & 105 deletions scapy/sendrecv.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -457,49 +463,19 @@ 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


@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:
Expand All @@ -509,61 +485,40 @@ 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


@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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that the only relevant fix? =)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s for #1918

There’s a lot of unrelevant cleaning happening 😄 sorry for the messy PRs

while sniff_sockets and continue_sniff:
if timeout is not None:
remain = stoptime - time.time()
if remain <= 0:
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions test/benchmark/common.py
Original file line number Diff line number Diff line change
@@ -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", ""))
7 changes: 1 addition & 6 deletions test/benchmark/dissection_and_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
34 changes: 34 additions & 0 deletions test/benchmark/latency_router.py
Original file line number Diff line number Diff line change
@@ -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))