Skip to content

Commit c1d43f4

Browse files
committed
Use IPv6 default route when no IPv4 is available
1 parent ac3d5bb commit c1d43f4

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

doc/scapy/usage.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,9 @@ make\_table() displays a table according to a lambda function
226226
Sending packets
227227
---------------
228228

229+
.. note::
230+
Scapy automatically detects the network interface to be used by default, and stores this result in ``conf.iface``. Packets built by Scapy uses this variable to set relevant fields such as Ethernet source addresses. When sending packets, with functions such as ``send()``, Scapy will use the network interface stored in ``conf.iface``. This behavior can be changed using the ``iface=`` argument. With IPv6 and link-local addresses, it is mandatory to setup both ``conf.iface`` and ``iface=`` the same value to get the desired result, as Scapy cannot find which interface to use for link-local communications.
231+
229232
.. index::
230233
single: Sending packets, send
231234

scapy/interfaces.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,19 +372,45 @@ def get_if_list():
372372
def get_working_if():
373373
# type: () -> NetworkInterface
374374
"""Return an interface that works"""
375+
376+
# IPv4
375377
# return the interface associated with the route with smallest
376378
# mask (route by default if it exists)
379+
ipv4_interface = resolve_iface(conf.loopback_name)
377380
routes = conf.route.routes[:]
378381
routes.sort(key=lambda x: x[1])
382+
print(routes)
379383
ifaces = (x[3] for x in routes)
380384
# First check the routing ifaces from best to worse,
381385
# then check all the available ifaces as backup.
382386
for ifname in itertools.chain(ifaces, conf.ifaces.values()):
387+
print(ifname)
383388
iface = resolve_iface(ifname) # type: ignore
389+
print(ifname, iface)
384390
if iface.is_valid():
385-
return iface
391+
ipv4_interface = iface
392+
break
393+
394+
print(ipv4_interface, conf.loopback_name)
395+
if ipv4_interface.name != conf.loopback_name:
396+
return ipv4_interface
397+
398+
# IPv6
399+
if conf.route6:
400+
routes_ipv6 = conf.route6.routes
401+
default_routes_ipv6 = [r for r in routes_ipv6 if r[0] == "::"]
402+
if default_routes_ipv6:
403+
# Sort the default routes using the priority (at index -1)
404+
tmp_routes = sorted(default_routes_ipv6, key=lambda r: r[-1])
405+
406+
# Return the interface (at index 3) of the highest priority default
407+
ifname = tmp_routes[-1][3]
408+
iface = resolve_iface(ifname)
409+
if iface.is_valid():
410+
return iface
411+
386412
# There is no hope left
387-
return resolve_iface(conf.loopback_name)
413+
return ipv4_interface
388414

389415

390416
def get_working_ifaces():

test/regression.uts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,20 @@ assert "cuz you know the way to go" + conf.iface # right +
334334

335335
_test_get_working_if()
336336

337+
# Test IPv6 default route interface selection
338+
ni4 = NetworkInterface(InterfaceProvider(), {"name": conf.loopback_name, "ips": ["127.0.0.1"], "mac": "aa:aa:aa:aa:aa:aa"})
339+
ni6 = NetworkInterface(InterfaceProvider(), {"name": "scapy0", "ips": ["::1"], "mac": "aa:aa:aa:aa:aa:aa"})
340+
341+
import mock
342+
@mock.patch("scapy.interfaces.conf.ifaces.values")
343+
@mock.patch("scapy.interfaces.conf.route.routes", [(0, 0, '0.0.0.0', 'lo0', '127.0.0.1', 1)])
344+
@mock.patch("scapy.interfaces.conf.route6.routes", [("::", 0, "", ni6, [""], 0)])
345+
def _test_get_working_if_v6(ifaces_values):
346+
ifaces_values.side_effect = lambda: []
347+
assert get_working_if() == ni6
348+
349+
_test_get_working_if_v6()
350+
337351
= Test conf.ifaces
338352

339353
conf.iface

0 commit comments

Comments
 (0)