]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
DHCP6: Clean up old lease when we fail to confirm/rebind, etc
authorRoy Marples <roy@marples.name>
Thu, 6 Feb 2020 12:58:43 +0000 (12:58 +0000)
committerRoy Marples <roy@marples.name>
Thu, 6 Feb 2020 12:58:43 +0000 (12:58 +0000)
Also removed the TIMEOUT states which makes things easier to read.

src/dhcp6.c
src/dhcp6.h
src/ipv6.c

index 7012c16468ad4441036d1c3a9ad72fc96e9bfc91..4fb148ada8f221cb61dbd760fb4546f30ba5411d 100644 (file)
@@ -174,6 +174,12 @@ static void dhcp6_bind(struct interface *, const char *, const char *);
 static void dhcp6_failinform(void *);
 static void dhcp6_recvaddr(void *);
 
+#ifdef SMALL
+#define dhcp6_hasprefixdelegation(a)   (0)
+#else
+static int dhcp6_hasprefixdelegation(struct interface *);
+#endif
+
 void
 dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
     const struct dhcp_opt *opts, size_t opts_len)
@@ -1580,11 +1586,12 @@ dhcp6_startdiscover(void *arg)
        struct dhcp6_state *state;
 
        ifp = arg;
+       state = D6_STATE(ifp);
 #ifndef SMALL
-       dhcp6_delete_delegates(ifp);
+       if (state->reason == NULL || strcmp(state->reason, "TIMEOUT6") != 0)
+               dhcp6_delete_delegates(ifp);
 #endif
        loginfox("%s: soliciting a DHCPv6 lease", ifp->name);
-       state = D6_STATE(ifp);
        state->state = DH6S_DISCOVER;
        state->RTC = 0;
        state->IMD = SOL_MAX_DELAY;
@@ -1636,7 +1643,22 @@ dhcp6_startinform(void *arg)
 }
 
 static void
-dhcp6_fail(struct interface *ifp)
+dhcp6_leaseextend(struct interface *ifp)
+{
+       struct dhcp6_state *state = D6_STATE(ifp);
+       struct ipv6_addr *ia;
+
+       logwarnx("%s: extending DHCPv6 lease", ifp->name);
+       TAILQ_FOREACH(ia, &state->addrs, next) {
+               ia->flags |= IPV6_AF_EXTENDED;
+               /* Set infinite lifetimes. */
+               ia->prefix_pltime = ND6_INFINITE_LIFETIME;
+               ia->prefix_vltime = ND6_INFINITE_LIFETIME;
+       }
+}
+
+static void
+dhcp6_fail(struct interface* ifp)
 {
        struct dhcp6_state *state = D6_STATE(ifp);
 
@@ -1647,36 +1669,36 @@ dhcp6_fail(struct interface *ifp)
         * mobile clients.
         * dhcpcd also has LASTLEASE_EXTEND to extend this lease past it's
         * expiry, but this is strictly not RFC compliant in any way or form. */
-       if (state->new == NULL ||
-           !(ifp->options->options & DHCPCD_LASTLEASE))
+       if (state->new != NULL &&
+           ifp->options->options & DHCPCD_LASTLEASE_EXTEND)
        {
+               dhcp6_leaseextend(ifp);
+               dhcp6_bind(ifp, NULL, NULL);
+       } else {
+               dhcp6_freedrop_addrs(ifp, 1, NULL);
 #ifndef SMALL
                dhcp6_delete_delegates(ifp);
 #endif
-               if (state->state != DH6S_INFORM)
-                       dhcp6_startdiscover(ifp);
-               return;
-       }
-
-       switch (state->state) {
-       case DH6S_INFORM:
-       case DH6S_INFORMED:
-               state->state = DH6S_ITIMEDOUT;
-               break;
-       default:
-               state->state = DH6S_TIMEDOUT;
-               break;
+               free(state->old);
+               state->old = state->new;
+               state->old_len = state->new_len;
+               state->new = NULL;
+               state->new_len = 0;
+               if (state->old != NULL)
+                       script_runreason(ifp, "EXPIRE6");
+               unlink(state->leasefile);
        }
 
-       dhcp6_bind(ifp, NULL, NULL);
-
-       switch (state->state) {
-       case DH6S_BOUND:
-       case DH6S_INFORMED:
-               break;
-       default:
+       if (ifp->options->options & DHCPCD_IA_FORCED ||
+           ipv6nd_hasradhcp(ifp, true))
                dhcp6_startdiscover(ifp);
-               break;
+       else if (ifp->options->options & DHCPCD_INFORM6 ||
+           ipv6nd_hasradhcp(ifp, false))
+               dhcp6_startinform(ifp);
+       else {
+               logwarnx("%s: no advertising IPv6 router wants DHCP",ifp->name);
+               state->state = DH6S_INIT;
+               eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
        }
 }
 
@@ -1710,9 +1732,7 @@ dhcp6_failinform(void *arg)
        dhcp6_fail(ifp);
 }
 
