diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 62fcd306db..3de23c8dd1 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1035,7 +1035,7 @@ static void link_free_bound_by_list(Link *link) { link_dirty(link); } -static int link_append_to_master(Link *link) { +int link_append_to_master(Link *link) { Link *master; int r; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 9340ecab53..99cdbbeef8 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -222,6 +222,7 @@ int link_get_master(Link *link, Link **ret); int link_getlink_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int link_call_getlink(Link *link, link_netlink_message_handler_t callback); int link_handle_bound_to_list(Link *link); +int link_append_to_master(Link *link); void link_enter_failed(Link *link); void link_set_state(Link *link, LinkState state); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index a1bc5c4691..291589800f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -939,7 +939,18 @@ static int manager_enumerate_links(Manager *m) { if (r < 0) return r; - return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link); + r = manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link); + if (r < 0) + return r; + + /* Slave interfaces enumerated before the master could not register themselves, as the master Link + * object did not exist yet at that time. Register them now, so that the slaves set correctly + * reflects the kernel state. */ + Link *link; + HASHMAP_FOREACH(link, m->links_by_index) + RET_GATHER(r, link_append_to_master(link)); + + return r; } static int manager_enumerate_qdisc(Manager *m) { diff --git a/test/test-network/conf/25-bond-fail-over-mac.netdev b/test/test-network/conf/25-bond-fail-over-mac.netdev new file mode 100644 index 0000000000..122c642117 --- /dev/null +++ b/test/test-network/conf/25-bond-fail-over-mac.netdev @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[NetDev] +Name=bond99 +Kind=bond + +[Bond] +Mode=active-backup +FailOverMACPolicy=active +MIIMonitorSec=1s diff --git a/test/test-network/conf/25-bond-fail-over-mac.network b/test/test-network/conf/25-bond-fail-over-mac.network new file mode 100644 index 0000000000..09da6a0c35 --- /dev/null +++ b/test/test-network/conf/25-bond-fail-over-mac.network @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=bond99 + +[Network] +IPv6AcceptRA=no +Address=192.168.123.45/24 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 7ca0567bab..b8c45017ef 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -6734,6 +6734,23 @@ class NetworkdBondTests(unittest.TestCase, Utilities): check_output('ip link set dummy98 master bond199') self.wait_online('dummy98:enslaved') + def test_bond_fail_over_mac_restart(self): + # The slave needs a lower ifindex than the bond, so that it is enumerated + # first and the race is deterministic. So create it before networkd starts. + check_output('ip link add dummy98 type dummy') + copy_network_unit( + '25-bond-fail-over-mac.netdev', + '25-bond-fail-over-mac.network', + '25-bond-slave.network', + ) + start_networkd() + self.wait_online('dummy98:enslaved', 'bond99:routable') + + self.check_link_attr('bond99', 'bonding', 'fail_over_mac', 'active 1') + + restart_networkd() + self.wait_online('dummy98:enslaved', 'bond99:routable') + def test_bond_active_slave(self): copy_network_unit( '23-active-slave.network',