]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-network: add test case for DHCP relay
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 10 May 2026 22:48:27 +0000 (07:48 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 21 May 2026 07:55:59 +0000 (16:55 +0900)
test/test-network/conf/25-dhcp-relay-downstream.network [new file with mode: 0644]
test/test-network/conf/25-dhcp-relay-upstream.network [new file with mode: 0644]
test/test-network/conf/25-dhcp-relay.conf [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

diff --git a/test/test-network/conf/25-dhcp-relay-downstream.network b/test/test-network/conf/25-dhcp-relay-downstream.network
new file mode 100644 (file)
index 0000000..49ff765
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=relay-down
+
+[Network]
+Address=198.51.100.10/24
+IPv6AcceptRA=no
+DHCPRelay=downstream
+
+[DHCPRelay]
+AgentAddress=198.51.100.10
+GatewayAddress=198.51.100.10
+CircuitId=string:test-dhcp-relay-circuit-id
+VirtualSubnetSelection=string:subnet-hoge
diff --git a/test/test-network/conf/25-dhcp-relay-upstream.network b/test/test-network/conf/25-dhcp-relay-upstream.network
new file mode 100644 (file)
index 0000000..d84f6a7
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=relay-up
+
+[Network]
+Address=192.0.2.10/24
+IPv6AcceptRA=no
+DHCPRelay=upstream
+
+[DHCPRelay]
+AgentAddress=192.0.2.10
+InterfacePriority=100
diff --git a/test/test-network/conf/25-dhcp-relay.conf b/test/test-network/conf/25-dhcp-relay.conf
new file mode 100644 (file)
index 0000000..5d6bcb2
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[DHCPRelay]
+ServerAddress=192.0.2.1
+OverrideServerIdentifier=yes
+RemoteId=string:test-dhcp-relay-remote-id
index 8609a63916a39d6fc5062e12e4164905cde1a366..28c418460c079b2194cc719cba7a4f2428f9ae21 100755 (executable)
@@ -1222,6 +1222,7 @@ def tear_down_common():
     # 3. remove network namespace
     call_quiet('ip netns del ns99')
     call_quiet('ip netns del ns-bridge')
+    call_quiet('ip netns del ns-relay')
     call_quiet('ip netns del ns-server')
 
     # 4. remove links
@@ -8393,6 +8394,115 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
         # For issue #30763.
         self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED')
 
+    def test_sd_dhcp_relay(self):
+        check_output('ip netns add ns-relay')
+        check_output('ip netns add ns-server')
+
+        check_output('ip link add relay-down type veth peer client')
+        check_output('ip link set relay-down netns ns-relay')
+        check_output('ip netns exec ns-relay ip link set relay-down up')
+
+        check_output('ip link add relay-up type veth peer server')
+        check_output('ip link set relay-up netns ns-relay')
+        check_output('ip netns exec ns-relay ip link set relay-up up')
+
+        check_output('ip link set server netns ns-server')
+        check_output('ip netns exec ns-server ip link set server up')
+        check_output('ip netns exec ns-server ip address add 192.0.2.1/24 dev server')
+
+        with tempfile.TemporaryDirectory() as tmp:
+            conf_dir = pathlib.Path(tmp) / 'run/systemd/networkd.conf.d'
+            unit_dir = pathlib.Path(tmp) / 'run/systemd/network'
+            netif_dir = pathlib.Path(tmp) / 'run/systemd/netif'
+            mkdir_p(conf_dir)
+            mkdir_p(unit_dir)
+            mkdir_p(netif_dir)
+
+            # Do not use shutil.chown(), as it fails when running with sanitizers.
+            check_output(f'chown systemd-network:systemd-network {netif_dir}')
+
+            cp(pathlib.Path(networkd_ci_temp_dir) / '25-dhcp-relay.conf', conf_dir)
+            cp(pathlib.Path(networkd_ci_temp_dir) / '25-dhcp-relay-downstream.network', unit_dir)
+            cp(pathlib.Path(networkd_ci_temp_dir) / '25-dhcp-relay-upstream.network', unit_dir)
+
+            cmd = [
+                'systemd-run',
+                '--unit=networkd-test-dhcp-relay.service',
+                '--service-type=notify-reload',
+                '-p', 'User=systemd-network',
+                '-p', 'FileDescriptorStoreMax=512',
+                '-p', 'AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_BPF CAP_SYS_ADMIN',
+                '-p', 'CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_BPF CAP_SYS_ADMIN',
+                '-p', f'BindPaths={conf_dir}:/run/systemd/networkd.conf.d',
+                '-p', f'BindPaths={unit_dir}:/run/systemd/network',
+                '-p', f'BindPaths={netif_dir}:/run/systemd/netif',
+                '-p', 'TemporaryFileSystem=/run/dbus',
+                '-p', 'TemporaryFileSystem=/run/systemd/report',
+                '-p', 'TemporaryFileSystem=/run/systemd/resolve.hook',
+                '-p', 'TemporaryFileSystem=/var/lib/systemd/network',
+                '-p', 'TemporaryFileSystem=/etc/systemd/network',
+                '-p', 'TemporaryFileSystem=/usr/lib/systemd/network',
+                '-p', 'ReadOnlyPaths=/sys',
+                '-p', 'NetworkNamespacePath=/run/netns/ns-relay',
+            ]  # fmt: skip
+            if enable_debug:
+                cmd += ['-p', 'Environment=SYSTEMD_LOG_LEVEL=debug']
+            if asan_options:
+                cmd += ['-p', f'Environment=ASAN_OPTIONS={asan_options}']
+            if lsan_options:
+                cmd += ['-p', f'Environment=LSAN_OPTIONS={lsan_options}']
+            if ubsan_options:
+                cmd += ['-p', f'Environment=UBSAN_OPTIONS={ubsan_options}']
+            if asan_options or lsan_options or ubsan_options:
+                cmd += ['-p', 'TimeoutStopFailureMode=abort']
+
+            cmd += [networkd_bin]
+
+            try:
+                check_output(*cmd)
+
+                start_dnsmasq(
+                    namespace='ns-server',
+                    interface='server',
+                    ipv4_range='198.51.100.100,198.51.100.109',
+                    ipv4_router='198.51.100.10',
+                )
+
+                copy_network_unit('25-dhcp-client-simple.network')
+                start_networkd()
+                self.wait_online('client:routable')
+
+                print('## ip -4 address show dev client scope global')
+                output = check_output('ip -4 address show dev client scope global')
+                print(output)
+                self.assertRegex(output, r'198\.51\.100\.10[0-9]/24')
+
+                print('## ip -4 route show dev client')
+                output = check_output('ip -4 route show dev client')
+                print(output)
+                # fmt: off
+                self.assertRegex(output, r'default via 198\.51\.100\.10 proto dhcp src 198\.51\.100\.10[0-9]')
+                self.assertRegex(output, r'198\.51\.100\.0/24 proto kernel scope link src 198\.51\.100\.10[0-9]')
+                self.assertRegex(output, r'198\.51\.100\.10 proto dhcp scope link src 198\.51\.100\.10[0-9]')
+                # fmt: on
+
+                print('## dnsmasq log')
+                output = read_dnsmasq_log_file()
+                print(output)
+                # The option 82 is logged as agent-info since dnsmasq-2.92, otherwise logged as agent-id.
+                self.assertRegex(output, r'option: 82 (agent-id|agent-info)')
+
+                print('## journal of networkd-test-dhcp-relay.service')
+                check_output('journalctl --sync')
+                output = check_output('journalctl --no-hostname --output=short-monotonic --unit=networkd-test-dhcp-relay.service -I')  # fmt: skip
+                self.assertIn('relay-down: DHCPv4 relay: Received BOOTREQUEST', output)
+                self.assertIn('relay-up: DHCPv4 relay: Forwarded BOOTREQUEST', output)
+                self.assertIn('relay-up: DHCPv4 relay: Received BOOTREPLY', output)
+                self.assertIn('relay-down: DHCPv4 relay: Forwarded BOOTREPLY', output)
+            finally:
+                call_quiet('systemctl stop networkd-test-dhcp-relay.service')
+                call_quiet('systemctl reset-failed networkd-test-dhcp-relay.service')
+
 
 class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
     def setUp(self):