-#ifdef SMALL
-#define dhcp6_hasprefixdelegation(a)   (0)
-#else
+#ifndef SMALL
 static void
 dhcp6_failrebind(void *arg)
 {
@@ -1837,21 +1857,6 @@ dhcp6_startconfirm(struct interface *ifp)
            CNF_MAX_RD, dhcp6_failconfirm, ifp);
 }
 
-static void
-dhcp6_leaseextend(struct interface *ifp)
-{
-       struct dhcp6_state *state = D6_STATE(ifp);
-       struct ipv6_addr *ia;
-
-       logwarnx("%s: extending DHCPv6 lease", ifp->name);
-       TAILQ_FOREACH(ia, &state->addrs, next) {
-               ia->flags |= IPV6_AF_EXTENDED;
-               /* Set infinite lifetimes. */
-               ia->prefix_pltime = ND6_INFINITE_LIFETIME;
-               ia->prefix_vltime = ND6_INFINITE_LIFETIME;
-       }
-}
-
 static void
 dhcp6_startexpire(void *arg)
 {
@@ -1861,24 +1866,7 @@ dhcp6_startexpire(void *arg)
        eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp);
 
        logerrx("%s: DHCPv6 lease expired", ifp->name);
-       if (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {
-               struct dhcp6_state *state = D6_STATE(ifp);
-
-               dhcp6_leaseextend(ifp);
-               ipv6_addaddrs(&state->addrs);
-       } else {
-               dhcp6_freedrop_addrs(ifp, 1, NULL);
-#ifndef SMALL
-               dhcp6_delete_delegates(ifp);
-#endif
-               script_runreason(ifp, "EXPIRE6");
-       }
-       if (!(ifp->options->options & DHCPCD_IPV6RS) ||
-           ipv6nd_hasradhcp(ifp) ||
-           dhcp6_hasprefixdelegation(ifp))
-               dhcp6_startdiscover(ifp);
-       else
-               logwarnx("%s: no advertising IPv6 router wants DHCP",ifp->name);
+       dhcp6_fail(ifp);
 }
 
 static void
@@ -2436,7 +2424,7 @@ dhcp6_deprecateaddrs(struct ipv6_addrhead *addrs)
                if (ia->flags & IPV6_AF_REQUEST) {
                        ia->prefix_vltime = ia->prefix_pltime = 0;
                        eloop_q_timeout_delete(ia->iface->ctx->eloop,
-                           0, NULL, ia);
+                           ELOOP_QUEUE_ALL, NULL, ia);
                        continue;
                }
                TAILQ_REMOVE(addrs, ia, next);
@@ -2969,7 +2957,7 @@ static void
 dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
 {
        struct dhcp6_state *state = D6_STATE(ifp);
-       bool has_new = false;
+       bool timedout = (op == NULL), has_new = false, confirmed;
        struct ipv6_addr *ia;
        logfunc_t *lognewinfo;
        struct timespec now;
@@ -2981,24 +2969,28 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
                }
        }
        lognewinfo = has_new ? loginfox : logdebugx;
