diff --git a/test/test-network/conf/26-bridge-configure-without-carrier.network b/test/test-network/conf/26-bridge-configure-without-carrier.network new file mode 100644 index 00000000000..e1196b866b4 --- /dev/null +++ b/test/test-network/conf/26-bridge-configure-without-carrier.network @@ -0,0 +1,9 @@ +[Match] +Name=bridge99 + +[Network] +LinkLocalAddressing=yes +IPv6AcceptRA=no +ConfigureWithoutCarrier=yes +Address=10.1.2.3/24 +Gateway=10.1.2.1 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 7f9737f809e..6dbc2b0c4f9 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -445,6 +445,9 @@ class Utilities(): def check_link_exists(self, link): self.assertTrue(link_exists(link)) + def check_link_attr(self, *args): + self.assertEqual(read_link_attr(*args[:-1]), args[-1]); + def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True): """Wait for the link to reach the specified operstate and/or setup state. @@ -2649,6 +2652,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): '11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev', + '26-bridge-configure-without-carrier.network', '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', '26-bridge-vlan-master.network', @@ -2763,6 +2767,55 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): print(output) self.assertRegex(output, 'ff00::/8 table local metric 256 (linkdown )?pref medium') + def test_bridge_configure_without_carrier(self): + copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-configure-without-carrier.network', + '11-dummy.netdev') + start_networkd() + + # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations + for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']: + with self.subTest(test=test): + if test == 'no-slave': + # bridge has no slaves; it's up but *might* not have carrier + self.wait_online(['bridge99:no-carrier']) + # due to a bug in the kernel, newly-created bridges are brought up + # *with* carrier, unless they have had any setting changed; e.g. + # their mac set, priority set, etc. Then, they will lose carrier + # as soon as a (down) slave interface is added, and regain carrier + # again once the slave interface is brought up. + #self.check_link_attr('bridge99', 'carrier', '0') + elif test == 'add-slave': + # add slave to bridge, but leave it down; bridge is definitely no-carrier + self.check_link_attr('test1', 'operstate', 'down') + check_output('ip link set dev test1 master bridge99') + self.wait_online(['bridge99:no-carrier:no-carrier']) + self.check_link_attr('bridge99', 'carrier', '0') + elif test == 'slave-up': + # bring up slave, which will have carrier; bridge gains carrier + check_output('ip link set dev test1 up') + self.wait_online(['bridge99:routable']) + self.check_link_attr('bridge99', 'carrier', '1') + elif test == 'slave-no-carrier': + # drop slave carrier; bridge loses carrier + check_output('ip link set dev test1 carrier off') + self.wait_online(['bridge99:no-carrier:no-carrier']) + self.check_link_attr('bridge99', 'carrier', '0') + elif test == 'slave-carrier': + # restore slave carrier; bridge gains carrier + check_output('ip link set dev test1 carrier on') + self.wait_online(['bridge99:routable']) + self.check_link_attr('bridge99', 'carrier', '1') + elif test == 'slave-down': + # bring down slave; bridge loses carrier + check_output('ip link set dev test1 down') + self.wait_online(['bridge99:no-carrier:no-carrier']) + self.check_link_attr('bridge99', 'carrier', '0') + + output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bridge99', env=env) + print(output) + self.assertRegex(output, '10.1.2.3') + self.assertRegex(output, '10.1.2.1') + def test_bridge_ignore_carrier_loss(self): copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev', '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',