From 62feba20aaf6848ff76d3be6da743c5638e2471b Mon Sep 17 00:00:00 2001 From: r-vdp Date: Mon, 13 Apr 2026 14:41:11 +0200 Subject: [PATCH] sd-radv: do not stop on transient send errors When the periodic RA timer fires, any error returned by sendmsg() currently propagates up through sd_radv_send() into radv_timeout(), which then calls sd_radv_stop(). The RA engine is never started again until the next carrier transition. On an 802.3ad bond there is a window right after carrier-up where the link is administratively up but no aggregator has been selected yet, so sendmsg() returns ENOBUFS. If the very first RA after a flap lands in that window, radv stops permanently and all clients lose their SLAAC addresses, on-link/PD prefixes, and default router once the previously advertised lifetimes expire, while IPv4 keeps working, leading to a very confusing situation with v4 up and v6 down. Handle this the same way solicited RAs already do (see radv_process_packet()): log the failure and reschedule the timer instead of giving up. ra_sent is left untouched on failure so we stay in the fast initial-advertisement regime until a send actually succeeds. --- src/libsystemd-network/sd-radv.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index b9c59d0164..21a80da38e 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -254,9 +254,13 @@ int sd_radv_send(sd_radv *ra) { r = radv_send_router(ra, NULL); if (r < 0) - return log_radv_errno(ra, r, "Unable to send Router Advertisement: %m"); - - ra->ra_sent++; + /* Do not treat transient send failures (e.g. ENOBUFS while a bond is still selecting an + * aggregator after a carrier flap, or ENETDOWN/EADDRNOTAVAIL during a short link bounce) as + * fatal: log and reschedule so we try again instead of stopping the RA engine for good. + * Solicited RAs already behave this way, see radv_process_packet(). */ + log_radv_errno(ra, r, "Unable to send Router Advertisement, will retry later: %m"); + else + ra->ra_sent++; /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */ if (ra->ra_sent <= RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) @@ -283,8 +287,12 @@ int sd_radv_send(sd_radv *ra) { assert(min_timeout <= max_timeout * 3 / 4); timeout = min_timeout + random_u64_range(max_timeout - min_timeout); - log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.", - FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); + if (r >= 0) + log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.", + FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); + else + log_radv(ra, "Next Router Advertisement attempt in %s.", + FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); return event_reset_time( ra->event, &ra->timeout_event_source,