-       if (op != NULL)
+       if (!timedout) {
                lognewinfo("%s: %s received from %s", ifp->name, op, sfrom);
+               /* If we delegated from an unconfirmed lease we MUST drop
+                * them now. Hopefully we have new delegations. */
+               if (state->reason != NULL &&
+                   strcmp(state->reason, "TIMEOUT6") == 0)
+                       dhcp6_delete_delegates(ifp);
+               state->reason = NULL;
+       } else
+               state->reason = "TIMEOUT6";
+
+       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+       clock_gettime(CLOCK_MONOTONIC, &now);
 
-       state->reason = NULL;
-       if (state->state != DH6S_ITIMEDOUT)
-               eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
        switch(state->state) {
        case DH6S_INFORM:
-               if (state->reason == NULL)
-                       state->reason = "INFORM6";
-               /* FALLTHROUGH */
-       case DH6S_ITIMEDOUT:
        {
                struct dhcp6_option *o;
                uint16_t ol;
 
                if (state->reason == NULL)
-                       state->reason = "ITIMEDOUT";
+                       state->reason = "INFORM6";
                o = dhcp6_findmoption(state->new, state->new_len,
                                      D6_OPTION_INFO_REFRESH_TIME, &ol);
                if (o == NULL || ol != sizeof(uint32_t))
@@ -3030,10 +3022,6 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
        case DH6S_CONFIRM:
                if (state->reason == NULL)
                        state->reason = "REBOOT6";
-               /* FALLTHROUGH */
-       case DH6S_TIMEDOUT:
-               if (state->reason == NULL)
-                       state->reason = "TIMEOUT6";
                if (state->renew != 0) {
                        bool all_expired = true;
 
@@ -3079,11 +3067,20 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
                break;
        }
 
-       clock_gettime(CLOCK_MONOTONIC, &now);
-       if (state->state == DH6S_TIMEDOUT || state->state == DH6S_ITIMEDOUT) {
+       if (state->state != DH6S_CONFIRM && !timedout) {
+               state->acquired = now;
+               free(state->old);
+               state->old = state->new;
+               state->old_len = state->new_len;
+               state->new = state->recv;
+               state->new_len = state->recv_len;
+               state->recv = NULL;
+               state->recv_len = 0;
+               confirmed = false;
+       } else {
+               /* Reduce timers based on when we got the lease. */
                uint32_t elapsed;
 
-               /* Reduce timers */
                elapsed = (uint32_t)eloop_timespec_diff(&now,
                    &state->acquired, NULL);
                if (state->renew && state->renew != ND6_INFINITE_LIFETIME) {
@@ -3101,69 +3098,19 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
                if (state->expire && state->expire != ND6_INFINITE_LIFETIME) {
                        if (state->expire > elapsed)
                                state->expire -= elapsed;
-                       else {
-                               if (!(ifp->options->options &
-                                   DHCPCD_LASTLEASE_EXTEND))
-                                       return;
-                               state->expire = ND6_INFINITE_LIFETIME;
-                       }
-               }
-               if (state->expire == ND6_INFINITE_LIFETIME &&
-                   ifp->options->options & DHCPCD_LASTLEASE_EXTEND)
-                       dhcp6_leaseextend(ifp);
-
-               /* Restart rebind or renew phases in a second. */
-               if (state->expire != ND6_INFINITE_LIFETIME) {
-                       if (state->rebind == 0 &&
-                           state->rebind != ND6_INFINITE_LIFETIME)
-                               state->rebind = 1;
-                       else if (state->renew == 0 &&
-                           state->renew != ND6_INFINITE_LIFETIME)
-                               state->renew = 1;
+                       else
+                               state->expire = 0;
                }
-       } else
-               state->acquired = now;
-
-       switch (state->state) {
-       case DH6S_CONFIRM:
-       case DH6S_TIMEDOUT:
-       case DH6S_ITIMEDOUT:
-               break;
-       default:
-               free(state->old);
-               state->old = state->new;
-               state->old_len = state->new_len;
-               state->new = state->recv;
-               state->new_len = state->recv_len;
-               state->recv = NULL;
-               state->recv_len = 0;
-               break;
+               confirmed = true;
        }
 
        if (ifp->ctx->options & DHCPCD_TEST)
                script_runreason(ifp, "TEST");
        else {
-               bool timed_out;
-
-               switch(state->state) {
-               case DH6S_TIMEDOUT:
-               case DH6S_ITIMEDOUT:
-                       timed_out = true;
-                       break;
-               default:
-                       timed_out = false;
-                       break;
-               }
-
-               switch(state->state) {
-               case DH6S_INFORM:
-               case DH6S_ITIMEDOUT:
+               if (state->state == DH6S_INFORM)
                        state->state = DH6S_INFORMED;
-                       break;
-               default:
+               else
                        state->state = DH6S_BOUND;
-                       break;
-               }
 
                if (state->renew && state->renew != ND6_INFINITE_LIFETIME)
                        eloop_timeout_add_sec(ifp->ctx->eloop,
@@ -3176,12 +3123,10 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
                if (state->expire != ND6_INFINITE_LIFETIME)
                        eloop_timeout_add_sec(ifp->ctx->eloop,
                            state->expire, dhcp6_startexpire, ifp);
-               else if (timed_out)
-                       eloop_timeout_add_sec(ifp->ctx->eloop,
-                           state->expire, dhcp6_startdiscover, ifp);
 
                ipv6_addaddrs(&state->addrs);
-               dhcp6_deprecateaddrs(&state->addrs);
+               if (!timedout)
+                       dhcp6_deprecateaddrs(&state->addrs);
 
                if (state->state == DH6S_INFORMED)
                        lognewinfo("%s: refresh in %"PRIu32" seconds",
@@ -3200,7 +3145,7 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
                        lognewinfo("%s: expire in %"PRIu32" seconds",
                            ifp->name, state->expire);
                rt_build(ifp->ctx, AF_INET6);
-               if (!timed_out)
+               if (!confirmed && !timedout)
                        dhcp6_writelease(ifp);
 #ifndef SMALL
                dhcp6_delegate_prefix(ifp);
@@ -3487,13 +3432,11 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
        memcpy(state->recv, r, len);
        state->recv_len = len;
 
-       switch (r->type) {
-       case DHCP6_ADVERTISE:
-       {
+       if (r->type == DHCP6_ADVERTISE) {
                struct ipv6_addr *ia;
 
                if (state->state == DH6S_REQUEST) /* rapid commit */
-                       break;
+                       goto bind;
                TAILQ_FOREACH(ia, &state->addrs, next) {
                        if (!(ia->flags & (IPV6_AF_STALE | IPV6_AF_REQUEST)))
                                break;
@@ -3506,13 +3449,11 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
                else
                        loginfox("%s: ADV %s from %s",
                            ifp->name, ia->saddr, sfrom);
-               if (ifp->ctx->options & DHCPCD_TEST)
-                       break;
                dhcp6_startrequest(ifp);
                return;
        }
-       }
 
+bind:
        dhcp6_bind(ifp, op, sfrom);
 }
 
index af66468befca4d652cea8303a782459645e63dfa..ac2c47a0e0f226a4f901ae7c9836a52fc16e281f 100644 (file)
@@ -169,8 +169,6 @@ enum DH6S {
        DH6S_RENEW_REQUESTED,
        DH6S_PROBE,
        DH6S_DELEGATED,
-       DH6S_TIMEDOUT,
-       DH6S_ITIMEDOUT,
        DH6S_RELEASE,
        DH6S_RELEASED
 };
index 216f0c703983df24b5ad486e40feb905d0111c81..cfeeda0bdfbd9df345eb5d00081fd413106dc7b0 100644 (file)
@@ -1528,9 +1528,10 @@ ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,
        /* If adding a new DHCP / RA derived address, check current flags
         * from an existing address. */
        ia = ipv6_iffindaddr(ifp, addr, 0);
-       if (ia != NULL)
+       if (ia != NULL) {
                addr_flags = ia->addr_flags;
-       else
+               flags |= IPV6_AF_ADDED;
+       } else
                addr_flags = IN6_IFF_TENTATIVE;
 
        ia = calloc(1, sizeof(*ia));