diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 8f68a50297..52643cf3c3 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -643,6 +643,11 @@ static int netdev_is_ready_to_create(NetDev *netdev, Link *link) { if (link->set_link_messages > 0) return false; + + /* If stacked netdevs are created before the underlying interface being activated, then + * the activation policy for the netdevs are ignored. See issue #22593. */ + if (!link->activated) + return false; } if (NETDEV_VTABLE(netdev)->is_ready_to_create) diff --git a/test/test-network/conf/25-activation-policy.network b/test/test-network/conf/25-activation-policy.network index a9294d88a0..401d35521c 100644 --- a/test/test-network/conf/25-activation-policy.network +++ b/test/test-network/conf/25-activation-policy.network @@ -1,6 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later [Match] Name=test1 +Name=vlan99 [Network] Address=192.168.10.30/24 diff --git a/test/test-network/conf/25-vlan6.netdev b/test/test-network/conf/25-vlan6.netdev deleted file mode 100644 index b2e5a18cb3..0000000000 --- a/test/test-network/conf/25-vlan6.netdev +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[NetDev] -Name=vlan6 -Kind=vlan -MTUBytes=1500 - -[VLAN] -Id=6 diff --git a/test/test-network/conf/25-vlan6.network b/test/test-network/conf/25-vlan6.network deleted file mode 100644 index 4174972081..0000000000 --- a/test/test-network/conf/25-vlan6.network +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Match] -Name=vlan6 - -[Network] -IPv6AcceptRA=false -Address=100.100.100.2/24 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index c5c1da9694..0a8349fd5b 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -609,11 +609,13 @@ class Utilities(): needle = f'{link}: Bringing link {state}' flag = state.upper() for iteration in range(timeout+1): + if iteration != 0: + time.sleep(1) + if not link_exists(link): + continue output = check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id) if needle in output and flag in check_output(f'ip link show {link}'): return True - if iteration < timeout: - time.sleep(1) if fail_assert: self.fail(f'Timed out waiting for {link} activated.') return False @@ -638,13 +640,14 @@ class Utilities(): setup_state = r'\S+' for secs in range(setup_timeout + 1): + if secs != 0: + time.sleep(1) + if not link_exists(link): + continue output = check_output(*networkctl_cmd, '-n', '0', 'status', link, env=env) print(output) if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output): return True - # don't bother sleeping if time is up - if secs < setup_timeout: - time.sleep(1) if fail_assert: self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}') return False @@ -2053,6 +2056,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): 'test1', 'veth-peer', 'veth99', + 'vlan99', 'vrf99', ] @@ -2060,6 +2064,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): '11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', + '21-vlan.netdev', + '21-vlan-test1.network', '23-active-slave.network', '24-keep-configuration-static.network', '24-search-domain.network', @@ -3115,10 +3121,12 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') self.wait_operstate('test1', 'routable') - def _test_activation_policy(self, test): + def _test_activation_policy(self, test, interface): conffile = '25-activation-policy.network' if test: conffile = f'{conffile}.d/{test}.conf' + if interface == 'vlan99': + copy_unit_to_networkd_unit_path('21-vlan.netdev', '21-vlan-test1.network') copy_unit_to_networkd_unit_path('11-dummy.netdev', conffile, dropins=False) start_networkd() @@ -3128,36 +3136,38 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): next_up = not expect_up if test.endswith('down'): - self.wait_activated('test1') + self.wait_activated(interface) for iteration in range(4): with self.subTest(iteration=iteration, expect_up=expect_up): operstate = 'routable' if expect_up else 'off' setup_state = 'configured' if expect_up else ('configuring' if iteration == 0 else None) - self.wait_operstate('test1', operstate, setup_state=setup_state, setup_timeout=20) + self.wait_operstate(interface, operstate, setup_state=setup_state, setup_timeout=20) if expect_up: - self.assertIn('UP', check_output('ip link show test1')) - self.assertIn('192.168.10.30/24', check_output('ip address show test1')) - self.assertIn('default via 192.168.10.1', check_output('ip route show dev test1')) + self.assertIn('UP', check_output(f'ip link show {interface}')) + self.assertIn('192.168.10.30/24', check_output(f'ip address show {interface}')) + self.assertIn('default via 192.168.10.1', check_output(f'ip route show dev {interface}')) else: - self.assertIn('DOWN', check_output('ip link show test1')) + self.assertIn('DOWN', check_output(f'ip link show {interface}')) if next_up: - check_output('ip link set dev test1 up') + check_output(f'ip link set dev {interface} up') else: - check_output('ip link set dev test1 down') + check_output(f'ip link set dev {interface} down') expect_up = initial_up if always else next_up next_up = not next_up if always: time.sleep(1) def test_activation_policy(self): - for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']: - with self.subTest(test=test): - self.setUp() - self._test_activation_policy(test) - self.tearDown() + for interface in ['test1', 'vlan99']: + with self.subTest(interface=interface): + for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']: + with self.subTest(test=test): + self.setUp() + self._test_activation_policy(test, interface) + self.tearDown() def _test_activation_policy_required_for_online(self, policy, required): conffile = '25-activation-policy.network'