]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd-network/sd-ndisc.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd-network / sd-ndisc.c
index 0437e0b0b7644f0a67ebb35a2af2248f0ec4bd11..b5c6d6e84d47994e9d3f54750d6078f3662d61d2 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 #include "in-addr-util.h"
 #include "ndisc-internal.h"
 #include "ndisc-router.h"
+#include "random-util.h"
 #include "socket-util.h"
 #include "string-util.h"
 #include "util.h"
 
-#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
-#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
+#define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
 
 static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) {
         assert(ndisc);
@@ -129,9 +130,10 @@ static int ndisc_reset(sd_ndisc *nd) {
         assert(nd);
 
         nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+        nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
+        nd->retransmit_time = 0;
         nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
         nd->fd = safe_close(nd->fd);
-        nd->nd_sent = 0;
 
         return 0;
 }
@@ -264,45 +266,64 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
         return ndisc_handle_datagram(nd, rt);
 }
 
+static usec_t ndisc_timeout_compute_random(usec_t val) {
+        /* compute a time that is random within ±10% of the given value */
+        return val - val / 10 +
+                (random_u64() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
+}
+
 static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_ndisc *nd = userdata;
-        usec_t time_now, next_timeout;
+        usec_t time_now;
         int r;
+        char time_string[FORMAT_TIMESPAN_MAX];
 
         assert(s);
         assert(nd);
         assert(nd->event);
 
-        if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
-                nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
-                ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
-                return 0;
+        assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+        nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+
+        if (!nd->retransmit_time)
+                nd->retransmit_time = ndisc_timeout_compute_random(NDISC_ROUTER_SOLICITATION_INTERVAL);
+        else {
+                if (nd->retransmit_time > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 2)
+                        nd->retransmit_time = ndisc_timeout_compute_random(NDISC_MAX_ROUTER_SOLICITATION_INTERVAL);
+                else
+                        nd->retransmit_time += ndisc_timeout_compute_random(nd->retransmit_time);
         }
 
-        r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
-        if (r < 0) {
-                log_ndisc_errno(r, "Error sending Router Solicitation: %m");
+        r = sd_event_add_time(nd->event, &nd->timeout_event_source,
+                              clock_boottime_or_monotonic(),
+                              time_now + nd->retransmit_time,
+                              10 * USEC_PER_MSEC, ndisc_timeout, nd);
+        if (r < 0)
                 goto fail;
-        }
 
-        log_ndisc("Sent Router Solicitation");
-        nd->nd_sent++;
+        r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
+        if (r < 0)
+                goto fail;
 
-        assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-        next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
+        (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout-no-ra");
 
-        r = sd_event_source_set_time(nd->timeout_event_source, next_timeout);
+        r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
         if (r < 0) {
-                log_ndisc_errno(r, "Error updating timer: %m");
+                log_ndisc_errno(r, "Error reenabling timer: %m");
                 goto fail;
         }
 
-        r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
+        r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
         if (r < 0) {
-                log_ndisc_errno(r, "Error reenabling timer: %m");
+                log_ndisc_errno(r, "Error sending Router Solicitation: %m");
                 goto fail;
         }
 
+        log_ndisc("Sent Router Solicitation, next solicitation in %s",
+                  format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                                  nd->retransmit_time, USEC_PER_SEC));
+
         return 0;
 
 fail:
@@ -310,6 +331,20 @@ fail:
         return 0;
 }
 
+static int ndisc_timeout_no_ra(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_ndisc *nd = userdata;
+
+        assert(s);
+        assert(nd);
+
+        log_ndisc("No RA received before link confirmation timeout");
+
+        nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
+        ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
+
+        return 0;
+}
+
 _public_ int sd_ndisc_stop(sd_ndisc *nd) {
         assert_return(nd, -EINVAL);
 
@@ -324,6 +359,7 @@ _public_ int sd_ndisc_stop(sd_ndisc *nd) {
 
 _public_ int sd_ndisc_start(sd_ndisc *nd) {
         int r;
+        usec_t time_now;
 
         assert_return(nd, -EINVAL);
         assert_return(nd->event, -EINVAL);
@@ -335,6 +371,10 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) {
         assert(!nd->recv_event_source);
         assert(!nd->timeout_event_source);
 
+        r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                goto fail;
+
         nd->fd = icmp6_bind_router_solicitation(nd->ifindex);
         if (nd->fd < 0)
                 return nd->fd;
@@ -359,6 +399,19 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) {
 
         (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
 
+        r = sd_event_add_time(nd->event, &nd->timeout_no_ra,
+                              clock_boottime_or_monotonic(),
+                              time_now + NDISC_TIMEOUT_NO_RA_USEC,
+                              10 * USEC_PER_MSEC, ndisc_timeout_no_ra, nd);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_priority(nd->timeout_no_ra, nd->event_priority);
+        if (r < 0)
+                goto fail;
+
+        (void) sd_event_source_set_description(nd->timeout_no_ra, "ndisc-timeout-no-ra");
+
         log_ndisc("Started IPv6 Router Solicitation client");
         return 1;