Skip to content

Commit

Permalink
Merge pull request #28424 from mrc0mmand/networkd-ra-captive-portals
Browse files Browse the repository at this point in the history
test-network: check for captive portals received via NDISC
  • Loading branch information
yuwata committed Jul 18, 2023
2 parents 106fcf0 + c1dd58b commit 74c4ad5
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 20 deletions.
9 changes: 9 additions & 0 deletions test/test-network/conf/25-veth-bridge-captive.network
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=client-p
Name=router-captivep

[Network]
Bridge=bridge99
IPv6AcceptRA=no
IPv6SendRA=yes
11 changes: 11 additions & 0 deletions test/test-network/conf/25-veth-client-captive.network
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=client

[Network]
IPv6AcceptRA=yes

[IPv6AcceptRA]
UseDNS=no
UseDomains=no
UseCaptivePortal=yes
9 changes: 9 additions & 0 deletions test/test-network/conf/25-veth-router-captive.netdev
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=router-captive
Kind=veth
MACAddress=12:34:56:78:9a:99

[Peer]
Name=router-captivep
MACAddress=12:34:56:78:9b:99
7 changes: 7 additions & 0 deletions test/test-network/conf/25-veth-router-captive.network
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=router-captive

[Network]
IPv6AcceptRA=no
IPv6SendRA=no
11 changes: 11 additions & 0 deletions test/test-network/conf/radvd/captive-portal.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
interface router-captive
{
AdvSendAdvert on;
AdvCaptivePortalAPI "http://systemd.io";

prefix 2002:da8:1:99::/64
{
AdvOnLink on;
AdvAutonomous on;
};
};
124 changes: 104 additions & 20 deletions test/test-network/systemd-networkd-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
isc_dhcpd_pid_file = '/run/networkd-ci/test-isc-dhcpd.pid'
isc_dhcpd_lease_file = '/run/networkd-ci/test-isc-dhcpd.lease'

radvd_pid_file = '/run/networkd-ci/test-radvd.pid'

systemd_lib_paths = ['/usr/lib/systemd', '/lib/systemd']
which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))

Expand Down Expand Up @@ -537,6 +539,23 @@ def read_ipv6_sysctl_attr(link, attribute):
def read_ipv4_sysctl_attr(link, attribute):
return read_ip_sysctl_attr(link, attribute, 'ipv4')

def stop_by_pid_file(pid_file):
if not os.path.exists(pid_file):
return
with open(pid_file, 'r', encoding='utf-8') as f:
pid = f.read().rstrip(' \t\r\n\0')
os.kill(int(pid), signal.SIGTERM)
for _ in range(25):
try:
os.kill(int(pid), 0)
print(f"PID {pid} is still alive, waiting...")
time.sleep(.2)
except OSError as e:
if e.errno == errno.ESRCH:
break
print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
rm_f(pid_file)

