From: Roy Marples Date: Tue, 7 Jan 2020 22:15:09 +0000 (+0000) Subject: DHCP6: Calulate ReTransmission using milliseconds X-Git-Tag: v9.0.0~154 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5b5763e6a858c9b0c65ef997e893d38de579a5d3;p=thirdparty%2Fdhcpcd.git DHCP6: Calulate ReTransmission using milliseconds This is the exact formula in RFC8415 Section 15, the prior one was not so exact. This makes the code a lot simpler and removes the need for complicated timespec handling. --- diff --git a/src/common.h b/src/common.h index 31cc8a36..ede85887 100644 --- a/src/common.h +++ b/src/common.h @@ -55,7 +55,7 @@ #define USEC_PER_NSEC 1000L #define NSEC_PER_SEC 1000000000L #define NSEC_PER_MSEC 1000000L -#define MSEC_PER_SEC 1000L +#define MSEC_PER_SEC 1000 #define CSEC_PER_SEC 100L #define NSEC_PER_CSEC 10000000L diff --git a/src/dhcp6.c b/src/dhcp6.c index 862c7edd..93d9cc4a 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -1162,10 +1162,7 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *)) .sin6_family = AF_INET6, .sin6_port = htons(DHCP6_SERVER_PORT), }; - struct timespec RTprev; - double rnd; - time_t ms; - uint8_t neg; + unsigned int RT; const char *broad_uni; const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT; struct ipv6_addr *lla; @@ -1214,60 +1211,33 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *)) !(ifp->options->options & DHCPCD_INITIAL_DELAY)) state->IMD = 0; if (state->IMD) { + state->RT = state->IMD * MSEC_PER_SEC; /* Some buggy PPP servers close the link too early * after sending an invalid status in their reply * which means this host won't see it. * 1 second grace seems to be the sweet spot. */ if (ifp->flags & IFF_POINTOPOINT) - state->RT.tv_sec = 1; - else - state->RT.tv_sec = 0; - state->RT.tv_nsec = (suseconds_t)arc4random_uniform( - (uint32_t)(state->IMD * NSEC_PER_SEC)); - timespecnorm(&state->RT); + state->RT += MSEC_PER_SEC; broad_uni = "delaying"; - goto logsend; - } - if (state->RTC == 0) { - RTprev.tv_sec = state->IRT; - RTprev.tv_nsec = 0; - state->RT.tv_sec = RTprev.tv_sec; - state->RT.tv_nsec = 0; - } else { - RTprev = state->RT; - timespecadd(&state->RT, &state->RT, &state->RT); - } + } else if (state->RTC == 0) + state->RT = state->IRT * MSEC_PER_SEC; - rnd = DHCP6_RAND_MIN; - rnd += (suseconds_t)arc4random_uniform( - DHCP6_RAND_MAX - DHCP6_RAND_MIN); - rnd /= MSEC_PER_SEC; - neg = (rnd < 0.0); - if (neg) - rnd = -rnd; - ts_to_ms(ms, &RTprev); - ms = (time_t)((double)ms * rnd); - ms_to_ts(&RTprev, ms); - if (neg) - timespecsub(&state->RT, &RTprev, &state->RT); - else - timespecadd(&state->RT, &RTprev, &state->RT); - - if (state->MRT != 0 && state->RT.tv_sec > state->MRT) { - RTprev.tv_sec = state->MRT; - RTprev.tv_nsec = 0; - state->RT.tv_sec = state->MRT; - state->RT.tv_nsec = 0; - ts_to_ms(ms, &RTprev); - ms = (time_t)((double)ms * rnd); - ms_to_ts(&RTprev, ms); - if (neg) - timespecsub(&state->RT, &RTprev, &state->RT); - else - timespecadd(&state->RT, &RTprev, &state->RT); + if (state->MRT != 0) { + unsigned int mrt = state->MRT * MSEC_PER_SEC; + + if (state->RT > mrt) + state->RT = mrt; } -logsend: + /* Add -.1 to .1 * RT randomness as per RFC8415 section 15 */ + uint32_t lru = arc4random_uniform( + state->RTC == 0 ? DHCP6_RAND_MAX + : DHCP6_RAND_MAX - DHCP6_RAND_MIN); + int lr = (int)lru - (state->RTC == 0 ? 0 : DHCP6_RAND_MAX); + RT = state->RT + + (unsigned int)((float)state->RT + * ((float)lr / DHCP6_RAND_DIV)); + if (ifp->carrier > LINK_DOWN) logdebugx("%s: %s %s (xid 0x%02x%02x%02x)," " next in %0.1f seconds", @@ -1277,17 +1247,12 @@ logsend: state->send->xid[0], state->send->xid[1], state->send->xid[2], - timespec_to_double(&state->RT)); - - /* This sometimes happens when we delegate to this interface - * AND run DHCPv6 on it normally. */ - assert(timespec_to_double(&state->RT) != 0); + (float)RT / MSEC_PER_SEC); /* Wait the initial delay */ if (state->IMD != 0) { state->IMD = 0; - eloop_timeout_add_tv(ctx->eloop, - &state->RT, callback, ifp); + eloop_timeout_add_msec(ctx->eloop, RT, callback, ifp); return 0; } } @@ -1376,14 +1341,17 @@ logsend: #ifdef PRIVSEP sent: #endif + state->RT = RT * 2; + if (state->RT < RT) /* Check overflow */ + state->RT = RT; state->RTC++; if (callback) { if (state->MRC == 0 || state->RTC < state->MRC) - eloop_timeout_add_tv(ctx->eloop, - &state->RT, callback, ifp); + eloop_timeout_add_msec(ctx->eloop, + RT, callback, ifp); else if (state->MRC != 0 && state->MRCcallback) - eloop_timeout_add_tv(ctx->eloop, - &state->RT, state->MRCcallback, ifp); + eloop_timeout_add_msec(ctx->eloop, + RT, state->MRCcallback, ifp); else logwarnx("%s: sent %d times with no reply", ifp->name, state->RTC); diff --git a/src/dhcp6.h b/src/dhcp6.h index d6e54447..af66468b 100644 --- a/src/dhcp6.h +++ b/src/dhcp6.h @@ -151,8 +151,10 @@ #define IRT_DEFAULT 86400 #define IRT_MINIMUM 600 -#define DHCP6_RAND_MIN -100 -#define DHCP6_RAND_MAX 100 +/* These should give -.1 to .1 randomness */ +#define DHCP6_RAND_MIN -100 +#define DHCP6_RAND_MAX 100 +#define DHCP6_RAND_DIV 1000.0f enum DH6S { DH6S_INIT, @@ -177,16 +179,18 @@ struct dhcp6_state { enum DH6S state; struct timespec started; - /* Message retransmission timings */ - struct timespec RT; + /* Message retransmission timings in seconds */ unsigned int IMD; unsigned int RTC; - time_t IRT; + unsigned int IRT; unsigned int MRC; - time_t MRT; + unsigned int MRT; void (*MRCcallback)(void *); - time_t sol_max_rt; - time_t inf_max_rt; + unsigned int sol_max_rt; + unsigned int inf_max_rt; + unsigned int RT; /* retransmission timer in milliseconds + * maximal RT is 1 day + RAND, + * so should be enough */ struct dhcp6_message *send; size_t send_len; diff --git a/src/eloop.c b/src/eloop.c index dfa9438c..e072fc24 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -96,7 +96,7 @@ #endif #ifndef MSEC_PER_SEC -#define MSEC_PER_SEC 1000L +#define MSEC_PER_SEC 1000 #define NSEC_PER_MSEC 1000000L #define NSEC_PER_SEC 1000000000U #endif @@ -671,10 +671,10 @@ eloop_q_timeout_add_sec(struct eloop *eloop, int queue, unsigned int seconds, } int -eloop_q_timeout_add_msec(struct eloop *eloop, int queue, long when, +eloop_q_timeout_add_msec(struct eloop *eloop, int queue, unsigned long when, void (*callback)(void *), void *arg) { - long seconds, nseconds; + unsigned long seconds, nseconds; seconds = when / MSEC_PER_SEC; if (seconds > UINT_MAX) { diff --git a/src/eloop.h b/src/eloop.h index 696ad32f..4508fe91 100644 --- a/src/eloop.h +++ b/src/eloop.h @@ -98,7 +98,7 @@ int eloop_q_timeout_add_tv(struct eloop *, int, int eloop_q_timeout_add_sec(struct eloop *, int, unsigned int, void (*)(void *), void *); int eloop_q_timeout_add_msec(struct eloop *, int, - long, void (*)(void *), void *); + unsigned long, void (*)(void *), void *); int eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *); int eloop_signal_set_cb(struct eloop *, const int *, size_t,