]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-network: add test cases for DHCPv6 prefix delegation 21000/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 14 Oct 2021 17:51:18 +0000 (02:51 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 26 Oct 2021 11:43:10 +0000 (20:43 +0900)
12 files changed:
test/test-network/conf/13-dummy.netdev [new file with mode: 0644]
test/test-network/conf/25-veth-downstream.netdev [new file with mode: 0644]
test/test-network/conf/dhcp6pd-downstream-dummy97.network [new file with mode: 0644]
test/test-network/conf/dhcp6pd-downstream-dummy98.network [new file with mode: 0644]
test/test-network/conf/dhcp6pd-downstream-dummy99.network [new file with mode: 0644]
test/test-network/conf/dhcp6pd-downstream-test1.network [new file with mode: 0644]
test/test-network/conf/dhcp6pd-downstream-veth98-peer.network [new file with mode: 0644]
test/test-network/conf/dhcp6pd-downstream-veth98.network [new file with mode: 0644]
test/test-network/conf/dhcp6pd-server.network [new file with mode: 0644]
test/test-network/conf/dhcp6pd-upstream.network [new file with mode: 0644]
test/test-network/conf/isc-dhcpd-dhcp6pd.conf [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

diff --git a/test/test-network/conf/13-dummy.netdev b/test/test-network/conf/13-dummy.netdev
new file mode 100644 (file)
index 0000000..5f34b2f
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=dummy99
+Kind=dummy
diff --git a/test/test-network/conf/25-veth-downstream.netdev b/test/test-network/conf/25-veth-downstream.netdev
new file mode 100644 (file)
index 0000000..1329d91
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=veth98
+Kind=veth
+MACAddress=12:34:56:78:9a:be
+
+[Peer]
+Name=veth98-peer
+MACAddress=12:34:56:78:9a:bf
diff --git a/test/test-network/conf/dhcp6pd-downstream-dummy97.network b/test/test-network/conf/dhcp6pd-downstream-dummy97.network
new file mode 100644 (file)
index 0000000..01174f3
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy97
+
+[Network]
+IPv6PrivacyExtensions=yes
+IPv6AcceptRA=no
+DHCP=no
+DHCPv6PrefixDelegation=yes
+
+[DHCPv6PrefixDelegation]
+UplinkInterface=veth99
+SubnetId=6
+Announce=no
+Token=eui64
+Token=::1a:2b:3c:4d
diff --git a/test/test-network/conf/dhcp6pd-downstream-dummy98.network b/test/test-network/conf/dhcp6pd-downstream-dummy98.network
new file mode 100644 (file)
index 0000000..8260e0c
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6PrivacyExtensions=yes
+IPv6AcceptRA=no
+DHCP=no
+DHCPv6PrefixDelegation=yes
+
+[DHCPv6PrefixDelegation]
+UplinkInterface=veth99
+SubnetId=3
+Announce=no
+Token=eui64
+Token=::1a:2b:3c:4d
diff --git a/test/test-network/conf/dhcp6pd-downstream-dummy99.network b/test/test-network/conf/dhcp6pd-downstream-dummy99.network
new file mode 100644 (file)
index 0000000..ad56f26
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy99
+
+[Network]
+IPv6PrivacyExtensions=yes
+IPv6AcceptRA=no
+DHCP=no
+DHCPv6PrefixDelegation=yes
+
+[DHCPv6PrefixDelegation]
+UplinkInterface=veth99
+Assign=no
+Announce=no
diff --git a/test/test-network/conf/dhcp6pd-downstream-test1.network b/test/test-network/conf/dhcp6pd-downstream-test1.network
new file mode 100644 (file)
index 0000000..78ddc72
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+IPv6PrivacyExtensions=yes
+IPv6AcceptRA=no
+DHCP=no
+DHCPv6PrefixDelegation=yes
+
+[DHCPv6PrefixDelegation]
+UplinkInterface=veth99
+SubnetId=0
+Announce=no
+Token=eui64
+Token=::1a:2b:3c:4d
diff --git a/test/test-network/conf/dhcp6pd-downstream-veth98-peer.network b/test/test-network/conf/dhcp6pd-downstream-veth98-peer.network
new file mode 100644 (file)
index 0000000..db0aec1
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth98-peer
+
+[Network]
+IPv6PrivacyExtensions=yes
+IPv6AcceptRA=yes
+
+[IPv6AcceptRA]
+Token=eui64
+Token=::1a:2b:3c:4e
diff --git a/test/test-network/conf/dhcp6pd-downstream-veth98.network b/test/test-network/conf/dhcp6pd-downstream-veth98.network
new file mode 100644 (file)
index 0000000..14c543d
--- /dev/null
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth98
+
+[Network]
+IPv6PrivacyExtensions=yes
+IPv6AcceptRA=no
+DHCP=no
+DHCPv6PrefixDelegation=yes
+IPv6SendRA=yes
+
+[DHCPv6PrefixDelegation]
+UplinkInterface=veth99
+SubnetId=9
+Announce=yes
+Token=eui64
+Token=::1a:2b:3c:4d
+
+[IPv6SendRA]
+UplinkInterface=:none
+EmitDNS=no
+EmitDomains=no
diff --git a/test/test-network/conf/dhcp6pd-server.network b/test/test-network/conf/dhcp6pd-server.network
new file mode 100644 (file)
index 0000000..f7a8526
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=no
+Address=3ffe:501:ffff:100::1/64
diff --git a/test/test-network/conf/dhcp6pd-upstream.network b/test/test-network/conf/dhcp6pd-upstream.network
new file mode 100644 (file)
index 0000000..dd26f2a
--- /dev/null
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth99
+
+[Network]
+IPv6PrivacyExtensions=yes
+IPv6AcceptRA=no
+DHCP=ipv6
+DHCPv6PrefixDelegation=yes
+
+[DHCPv6]
+WithoutRA=solicit
+
+[DHCPv6PrefixDelegation]
+UplinkInterface=:self
+SubnetId=10
+Announce=no
+Token=eui64
+Token=::1a:2b:3c:4d
diff --git a/test/test-network/conf/isc-dhcpd-dhcp6pd.conf b/test/test-network/conf/isc-dhcpd-dhcp6pd.conf
new file mode 100644 (file)
index 0000000..c95b00e
--- /dev/null
@@ -0,0 +1,21 @@
+default-lease-time 2592000;
+preferred-lifetime 604800;
+
+option dhcp-renewal-time 3600;
+option dhcp-rebinding-time 7200;
+
+# Enable RFC 5007 support (same than for DHCPv4)
+allow leasequery;
+
+option dhcp6.name-servers 3ffe:501:ffff:100:200:ff:fe00:3f3e;
+option dhcp6.domain-search "test.example.com","example.com";
+
+option dhcp6.info-refresh-time 21600;
+
+subnet6 3ffe:501:ffff:100::/64 {
+       # Addresses available to clients
+       range6 3ffe:501:ffff:100::10 3ffe:501:ffff:100::100;
+
+       # Some /64 prefixes available for Prefix Delegation (RFC 3633)
+       prefix6 3ffe:501:ffff:200:: 3ffe:501:ffff:f00:: /56;
+}
index ac5447b9ffd669cd76f73eb0978570a2b2b58d54..fc46c31c5dae9c349135f45dce38e2c845254b98 100755 (executable)
@@ -14,6 +14,7 @@ import sys
 import time
 import unittest
 from shutil import copytree
+from pathlib import Path
 
 network_unit_file_path='/run/systemd/network'
 networkd_runtime_directory='/run/systemd/netif'
@@ -26,6 +27,9 @@ dnsmasq_pid_file='/run/networkd-ci/test-dnsmasq.pid'
 dnsmasq_log_file='/run/networkd-ci/test-dnsmasq.log'
 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'
+
 systemd_lib_paths=['/usr/lib/systemd', '/lib/systemd']
 which_paths=':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
 
@@ -531,6 +535,17 @@ def remove_dnsmasq_log_file():
     if os.path.exists(dnsmasq_log_file):
         os.remove(dnsmasq_log_file)
 
+def start_isc_dhcpd(interface, conf_file):
+    conf_file_path = os.path.join(networkd_ci_path, conf_file)
+    isc_dhcpd_command = f'dhcpd -6 -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
+    Path(isc_dhcpd_lease_file).touch()
+    check_output(isc_dhcpd_command)
+
+def stop_isc_dhcpd():
+    stop_by_pid_file(isc_dhcpd_pid_file)
+    if os.path.exists(isc_dhcpd_lease_file):
+        os.remove(isc_dhcpd_lease_file)
+
 def remove_networkd_state_files():
     if os.path.exists(os.path.join(networkd_runtime_directory, 'state')):
         os.remove(os.path.join(networkd_runtime_directory, 'state'))
@@ -4846,6 +4861,184 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
 
+class NetworkdDHCP6PDTests(unittest.TestCase, Utilities):
+    links = [
+        'dummy97',
+        'dummy98',
+        'dummy99',
+        'test1',
+        'veth98',
+        'veth99',
+    ]
+
+    units = [
+        '11-dummy.netdev',
+        '12-dummy.netdev',
+        '13-dummy.netdev',
+        '25-veth.netdev',
+        '25-veth-downstream.netdev',
+        'dhcp6pd-downstream-dummy97.network',
+        'dhcp6pd-downstream-dummy98.network',
+        'dhcp6pd-downstream-dummy99.network',
+        'dhcp6pd-downstream-test1.network',
+        'dhcp6pd-downstream-veth98.network',
+        'dhcp6pd-downstream-veth98-peer.network',
+        'dhcp6pd-server.network',
+        'dhcp6pd-upstream.network',
+    ]
+
+    def setUp(self):
+        stop_isc_dhcpd()
+        remove_links(self.links)
+        stop_networkd(show_logs=False)
+
+    def tearDown(self):
+        stop_isc_dhcpd()
+        remove_links(self.links)
+        remove_unit_from_networkd_path(self.units)
+        stop_networkd(show_logs=True)
+
+    def test_dhcp6pd(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp6pd-server.network', 'dhcp6pd-upstream.network',
+                                        '25-veth-downstream.netdev', 'dhcp6pd-downstream-veth98.network', 'dhcp6pd-downstream-veth98-peer.network',
+                                        '11-dummy.netdev', 'dhcp6pd-downstream-test1.network',
+                                        'dhcp6pd-downstream-dummy97.network',
+                                        '12-dummy.netdev', 'dhcp6pd-downstream-dummy98.network',
+                                        '13-dummy.netdev', 'dhcp6pd-downstream-dummy99.network')
+
+        start_networkd()
+        self.wait_online(['veth-peer:carrier'])
+        start_isc_dhcpd('veth-peer', 'isc-dhcpd-dhcp6pd.conf')
+        self.wait_online(['veth-peer:routable', 'veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
+                          'veth98:routable', 'veth98-peer:routable'])
+
+        print('### ip -6 address show dev veth-peer scope global')
+        output = check_output('ip -6 address show dev veth-peer scope global')
+        print(output)
+        self.assertIn('inet6 3ffe:501:ffff:100::1/64 scope global', output)
+
+        print('### ip -6 address show dev veth99 scope global')
+        output = check_output('ip -6 address show dev veth99 scope global')
+        print(output)
+        # IA_NA
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:100::[0-9]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
+        # address in IA_PD (Token=static)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (Token=eui64)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]10:1034:56ff:fe78:9abc/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (temporary)
+        # Note that the temporary addresses may appear after the link enters configured state
+        self.wait_address('veth99', 'inet6 3ffe:501:ffff:[2-9a-f]10:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
+
+        print('### ip -6 address show dev test1 scope global')
+        output = check_output('ip -6 address show dev test1 scope global')
+        print(output)
+        # address in IA_PD (Token=static)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (temporary)
+        self.wait_address('test1', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
+
+        print('### ip -6 address show dev dummy98 scope global')
+        output = check_output('ip -6 address show dev dummy98 scope global')
+        print(output)
+        # address in IA_PD (Token=static)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]03:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (temporary)
+        self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]03:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
+
+        print('### ip -6 address show dev veth98 scope global')
+        output = check_output('ip -6 address show dev veth98 scope global')
+        print(output)
+        # address in IA_PD (Token=static)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (Token=eui64)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abe/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (temporary)
+        self.wait_address('veth98', 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
+
+        print('### ip -6 address show dev veth98-peer scope global')
+        output = check_output('ip -6 address show dev veth98-peer scope global')
+        print(output)
+        # NDisc address (Token=static)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1a:2b:3c:4e/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # NDisc address (Token=eui64)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]09:1034:56ff:fe78:9abf/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # NDisc address (temporary)
+        self.wait_address('veth98-peer', 'inet6 3ffe:501:ffff:[2-9a-f]09:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
+
+        print('### ip -6 address show dev dummy99 scope global')
+        output = check_output('ip -6 address show dev dummy98 scope global')
+        print(output)
+
+        print('### ip -6 route show type unreachable')
+        output = check_output('ip -6 route show type unreachable')
+        print(output)
+        self.assertRegex(output, 'unreachable 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
+
+        print('### ip -6 route show dev veth99')
+        output = check_output('ip -6 route show dev veth99')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]10::/64 proto kernel metric [0-9]* expires')
+
+        print('### ip -6 route show dev test1')
+        output = check_output('ip -6 route show dev test1')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires')
+
+        print('### ip -6 route show dev dummy98')
+        output = check_output('ip -6 route show dev dummy98')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]03::/64 proto kernel metric [0-9]* expires')
+
+        print('### ip -6 route show dev dummy99')
+        output = check_output('ip -6 route show dev dummy99')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]01::/64 proto dhcp metric [0-9]* expires')
+
+        print('### ip -6 route show dev veth98')
+        output = check_output('ip -6 route show dev veth98')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto kernel metric [0-9]* expires')
+
+        print('### ip -6 route show dev veth98-peer')
+        output = check_output('ip -6 route show dev veth98-peer')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]09::/64 proto ra metric [0-9]* expires')
+
+        # Test case for a downstream which appears later
+        check_output('ip link add dummy97 type dummy')
+        self.wait_online(['dummy97:routable'])
+
+        print('### ip -6 address show dev dummy97 scope global')
+        output = check_output('ip -6 address show dev dummy97 scope global')
+        print(output)
+        # address in IA_PD (Token=static)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]06:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (temporary)
+        self.wait_address('dummy97', 'inet6 3ffe:501:ffff:[2-9a-f]06:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
+
+        print('### ip -6 route show dev dummy97')
+        output = check_output('ip -6 route show dev dummy97')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]06::/64 proto kernel metric [0-9]* expires')
+
+        # Test case for reconfigure
+        check_output(*networkctl_cmd, 'reconfigure', 'dummy98', env=env)
+        self.wait_online(['dummy98:routable'])
+
+        print('### ip -6 address show dev dummy98 scope global')
+        output = check_output('ip -6 address show dev dummy98 scope global')
+        print(output)
+        # address in IA_PD (Token=static)
+        self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]03:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr')
+        # address in IA_PD (temporary)
+        self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]03:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6')
+
+        print('### ip -6 route show dev dummy98')
+        output = check_output('ip -6 route show dev dummy98')
+        print(output)
+        self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]03::/64 proto kernel metric [0-9]* expires')
+
 class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
     links = [
         'dummy98',