def start_dnsmasq(*additional_options, interface='veth-peer', lease_time='2m', ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20'):
command = (
'dnsmasq',
Expand All @@ -558,23 +577,6 @@ def start_dnsmasq(*additional_options, interface='veth-peer', lease_time='2m', i
) + additional_options
check_output(*command)

def stop_by_pid_file(pid_file):
if not os.path.exists(pid_file):
return
with open(pid_file, 'r', encoding='utf-8') as f:
pid = f.read().rstrip(' \t\r\n\0')
os.kill(int(pid), signal.SIGTERM)
for _ in range(25):
try:
os.kill(int(pid), 0)
print(f"PID {pid} is still alive, waiting...")
time.sleep(.2)
except OSError as e:
if e.errno == errno.ESRCH:
break
print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
os.remove(pid_file)

def stop_dnsmasq():
stop_by_pid_file(dnsmasq_pid_file)
rm_f(dnsmasq_lease_file)
Expand All @@ -594,6 +596,29 @@ def stop_isc_dhcpd():
stop_by_pid_file(isc_dhcpd_pid_file)
rm_f(isc_dhcpd_lease_file)

def start_radvd(*additional_options, config_file):
config_file_path = os.path.join(networkd_ci_temp_dir, 'radvd', config_file)
command = (
'radvd',
f'--pidfile={radvd_pid_file}',
f'--config={config_file_path}',
'--logmethod=stderr',
) + additional_options
check_output(*command)

def stop_radvd():
stop_by_pid_file(radvd_pid_file)

def radvd_check_config(config_file):
if not shutil.which('radvd'):
print('radvd is not installed, assuming the config check failed')
return False

# Note: can't use networkd_ci_temp_dir here, as this command may run before that dir is
# set up (one instance is @unittest.skipX())
config_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf/radvd', config_file)
return call(f'radvd --config={config_file_path} --configtest') == 0

def networkd_invocation_id():
return check_output('systemctl show --value -p InvocationID systemd-networkd.service')

Expand Down Expand Up @@ -637,9 +662,10 @@ def setup_common():
print()

def tear_down_common():
# 1. stop DHCP servers
# 1. stop DHCP/RA servers
stop_dnsmasq()
stop_isc_dhcpd()
stop_radvd()

# 2. remove modules
call_quiet('rmmod netdevsim')
Expand Down Expand Up @@ -695,7 +721,7 @@ def setUpModule():
[
'[Service]',
'ExecStart=',
f'ExecStart=!!{udevd_bin}',
f'ExecStart=!!@{udevd_bin} systemd-udevd',
]
)
create_unit_dropin(
Expand Down Expand Up @@ -4553,6 +4579,64 @@ def test_router_preference(self):
print(output)
self.assertIn('pref low', output)

@unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
def test_captive_portal(self):
copy_network_unit('25-veth-client.netdev',
'25-veth-router-captive.netdev',
'26-bridge.netdev',
'25-veth-client-captive.network',
'25-veth-router-captive.network',
'25-veth-bridge-captive.network',
'25-bridge99.network')
start_networkd()
self.wait_online(['bridge99:routable', 'client-p:enslaved',
'router-captive:degraded', 'router-captivep:enslaved'])

start_radvd(config_file='captive-portal.conf')
networkctl_reconfigure('client')
self.wait_online(['client:routable'])

self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
output = check_output(*networkctl_cmd, 'status', 'client', env=env)
print(output)
self.assertIn('Captive Portal: http://systemd.io', output)

@unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
def test_invalid_captive_portal(self):
def radvd_write_config(captive_portal_uri):
with open(os.path.join(networkd_ci_temp_dir, 'radvd/bogus-captive-portal.conf'), mode='w', encoding='utf-8') as f:
f.write(f'interface router-captive {{ AdvSendAdvert on; AdvCaptivePortalAPI "{captive_portal_uri}"; prefix 2002:da8:1:99::/64 {{ AdvOnLink on; AdvAutonomous on; }}; }};')

captive_portal_uris = [
"42ěščěškd ěšč ě s",
" ",
"🤔",
]

copy_network_unit('25-veth-client.netdev',
'25-veth-router-captive.netdev',
'26-bridge.netdev',
'25-veth-client-captive.network',
'25-veth-router-captive.network',
'25-veth-bridge-captive.network',
'25-bridge99.network')
start_networkd()
self.wait_online(['bridge99:routable', 'client-p:enslaved',
'router-captive:degraded', 'router-captivep:enslaved'])

for uri in captive_portal_uris:
print(f"Captive portal: {uri}")
radvd_write_config(uri)
stop_radvd()
start_radvd(config_file='bogus-captive-portal.conf')
networkctl_reconfigure('client')
self.wait_online(['client:routable'])

self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
output = check_output(*networkctl_cmd, 'status', 'client', env=env)
print(output)
self.assertNotIn('Captive Portal:', output)

class NetworkdDHCPServerTests(unittest.TestCase, Utilities):

def setUp(self):
Expand Down Expand Up @@ -5882,7 +5966,7 @@ def test_mtu_link_ipv6_mtu(self):
networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
udevd_bin = os.path.join(ns.build_dir, 'systemd-udevd')
udevd_bin = os.path.join(ns.build_dir, 'udevadm')
wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
Expand Down

0 comments on commit 74c4ad5

Please sign in to comment.