mirror of
https://github.com/systemd/systemd
synced 2024-07-21 10:17:21 +00:00
test-network: check for captive portals received via NDISC
This requires fairly recent radvd that supports sending RAs with captive portals [0]. Also, this should hopefully provide coverage for issues like: - https://github.com/systemd/systemd/issues/28229 - https://github.com/systemd/systemd/issues/28231 - https://github.com/systemd/systemd/issues/28277 [0] https://github.com/radvd-project/radvd/pull/141
This commit is contained in:
parent
5a000cd46f
commit
c1dd58b3b6
9
test/test-network/conf/25-veth-bridge-captive.network
Normal file
9
test/test-network/conf/25-veth-bridge-captive.network
Normal file
|
@ -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
test/test-network/conf/25-veth-client-captive.network
Normal file
11
test/test-network/conf/25-veth-client-captive.network
Normal file
|
@ -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
test/test-network/conf/25-veth-router-captive.netdev
Normal file
9
test/test-network/conf/25-veth-router-captive.netdev
Normal file
|
@ -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
test/test-network/conf/25-veth-router-captive.network
Normal file
7
test/test-network/conf/25-veth-router-captive.network
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=router-captive
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
IPv6SendRA=no
|
11
test/test-network/conf/radvd/captive-portal.conf
Normal file
11
test/test-network/conf/radvd/captive-portal.conf
Normal file
|
@ -0,0 +1,11 @@
|
|||
interface router-captive
|
||||
{
|
||||
AdvSendAdvert on;
|
||||
AdvCaptivePortalAPI "http://systemd.io";
|
||||
|
||||
prefix 2002:da8:1:99::/64
|
||||
{
|
||||
AdvOnLink on;
|
||||
AdvAutonomous on;
|
||||
};
|
||||
};
|
|
@ -35,6 +35,8 @@ dnsmasq_lease_file = '/run/networkd-ci/test-dnsmasq.lease'
|
|||
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(':'))
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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')
|
||||
|
@ -4553,6 +4579,64 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
|||
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):
|
||||
|
|
Loading…
Reference in a new issue