mirror of
https://github.com/systemd/systemd.git
synced 2025-02-09 13:57:42 +03:00
Merge pull request #4512 from pfl/ndisc_exponential_backoff
Ndisc exponential backoff
This commit is contained in:
commit
4af273d149
@ -23,6 +23,10 @@
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
|
||||
#define NDISC_MAX_ROUTER_SOLICITATION_INTERVAL (3600U * USEC_PER_SEC)
|
||||
#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
|
||||
|
||||
struct sd_ndisc {
|
||||
unsigned n_ref;
|
||||
|
||||
@ -38,8 +42,9 @@ struct sd_ndisc {
|
||||
|
||||
sd_event_source *recv_event_source;
|
||||
sd_event_source *timeout_event_source;
|
||||
sd_event_source *timeout_no_ra;
|
||||
|
||||
unsigned nd_sent;
|
||||
usec_t retransmit_time;
|
||||
|
||||
sd_ndisc_callback_t callback;
|
||||
void *userdata;
|
||||
|
@ -28,12 +28,12 @@
|
||||
#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 +129,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,19 +265,52 @@ 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 = 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;
|
||||
|
||||
r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout-no-ra");
|
||||
|
||||
r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(r, "Error reenabling timer: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
|
||||
@ -285,23 +319,9 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_ndisc("Sent Router Solicitation");
|
||||
nd->nd_sent++;
|
||||
|
||||
assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
|
||||
|
||||
r = sd_event_source_set_time(nd->timeout_event_source, next_timeout);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(r, "Error updating timer: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(r, "Error reenabling timer: %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;
|
||||
|
||||
@ -310,6 +330,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 +358,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 +370,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 +398,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;
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "icmp6-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "strv.h"
|
||||
#include "ndisc-internal.h"
|
||||
|
||||
static struct ether_addr mac_addr = {
|
||||
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
|
||||
@ -35,6 +36,7 @@ static struct ether_addr mac_addr = {
|
||||
static bool verbose = false;
|
||||
static sd_event_source *test_hangcheck;
|
||||
static int test_fd[2];
|
||||
static sd_ndisc *test_timeout_nd;
|
||||
|
||||
typedef int (*send_ra_t)(uint8_t flags);
|
||||
static send_ra_t send_ra_function;
|
||||
@ -237,6 +239,9 @@ static int send_ra(uint8_t flags) {
|
||||
}
|
||||
|
||||
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
|
||||
if (!send_ra_function)
|
||||
return 0;
|
||||
|
||||
return send_ra_function(0);
|
||||
}
|
||||
|
||||
@ -320,6 +325,100 @@ static void test_rs(void) {
|
||||
sd_event_unref(e);
|
||||
}
|
||||
|
||||
static int test_timeout_value(uint8_t flags) {
|
||||
static int count = 0;
|
||||
static usec_t last = 0;
|
||||
sd_ndisc *nd = test_timeout_nd;
|
||||
usec_t min, max;
|
||||
char time_string_min[FORMAT_TIMESPAN_MAX];
|
||||
char time_string_nd[FORMAT_TIMESPAN_MAX];
|
||||
char time_string_max[FORMAT_TIMESPAN_MAX];
|
||||
|
||||
assert_se(nd);
|
||||
assert_se(nd->event);
|
||||
|
||||
if (++count >= 20)
|
||||
sd_event_exit(nd->event, 0);
|
||||
|
||||
if (last == 0) {
|
||||
/* initial RT = IRT + RAND*IRT */
|
||||
min = NDISC_ROUTER_SOLICITATION_INTERVAL -
|
||||
NDISC_ROUTER_SOLICITATION_INTERVAL / 10;
|
||||
max = NDISC_ROUTER_SOLICITATION_INTERVAL +
|
||||
NDISC_ROUTER_SOLICITATION_INTERVAL / 10;
|
||||
} else {
|
||||
/* next RT = 2*RTprev + RAND*RTprev */
|
||||
min = 2 * last - last / 10;
|
||||
max = 2 * last + last / 10;
|
||||
}
|
||||
|
||||
/* final RT > MRT */
|
||||
if (last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL) {
|
||||
min = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL -
|
||||
NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10;
|
||||
max = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL +
|
||||
NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10;
|
||||
}
|
||||
|
||||
format_timespan(time_string_min, FORMAT_TIMESPAN_MAX,
|
||||
min, USEC_PER_MSEC);
|
||||
format_timespan(time_string_nd, FORMAT_TIMESPAN_MAX,
|
||||
nd->retransmit_time, USEC_PER_MSEC);
|
||||
format_timespan(time_string_max, FORMAT_TIMESPAN_MAX,
|
||||
max, USEC_PER_MSEC);
|
||||
|
||||
log_info("backoff timeout interval %2d %s%s <= %s <= %s",
|
||||
count,
|
||||
(last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL)? "(max) ": "",
|
||||
time_string_min, time_string_nd, time_string_max);
|
||||
|
||||
assert_se(min <= nd->retransmit_time);
|
||||
assert_se(max >= nd->retransmit_time);
|
||||
|
||||
last = nd->retransmit_time;
|
||||
|
||||
assert_se(sd_event_source_set_time(nd->timeout_event_source, 0) >= 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_timeout(void) {
|
||||
sd_event *e;
|
||||
sd_ndisc *nd;
|
||||
usec_t time_now = now(clock_boottime_or_monotonic());
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
|
||||
send_ra_function = test_timeout_value;
|
||||
|
||||
assert_se(sd_event_new(&e) >= 0);
|
||||
|
||||
assert_se(sd_ndisc_new(&nd) >= 0);
|
||||
assert_se(nd);
|
||||
|
||||
test_timeout_nd = nd;
|
||||
|
||||
assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
|
||||
|
||||
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
|
||||
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
|
||||
|
||||
assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
|
||||
time_now + 2U * USEC_PER_SEC, 0,
|
||||
test_rs_hangcheck, NULL) >= 0);
|
||||
|
||||
assert_se(sd_ndisc_start(nd) >= 0);
|
||||
|
||||
sd_event_loop(e);
|
||||
|
||||
test_hangcheck = sd_event_source_unref(test_hangcheck);
|
||||
|
||||
nd = sd_ndisc_unref(nd);
|
||||
|
||||
sd_event_unref(e);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
@ -327,6 +426,7 @@ int main(int argc, char *argv[]) {
|
||||
log_open();
|
||||
|
||||
test_rs();
|
||||
test_timeout();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user