From 0bc40c69352e850500890b633d326963da4e9a1a Mon Sep 17 00:00:00 2001 From: gpotter2 <10530980+gpotter2@users.noreply.github.com> Date: Sat, 26 Aug 2023 20:42:38 +0200 Subject: [PATCH] Use L3RawSocket(6) automatically on lo (#4099) --- doc/scapy/installation.rst | 35 -------------- doc/scapy/troubleshooting.rst | 91 ++++++++++++++++++++++++++++------- doc/scapy/usage.rst | 16 +----- scapy/config.py | 4 +- scapy/interfaces.py | 33 ++++++++----- scapy/sendrecv.py | 45 +++++++++++------ test/linux.uts | 11 +++-- test/regression.uts | 1 + 8 files changed, 136 insertions(+), 100 deletions(-) diff --git a/doc/scapy/installation.rst b/doc/scapy/installation.rst index afb687b1f51..695b0403a77 100644 --- a/doc/scapy/installation.rst +++ b/doc/scapy/installation.rst @@ -309,41 +309,6 @@ Screenshots :scale: 80 :align: center -Known bugs -^^^^^^^^^^ - -You may bump into the following bugs, which are platform-specific, if Scapy didn't manage work around them automatically: - - * You may not be able to capture WLAN traffic on Windows. Reasons are explained on the `Wireshark wiki `_ and in the `WinPcap FAQ `_. Try switching off promiscuous mode with ``conf.sniff_promisc=False``. - * Packets sometimes cannot be sent to localhost (or local IP addresses on your own host). - -Winpcap/Npcap conflicts -^^^^^^^^^^^^^^^^^^^^^^^ - -As ``Winpcap`` is becoming old, it's recommended to use ``Npcap`` instead. ``Npcap`` is part of the ``Nmap`` project. - -.. note:: - This does NOT apply for Windows XP, which isn't supported by ``Npcap``. - -1. If you get the message ``'Winpcap is installed over Npcap.'`` it means that you have installed both Winpcap and Npcap versions, which isn't recommended. - -You may first **uninstall winpcap from your Program Files**, then you will need to remove:: - - C:/Windows/System32/wpcap.dll - C:/Windows/System32/Packet.dll - -And if you are on an x64 machine:: - - C:/Windows/SysWOW64/wpcap.dll - C:/Windows/SysWOW64/Packet.dll - -To use ``Npcap`` instead, as those files are not removed by the ``Winpcap`` un-installer. - -2. If you get the message ``'The installed Windump version does not work with Npcap'`` it surely means that you have installed an old version of ``Windump``, made for ``Winpcap``. -Download the correct one on https://github.com/hsluoyz/WinDump/releases - -In some cases, it could also mean that you had installed ``Npcap`` and ``Winpcap``, and that ``Windump`` is using ``Winpcap``. Fully delete ``Winpcap`` using the above method to solve the problem. - Build the documentation offline =============================== diff --git a/doc/scapy/troubleshooting.rst b/doc/scapy/troubleshooting.rst index 3025e3c097d..4131ec280f6 100644 --- a/doc/scapy/troubleshooting.rst +++ b/doc/scapy/troubleshooting.rst @@ -8,21 +8,33 @@ FAQ I can't sniff/inject packets in monitor mode. --------------------------------------------- -The use monitor mode varies greatly depending on the platform. +The use monitor mode varies greatly depending on the platform, reasons are explained on the `Wireshark wiki `_: + + *Unfortunately, changing the 802.11 capture modes is very platform/network adapter/driver/libpcap dependent, and might not be possible at all (Windows is very limited here).* + +Here is some guidance on how to properly use monitor mode with Scapy: + +- **Using Libpcap (or Npcap)**: + ``libpcap`` must be called differently by Scapy in order for it to create the sockets in monitor mode. You will need to pass the ``monitor=True`` to any calls that open a socket (``send``, ``sniff``...) or to a Scapy socket that you create yourself (``conf.L2Socket``...) + + **On Windows**, you additionally need to turn on monitor mode on the WiFi card, use:: + + # Of course, conf.iface can be replaced by any interfaces accessed through conf.ifaces + >>> conf.iface.setmonitor(True) -- **Using Libpcap** - ``libpcap`` must be called differently by Scapy in order for it to create the sockets in monitor mode. You will need to pass the ``monitor=True`` to any calls that open a socket (``send``, ``sniff``...) or to a Scapy socket that you create yourself (``conf.L2Socket``...) - **Native Linux (with libpcap disabled):** - You should set the interface in monitor mode on your own. I personally like - to use iwconfig for that (replace ``monitor`` by ``managed`` to disable):: + You should set the interface in monitor mode on your own. The easiest way to do that is to use ``airmon-ng``:: + + $ sudo airmon-ng start wlan0 + + You can also use:: - $ sudo ifconfig IFACE down - $ sudo iwconfig IFACE mode monitor - $ sudo ifconfig IFACE up + $ iw dev wlan0 interface add mon0 type monitor + $ ifconfig mon0 up -**If you are using Npcap:** please note that Npcap ``npcap-0.9983`` broke the 802.11 util back in 2019. It has yet to be fixed (as of Npcap 0.9994) so in the meantime, use `npcap-0.9982.exe `_ + If you want to enable monitor mode manually, have a look at https://wiki.wireshark.org/CaptureSetup/WLAN#linux -.. note:: many adapters do not support monitor mode, especially on Windows, or may incorrectly report the headers. See `the Wireshark doc about this `_ +.. warning:: **If you are using Npcap:** please note that Npcap ``npcap-0.9983`` broke the 802.11 support until ``npcap-1.3.0``. Avoid using those versions. We make our best to make this work, if your adapter works with Wireshark for instance, but not with Scapy, feel free to report an issue. @@ -35,12 +47,14 @@ I can't ping 127.0.0.1 (or ::1). Scapy does not work with 127.0.0.1 (or ::1) on The loopback interface is a very special interface. Packets going through it are not really assembled and disassembled. The kernel routes the packet to its destination while it is still stored an internal structure. What you see with ```tcpdump -i lo``` is only a fake to make you think everything is normal. The kernel is not aware of what Scapy is doing behind his back, so what you see on the loopback interface is also a fake. Except this one did not come from a local structure. Thus the kernel will never receive it. -On Linux, in order to speak to local IPv4 applications, you need to build your packets one layer upper, using a PF_INET/SOCK_RAW socket instead of a PF_PACKET/SOCK_RAW (or its equivalent on other systems than Linux):: +.. note:: Starting from Scapy > **2.5.0**, Scapy will automatically use ``L3RawSocket`` when necessary when using L3-functions (sr-like) on the loopback interface, when libpcap is not in use. + +**On Linux**, in order to speak to local IPv4 applications, you need to build your packets one layer upper, using a PF_INET/SOCK_RAW socket instead of a PF_PACKET/SOCK_RAW (or its equivalent on other systems than Linux):: >>> conf.L3socket >>> conf.L3socket = L3RawSocket - >>> sr1(IP(dst) / ICMP()) + >>> sr1(IP() / ICMP()) > With IPv6, you can simply do:: @@ -50,11 +64,20 @@ With IPv6, you can simply do:: > # Layer 2 - >>> conf.iface = "lo" - >>> srp1(Ether() / IPv6() / ICMPv6EchoRequest()) + >>> srp1(Ether() / IPv6() / ICMPv6EchoRequest(), iface=conf.loopback_name) >> -On Windows, BSD, and macOS, you must deactivate the local firewall and set ````conf.iface``` to the loopback interface prior to using the following commands:: +.. warning:: + On Linux, libpcap does not support loopback IPv4 pings: + >>> conf.use_pcap = True + >>> sr1(IP() / ICMP()) + Begin emission: + Finished sending 1 packets. + ..................................... + + You can disable libpcap using ``conf.use_pcap = False`` or bypass it on layer 3 using ``conf.L3socket = L3RawSocket``. + +**On Windows, BSD, and macOS**, you must deactivate/configure the local firewall prior to using the following commands:: # Layer 3 >>> sr1(IP() / ICMP()) @@ -63,11 +86,45 @@ On Windows, BSD, and macOS, you must deactivate the local firewall and set ````c > # Layer 2 - >>> srp1(Loopback() / IP() / ICMP()) + >>> srp1(Loopback() / IP() / ICMP(), iface=conf.loopback_name) >> - >>> srp1(Loopback() / IPv6() / ICMPv6EchoRequest()) + >>> srp1(Loopback() / IPv6() / ICMPv6EchoRequest(), iface=conf.loopback_name) >> +Getting 'failed to set hardware filter to promiscuous mode' error +----------------------------------------------------------------- + +Disable promiscuous mode:: + + conf.sniff_promisc = False + +Scapy says there are 'Winpcap/Npcap conflicts' +---------------------------------------------- + +**On Windows**, as ``Winpcap`` is becoming old, it's recommended to use ``Npcap`` instead. ``Npcap`` is part of the ``Nmap`` project. + +.. note:: + This does NOT apply for Windows XP, which isn't supported by ``Npcap``. On XP, uninstall ``Npcap`` and keep ``Winpcap``. + +1. If you get the message ``'Winpcap is installed over Npcap.'`` it means that you have installed both Winpcap and Npcap versions, which isn't recommended. + +You may first **uninstall winpcap from your Program Files**, then you will need to remove some files that are not deleted by the ``Winpcap`` uninstaller:: + + C:/Windows/System32/wpcap.dll + C:/Windows/System32/Packet.dll + +And if you are on an x64 machine, additionally the 32-bit variants:: + + C:/Windows/SysWOW64/wpcap.dll + C:/Windows/SysWOW64/Packet.dll + +Once that is done, you'll be able to use ``Npcap`` properly. + +2. If you get the message ``'The installed Windump version does not work with Npcap'`` it means that you have probably installed an old version of ``Windump``, made for ``Winpcap``. +Download the one compatible with ``Npcap`` on https://github.com/hsluoyz/WinDump/releases + +In some cases, it could also mean that you had installed both ``Npcap`` and ``Winpcap``, and that the Npcap ``Windump`` is using ``Winpcap``. Fully delete ``Winpcap`` using the above method to solve the problem. + BPF filters do not work. I'm on a ppp link ------------------------------------------ diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst index 020ab493a6e..a06b5ba200d 100644 --- a/doc/scapy/usage.rst +++ b/doc/scapy/usage.rst @@ -1213,21 +1213,9 @@ Wireless frame injection single: FakeAP, Dot11, wireless, WLAN .. note:: - See the TroubleShooting section for more information on the usage of Monitor mode among Scapy. + See the :doc:`TroubleShooting ` section for more information on the usage of Monitor mode among Scapy. -Provided that your wireless card and driver are correctly configured for frame injection - -:: - - $ iw dev wlan0 interface add mon0 type monitor - $ ifconfig mon0 up - -On Windows, if using Npcap, the equivalent would be to call:: - - >>> # Of course, conf.iface can be replaced by any interfaces accessed through conf.ifaces - ... conf.iface.setmonitor(True) - -you can have a kind of FakeAP:: +Provided that your wireless card and driver are correctly configured for frame injection, you can have a kind of FakeAP:: >>> sendp(RadioTap()/ Dot11(addr1="ff:ff:ff:ff:ff:ff", diff --git a/scapy/config.py b/scapy/config.py index 679f18e53ba..639d19e7556 100755 --- a/scapy/config.py +++ b/scapy/config.py @@ -747,10 +747,8 @@ class Conf(ConfClass): check_TCPerror_seqack = False verb = 2 #: level of verbosity, from 0 (almost mute) to 3 (verbose) prompt = Interceptor("prompt", ">>> ", _prompt_changer) - #: default mode for listening socket (to get answers if you + #: default mode for the promiscuous mode of a socket (to get answers if you #: spoof on a lan) - promisc = True - #: default mode for sniff() sniff_promisc = True # type: bool raw_layer = None # type: Type[Packet] raw_summary = False # type: Union[bool, Callable[[bytes], Any]] diff --git a/scapy/interfaces.py b/scapy/interfaces.py index 3bceeac0978..17985c4e900 100644 --- a/scapy/interfaces.py +++ b/scapy/interfaces.py @@ -12,7 +12,7 @@ from collections import defaultdict from scapy.config import conf -from scapy.consts import WINDOWS +from scapy.consts import WINDOWS, LINUX from scapy.utils import pretty_list from scapy.utils6 import in6_isvalid @@ -20,6 +20,7 @@ import scapy from scapy.compat import UserDict from typing import ( + cast, Any, DefaultDict, Dict, @@ -49,19 +50,27 @@ def reload(self): """Same than load() but for reloads. By default calls load""" return self.load() - def l2socket(self): - # type: () -> Type[scapy.supersocket.SuperSocket] + def _l2socket(self, dev): + # type: (NetworkInterface) -> Type[scapy.supersocket.SuperSocket] """Return L2 socket used by interfaces of this provider""" return conf.L2socket - def l2listen(self): - # type: () -> Type[scapy.supersocket.SuperSocket] + def _l2listen(self, dev): + # type: (NetworkInterface) -> Type[scapy.supersocket.SuperSocket] """Return L2listen socket used by interfaces of this provider""" return conf.L2listen - def l3socket(self): - # type: () -> Type[scapy.supersocket.SuperSocket] + def _l3socket(self, dev, ipv6): + # type: (NetworkInterface, bool) -> Type[scapy.supersocket.SuperSocket] """Return L3 socket used by interfaces of this provider""" + if LINUX and not self.libpcap and dev.name == conf.loopback_name: + # handle the loopback case. see troubleshooting.rst + if ipv6: + from scapy.layers.inet6 import L3RawSocket6 + return cast(Type['scapy.supersocket.SuperSocket'], L3RawSocket6) + else: + from scapy.supersocket import L3RawSocket + return L3RawSocket return conf.L3socket def _is_valid(self, dev): @@ -156,15 +165,15 @@ def is_valid(self): def l2socket(self): # type: () -> Type[scapy.supersocket.SuperSocket] - return self.provider.l2socket() + return self.provider._l2socket(self) def l2listen(self): # type: () -> Type[scapy.supersocket.SuperSocket] - return self.provider.l2listen() + return self.provider._l2listen(self) - def l3socket(self): - # type: () -> Type[scapy.supersocket.SuperSocket] - return self.provider.l3socket() + def l3socket(self, ipv6): + # type: (bool) -> Type[scapy.supersocket.SuperSocket] + return self.provider._l3socket(self, ipv6) def __repr__(self): # type: () -> str diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index 8628a000e96..85136ab18a5 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -11,6 +11,7 @@ from threading import Thread, Event import os import re +import socket import subprocess import time @@ -24,6 +25,7 @@ NetworkInterface, ) from scapy.packet import Packet +from scapy.pton_ntop import inet_pton from scapy.utils import get_temp_file, tcpdump, wrpcap, \ ContextManagerSubprocess, PcapReader, EDecimal from scapy.plist import ( @@ -439,10 +441,10 @@ def send(x, # type: _PacketIterable :param monitor: (not on linux) send in monitor mode :returns: None """ - iface = _interface_selection(iface, x) + iface, ipv6 = _interface_selection(iface, x) return _send( x, - lambda iface: iface.l3socket(), + lambda iface: iface.l3socket(ipv6), iface=iface, **kargs ) @@ -616,19 +618,26 @@ def _parse_tcpreplay_result(stdout_b, stderr_b, argv): def _interface_selection(iface, # type: Optional[_GlobInterfaceType] packet # type: _PacketIterable ): - # type: (...) -> _GlobInterfaceType + # type: (...) -> Tuple[NetworkInterface, bool] """ Select the network interface according to the layer 3 destination """ - + _iff, src, _ = next(packet.__iter__()).route() + ipv6 = False + if src: + try: + inet_pton(socket.AF_INET6, src) + ipv6 = True + except OSError: + pass if iface is None: try: - iff = next(packet.__iter__()).route()[0] + iff = resolve_iface(_iff or conf.iface) except AttributeError: iff = None - return iff or conf.iface + return iff or conf.iface, ipv6 - return iface + return resolve_iface(iface), ipv6 @conf.commands.register @@ -644,9 +653,11 @@ def sr(x, # type: _PacketIterable """ Send and receive packets at layer 3 """ - iface = _interface_selection(iface, x) - s = conf.L3socket(promisc=promisc, filter=filter, - iface=iface, nofilter=nofilter) + iface, ipv6 = _interface_selection(iface, x) + s = iface.l3socket(ipv6)( + promisc=promisc, filter=filter, + iface=iface, nofilter=nofilter, + ) result = sndrcv(s, x, *args, **kargs) s.close() return result @@ -887,8 +898,11 @@ def srflood(x, # type: _PacketIterable :param filter: provide a BPF filter :param iface: listen answers only on the given interface """ - iface = resolve_iface(iface or conf.iface) - s = iface.l3socket()(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) # noqa: E501 + iface, ipv6 = _interface_selection(iface, x) + s = iface.l3socket(ipv6)( + promisc=promisc, filter=filter, + iface=iface, nofilter=nofilter, + ) r = sndrcvflood(s, x, *args, **kargs) s.close() return r @@ -912,8 +926,11 @@ def sr1flood(x, # type: _PacketIterable :param filter: provide a BPF filter :param iface: listen answers only on the given interface """ - iface = resolve_iface(iface or conf.iface) - s = iface.l3socket()(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) # noqa: E501 + iface, ipv6 = _interface_selection(iface, x) + s = iface.l3socket(ipv6)( + promisc=promisc, filter=filter, + nofilter=nofilter, iface=iface, + ) ans, _ = sndrcvflood(s, x, *args, **kargs) s.close() if len(ans) > 0: diff --git a/test/linux.uts b/test/linux.uts index b6a6fc5712c..48e27921cbd 100644 --- a/test/linux.uts +++ b/test/linux.uts @@ -11,12 +11,9 @@ = L3RawSocket ~ netaccess IP TCP linux needs_root -old_l3socket = conf.L3socket -conf.L3socket = L3RawSocket with no_debug_dissector(): x = sr1(IP(dst="www.google.com")/TCP(sport=RandShort(), dport=80, flags="S"),timeout=3) -conf.L3socket = old_l3socket x assert x[IP].ottl() in [32, 64, 128, 255] assert 0 <= x[IP].hops() <= 126 @@ -309,16 +306,20 @@ assert test_L3PacketSocket_sendto_python3() import os from scapy.sendrecv import _interface_selection -assert _interface_selection(None, IP(dst="8.8.8.8")/UDP()) == conf.iface +assert _interface_selection(None, IP(dst="8.8.8.8")/UDP()) == (conf.iface, False) exit_status = os.system("ip link add name scapy0 type dummy") exit_status = os.system("ip addr add 192.0.2.1/24 dev scapy0") +exit_status = os.system("ip addr add fc00::/24 dev scapy0") exit_status = os.system("ip link set scapy0 up") conf.ifaces.reload() conf.route.resync() -assert _interface_selection(None, IP(dst="192.0.2.42")/UDP()) == "scapy0" +conf.route6.resync() +assert _interface_selection(None, IP(dst="192.0.2.42")/UDP()) == ("scapy0", False) +assert _interface_selection(None, IPv6(dst="fc00::ae0d")/UDP()) == ("scapy0", True) exit_status = os.system("ip link del name dev scapy0") conf.ifaces.reload() conf.route.resync() +conf.route6.resync() = Test 802.Q sniffing ~ linux needs_root veth diff --git a/test/regression.uts b/test/regression.uts index 7f43b7fc22b..3f36ec9c47a 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -1518,6 +1518,7 @@ retry_test(_test) = Latency check: localhost ICMP ~ netaccess needs_root linux latency +# Note: still needs to enforce L3RawSocket as this won't work otherwise with libpcap sock = conf.L3socket conf.L3socket = L3RawSocket