]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
DHCP6: Calulate ReTransmission using milliseconds
authorRoy Marples <roy@marples.name>
Tue, 7 Jan 2020 22:15:09 +0000 (22:15 +0000)
committerRoy Marples <roy@marples.name>
Tue, 7 Jan 2020 22:15:09 +0000 (22:15 +0000)
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.

src/common.h
src/dhcp6.c
src/dhcp6.h
src/eloop.c
src/eloop.h

index 31cc8a36536455344129ff3efd7a4cbc61c7e7fa..ede85887c703d7f267f4303f80739899ccf8f425 100644 (file)
@@ -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
 
index 862c7edd99938e872c55c6da48a092357eca1c82..93d9cc4a6f20cfec4c7aad875b19c7edcbadfbaa 100644 (file)
@@ -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);
index d6e5444799c806781f70cb7a00dfe3f58c7ddca1..af66468befca4d652cea8303a782459645e63dfa 100644 (file)
 #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;
index dfa9438c47ea46f1e54c8004fb252ddc730abb66..e072fc24c738f1a2f8c4a6f50a9773842154c6ff 100644 (file)
@@ -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) {
index 696ad32ffa8705fb9df714e3ee7c5625eca75de4..4508fe91aa50eed2c79d1defa998933a632db02c 100644 (file)
@@ -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,