]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-radv: do not stop on transient send errors
authorr-vdp <ramses@well-founded.dev>
Mon, 13 Apr 2026 12:41:11 +0000 (14:41 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 14 May 2026 18:34:52 +0000 (03:34 +0900)
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

index b9c59d01643af8730be9f903ad30ba0235ce3d39..21a80da38ee9095fa0b2da09b45b7b418f484a96 100644 (file)
@@ -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,