network: register already enslaved interfaces when their master appears (#43557)
When networkd starts or restarts, links are enumerated in ifindex order. A slave enumerated before its master cannot register itself in link_append_to_master(), as the master Link object does not exist yet. The master then wrongly believes it has no slaves until the next RTM_NEWLINK message for the slave arrives. The stale slaves set affects operational state propagation (degraded-carrier detection) and any code that inspects the slaves of a master. For instance, on restart, when networkd thinks that the master has no slaves yet, we try to set the bond fail-over policy, which the kernel then refuses because in reality the bond does have slaves. To fix the issue, this updates the list of slaves after enumeration. Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=bond99
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
Address=192.168.123.45/24
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user