scapy icon indicating copy to clipboard operation
scapy copied to clipboard

No route found for IPv6 destination ff02::1:ff00:1 (no default route?) when running IPv6-only

Open oskar456 opened this issue 1 year ago • 5 comments

Brief description

When running Scapy on IPv6-only host, it produces warnings every time it sends any data despite working normally.

Scapy version

2.5.0

Python version

3.10.12

Operating system

3.10.0-1160.102.1.el7.x86_64

Additional environment information

Running in an IPv6-only docker container based on Ubuntu

How to reproduce

>>> a, u = sr(IPv6(dst="2001:db8:f:1::1")/ICMPv6EchoRequest())

Actual result

Begin emission:
WARNING: No route found for IPv6 destination ff02::1:ff00:1 (no default route?)
WARNING: Mac address to reach destination not found. Using broadcast.
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets

Expected result

Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets

Related resources

The routing table has an IPv6 default route. The issue lies in the fact the loopback gets selected as the default interface (conf.iface) because the selection algorithm of default interface is IPv4-only. Also even though eth0 has some IPv6 addresses, they are not shown when there is no IPv4 address configured.

>>> conf.route6
INFO: Table cropped to fit the terminal (conf.auto_crop_tables==True)
Destination                       Next Hop                   Iface  Src candidates                    Metric
2001:db8:f:1::/64                 ::                         eth0   2001:db8:f:1:a8c1:abff:fe7e:4cd5  256   
fe80::/64                         ::                         eth0   fe80::a8c1:abff:fe7e:4cd5         256   
::1/128                           ::                         lo     ::1                               0     
2001:db8:f:1:a8c1:abff:fe7e:4cd_  ::                         lo     ::1                               0     
fe80::a8c1:abff:fe7e:4cd5/128     ::                         lo     ::1                               0     
::/0                              fe80::a8c1:abff:fe2c:3b40  eth0   2001:db8:f:1:a8c1:abff:fe7e:4cd5  1024  
>>> conf.ifaces
Source  Index  Name  MAC                IPv4       IPv6
sys     1      lo    00:00:00:00:00:00  127.0.0.1  ::1 
sys     1620   eth0  aa:c1:ab:7e:4c:d5                 
>>> conf.iface
<NetworkInterface lo [UP+LOOPBACK+RUNNING]>

Changing conf.iface manually to eth0 works around the issue. I failed to find a way how to do this automatically though, as it is apparently not possible to adjust this using prestart.py.

oskar456 avatar Feb 28 '24 15:02 oskar456

Thanks for you report. Does this patch fixes your issue:

$ git diff scapy/
diff --git a/scapy/interfaces.py b/scapy/interfaces.py
index f40f529e..30056b60 100644
--- a/scapy/interfaces.py
+++ b/scapy/interfaces.py
@@ -372,6 +372,8 @@ def get_if_list():
 def get_working_if():
     # type: () -> NetworkInterface
     """Return an interface that works"""
+
+    # IPv4
     # return the interface associated with the route with smallest
     # mask (route by default if it exists)
     routes = conf.route.routes[:]
@@ -383,6 +385,17 @@ def get_working_if():
         iface = resolve_iface(ifname)  # type: ignore
         if iface.is_valid():
             return iface
+
+    # IPv6
+    routes_ipv6 = conf.route6.routes
+    default_routes_ipv6 = [r for r in routes_ipv6 if r[0] == "::"]
+    if default_routes_ipv6:
+        # Sort the default routes using the priority (at index -1)
+        tmp_routes = sorted(default_routes_ipv6, key=lambda r: r[-1])
+
+        # Return the interface (at index 2), of the highes priority default
+        return tmp_routes[-1][2]
+
     # There is no hope left
     return resolve_iface(conf.loopback_name)

guedou avatar Mar 16 '24 10:03 guedou

Thanks for you report. Does this patch fixes your issue:

Hey, sorry for delayed answer. No, this patch does not help as the IPv6 section is never reached, if there is at least one route in the IPv4 routing table. Which is almost always the case because of the loopback interface:

>>> conf.route
Network    Netmask    Gateway  Iface  Output IP  Metric
127.0.0.0  255.0.0.0  0.0.0.0  lo     127.0.0.1  1

oskar456 avatar Mar 27 '24 13:03 oskar456

That's a good point. We could check if the only IPv4 interface is the loopback, and move on to IPv6 if it is true. What do you think?

guedou avatar Apr 21 '24 12:04 guedou

That would work. Or we can just look for IPv4 default route, if it is not there look for IPv6 default route and if that is not there either, fall back to loopback.

oskar456 avatar Apr 25 '24 12:04 oskar456

So I have created the proposed solution:

diff --git a/scapy/interfaces.py b/scapy/interfaces.py
index f40f529e..73403098 100644
--- a/scapy/interfaces.py
+++ b/scapy/interfaces.py
@@ -372,11 +372,17 @@ def get_if_list():
 def get_working_if():
     # type: () -> NetworkInterface
     """Return an interface that works"""
-    # return the interface associated with the route with smallest
-    # mask (route by default if it exists)
-    routes = conf.route.routes[:]
-    routes.sort(key=lambda x: x[1])
-    ifaces = (x[3] for x in routes)
+    # return the interface associated with the default route
+    # IPv4 default route is preferred, then IPv6 route shorter or equal than /8 or 
+    # loopback as a fallback
+    default_routes_v4 = (x for x in conf.route.routes if x[1] == 0)
+    if conf.route6:
+        default_routes_v6 = (x for x in conf.route6.routes if x[1] <= 8)
+        default_routes_v6 = sorted(default_routes_v6, key=lambda x: x[1])
+    else:
+        default_routes_v6 = list()
+
+    ifaces = (x[3] for x in itertools.chain(default_routes_v4, default_routes_v6))
     # First check the routing ifaces from best to worse,
     # then check all the available ifaces as backup.
     for ifname in itertools.chain(ifaces, conf.ifaces.values()):

Unfortunately it does not work because conf.route6 is populated later than get_working_if() is called. Trying to import route6 from interfaces.py leads to circular import. Any idea how to resolve this?

oskar456 avatar May 07 '24 08:05 oskar456