From dc4108c43cbe8f4cdddb49fec9787c99c2ef24b9 Mon Sep 17 00:00:00 2001 From: r-vdp Date: Wed, 19 Aug 2026 01:14:21 +0200 Subject: [PATCH] network: re-solicit RAs when the link MAC changes A MAC change can mean the link joined another network, e.g. on an active-backup bond failover with FailOverMACPolicy=active. NDisc only solicits when it starts, so IPv6 sat stale until the next unsolicited RA. Restart NDisc to solicit immediately. --- src/network/networkd-link.c | 12 +++---- src/network/networkd-ndisc.c | 62 ++++++++++++++++++++++++++++++++---- src/network/networkd-ndisc.h | 1 + 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 1e419c1415..62fcd306db 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2490,12 +2490,6 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) if (r < 0) return log_link_debug_errno(link, r, "Could not update MAC address for Router Advertisement: %m"); - if (link->ndisc && link->hw_addr.length == ETH_ALEN) { - r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether); - if (r < 0) - return log_link_debug_errno(link, r, "Could not update MAC for NDisc: %m"); - } - if (link->lldp_rx) { r = sd_lldp_rx_set_filter_address(link->lldp_rx, &link->hw_addr.ether); if (r < 0) @@ -2508,6 +2502,12 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) return log_link_debug_errno(link, r, "Could not update MAC address for LLDP Tx: %m"); } + /* Do this at the end so that we can fail the link in case we don't manage to restart NDisc. */ + r = ndisc_update_mac(link); + if (r < 0) + return log_link_warning_errno( + link, r, "Could not restart IPv6 Router Discovery after MAC change: %m"); + return 1; /* needs reconfigure */ } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 31745b3600..4198a6d017 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -2971,18 +2971,27 @@ static int ndisc_configure(Link *link) { return 0; } +static bool ndisc_can_start(Link *link) { + assert(link); + + if (!link->ndisc || !link->dhcp6_client) + return false; + + if (!link_has_carrier(link)) + return false; + + if (in6_addr_is_null(&link->ipv6ll_address)) + return false; + + return true; +} + int ndisc_start(Link *link) { int r; assert(link); - if (!link->ndisc || !link->dhcp6_client) - return 0; - - if (!link_has_carrier(link)) - return 0; - - if (in6_addr_is_null(&link->ipv6ll_address)) + if (!ndisc_can_start(link)) return 0; r = sd_ndisc_set_link_local_address(link->ndisc, &link->ipv6ll_address); @@ -2998,6 +3007,45 @@ int ndisc_start(Link *link) { return 1; } +int ndisc_update_mac(Link *link) { + int r; + + assert(link); + + if (!link->ndisc || link->hw_addr.length != ETH_ALEN) + return 0; + + r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether); + if (r < 0) + return r; + + if (sd_ndisc_is_running(link->ndisc) <= 0) + return 0; + + /* Only restart if a start is possible right now. Otherwise keep the running client, + * whatever makes the conditions true again should also restart discovery. */ + if (!ndisc_can_start(link)) { + log_link_debug(link, "MAC address changed, but not restarting IPv6 Router Discovery now."); + return 0; + } + + /* A changed MAC can mean a changed network, e.g. on a bond failover. Restart discovery to + * solicit RAs instead of waiting for the next unsolicited one. Keep learned state, so use + * sd_ndisc_stop() rather than ndisc_stop(). */ + r = sd_ndisc_stop(link->ndisc); + if (r < 0) + return r; + + r = ndisc_start(link); + if (r < 0) + return r; + /* Should be unreachable, otherwise check ndisc_can_start(). */ + if (r == 0) + log_link_debug(link, "MAC address changed, but IPv6 Router Discovery was not restarted."); + + return 0; +} + static int ndisc_process_request(Request *req, Link *link, void *userdata) { int r; diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 1d73b9a313..2efa76950c 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -61,6 +61,7 @@ void network_adjust_ndisc(Network *network); int ndisc_start(Link *link); int ndisc_stop(Link *link); +int ndisc_update_mac(Link *link); void ndisc_flush(Link *link); int link_request_ndisc(Link *link);