]> 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 13:14:34 +0000 (13:14 +0000)
Also removed the TIMEOUT states which makes things easier to read.

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

index d1adbbcc6ed877f87bf565d0c343b9879f7a8fd6..16b0b05a074eda2d009b8a739c9c3a7a5a9a955d 100644 (file)
@@ -174,6 +174,12 @@ static void dhcp6_failinform(void *);
 static int dhcp6_openudp(unsigned int, struct in6_addr *);
 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)
@@ -1551,11 +1557,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;
@@ -1607,7 +1614,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);
 
@@ -1618,36 +1640,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);
        }
 }
 
@@ -1681,9 +1703,7 @@ dhcp6_failinform(void *arg)
        dhcp6_fail(ifp);
 }
 
-#ifdef SMALL
-#define dhcp6_hasprefixdelegation(a)   (0)
-#else
+#ifndef SMALL
 static void
 dhcp6_failrebind(void *arg)
 {
@@ -1808,21 +1828,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)
 {
@@ -1832,24 +1837,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
@@ -2407,7 +2395,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);
@@ -2938,7 +2926,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;
@@ -2950,24 +2938,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))
@@ -2999,10 +2991,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;
 
@@ -3048,12 +3036,21 @@ 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. */
                struct timespec diff;
                uint32_t diffsec;
 
-               /* Reduce timers */
                timespecsub(&now, &state->acquired, &diff);
                diffsec = (uint32_t)diff.tv_sec;
                if (state->renew && state->renew != ND6_INFINITE_LIFETIME) {
@@ -3071,69 +3068,19 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
                if (state->expire && state->expire != ND6_INFINITE_LIFETIME) {
                        if (state->expire > diffsec)
                                state->expire -= diffsec;
-                       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,
@@ -3146,12 +3093,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,
                            (time_t)state->expire, dhcp6_startexpire, ifp);
-               else if (timed_out)
-                       eloop_timeout_add_sec(ifp->ctx->eloop,
-                           (time_t)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",
@@ -3170,7 +3115,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);
@@ -3457,13 +3402,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;
@@ -3476,13 +3419,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 ce6ad8d573b9cc9490722a5ee4c657702d51fe17..6df41c34a54405e401f17df2551ff20fd8ec7beb 100644 (file)
@@ -167,8 +167,6 @@ enum DH6S {
        DH6S_RENEW_REQUESTED,
        DH6S_PROBE,
        DH6S_DELEGATED,
-       DH6S_TIMEDOUT,
-       DH6S_ITIMEDOUT,
        DH6S_RELEASE,
        DH6S_RELEASED
 };
index 9092a6c9519a17ab644511e9cdf26c68e12cb0c6..e09c33768e0ae5c9700c30d5696a055da0ed61f7 100644 (file)
@@ -1539,9 +1539,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));