]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
dhcp6: implement lastlease and lastleaseextend
authorRoy Marples <roy@marples.name>
Tue, 25 Jul 2017 10:00:44 +0000 (11:00 +0100)
committerRoy Marples <roy@marples.name>
Tue, 25 Jul 2017 17:54:46 +0000 (18:54 +0100)
Summary:
This has resulted in a fair churn of code, but in summary:
  *  TIMEDOUT state introduced
  *  lease binding split out from handledata
  *  generic fail function introduced to attempt rebind
     of lastlease
  *  extended addresses are remembered and purged when a
     lease is re-negotiated
  *  defaults of 0 are no longer hard coded for most timers

Fixes T128.

Test Plan:
Obtain a dhcp6 lease, kill dhcpcd and the dhcp6 server.
run with `--lastlease` and check it's applied correctly.
Also, observe timers to ensure it renews, rebinds and expires.
Do same, but now with `--lastleaseextend as well`.
Observe same timers, but it should not expire but become an
infinite lease until a new one appears.

Reviewers: shahid

Reviewed By: shahid

Maniphest Tasks: T128

Differential Revision: https://dev.marples.name/D125

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

index bd17325cd9f9e1c930bc901e479a372145cbe211..7f75c351a368e1315f40162ede7cc1869e2f59c6 100644 (file)
@@ -163,6 +163,9 @@ static const char * const dhcp6_statuses[] = {
        "No Prefix Available"
 };
 
+static void dhcp6_bind(struct interface *, const char *);
+static void dhcp6_failinform(void *);
+
 void
 dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
     const struct dhcp_opt *opts, size_t opts_len)
@@ -677,6 +680,8 @@ dhcp6_makemessage(struct interface *ifp)
                        ml = state->new_len;
                }
                TAILQ_FOREACH(ap, &state->addrs, next) {
+                       if (ap->flags & IPV6_AF_STALE)
+                               continue;
                        if (ap->prefix_vltime == 0 &&
                            !(ap->flags & IPV6_AF_REQUEST))
                                continue;
@@ -816,6 +821,8 @@ dhcp6_makemessage(struct interface *ifp)
                ia_na.t2 = 0;
                COPYIN(ifo->ia[l].ia_type, &ia_na, sizeof(ia_na));
                TAILQ_FOREACH(ap, &state->addrs, next) {
+                       if (ap->flags & IPV6_AF_STALE)
+                               continue;
                        if (ap->prefix_vltime == 0 &&
                            !(ap->flags & IPV6_AF_REQUEST))
                                continue;
@@ -1298,6 +1305,7 @@ dhcp6_startrenew(void *arg)
 
        state->state = DH6S_RENEW;
        state->RTC = 0;
+       state->IMD = REN_MAX_DELAY;
        state->IRT = REN_TIMEOUT;
        state->MRT = REN_MAX_RT;
        state->MRC = 0;
@@ -1431,24 +1439,96 @@ dhcp6_startdiscover(void *arg)
        state->IMD = SOL_MAX_DELAY;
        state->IRT = SOL_TIMEOUT;
        state->MRT = state->sol_max_rt;
-       state->MRC = 0;
+       state->MRC = SOL_MAX_RC;
 
        eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
        free(state->new);
        state->new = NULL;
        state->new_len = 0;
 
-       dhcp6_freedrop_addrs(ifp, 0, NULL);
-       unlink(state->leasefile);
-
-       dhcp6_addrequestedaddrs(ifp);
-
        if (dhcp6_makemessage(ifp) == -1)
                logerr("%s: %s", __func__, ifp->name);
        else
                dhcp6_senddiscover(ifp);
 }
 
+static void
+dhcp6_startinform(void *arg)
+{
+       struct interface *ifp;
+       struct dhcp6_state *state;
+
+       ifp = arg;
+       state = D6_STATE(ifp);
+       if (state->new == NULL || ifp->options->options & DHCPCD_DEBUG)
+               loginfox("%s: requesting DHCPv6 information", ifp->name);
+       state->state = DH6S_INFORM;
+       state->RTC = 0;
+       state->IMD = INF_MAX_DELAY;
+       state->IRT = INF_TIMEOUT;
+       state->MRT = state->inf_max_rt;
+       state->MRC = 0;
+
+       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+       if (dhcp6_makemessage(ifp) == -1) {
+               logerr("%s: %s", __func__, ifp->name);
+               return;
+       }
+       dhcp6_sendinform(ifp);
+       /* RFC3315 18.1.2 says that if CONFIRM failed then the prior addresses
+        * SHOULD be used. The wording here is poor, because the addresses are
+        * merely one facet of the lease as a whole.
+        * This poor wording might explain the lack of similar text for INFORM
+        * in 18.1.5 because there are no addresses in the INFORM message. */
+       eloop_timeout_add_sec(ifp->ctx->eloop,
+           INF_MAX_RD, dhcp6_failinform, ifp);
+}
+
+static void
+dhcp6_fail(struct interface *ifp)
+{
+       struct dhcp6_state *state = D6_STATE(ifp);
+
+       /* RFC3315 18.1.2 says that prior addresses SHOULD be used on failure.
+        * RFC2131 3.2.3 says that MAY chose to use the prior address.
+        * Because dhcpcd was written first for RFC2131, we have the LASTLEASE
+        * option which defaults to off as that makes the most sense for
+        * 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))
+       {
+#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;
+       }
+
+       dhcp6_bind(ifp, NULL);
+
+       switch (state->state) {
+       case DH6S_BOUND:
+       case DH6S_INFORMED:
+               break;
+       default:
+               dhcp6_startdiscover(ifp);
+               break;
+       }
+}
+
 static void
 dhcp6_failconfirm(void *arg)
 {
@@ -1456,10 +1536,7 @@ dhcp6_failconfirm(void *arg)
 
        ifp = arg;
        logerrx("%s: failed to confirm prior address", ifp->name);
-       /* Section 18.1.2 says that we SHOULD use the last known
-        * IP address(s) and lifetimes if we didn't get a reply.
-        * I disagree with this. */
-       dhcp6_startdiscover(ifp);
+       dhcp6_fail(ifp);
 }
 
 static void
@@ -1469,11 +1546,17 @@ dhcp6_failrequest(void *arg)
 
        ifp = arg;
        logerrx("%s: failed to request address", ifp->name);
-       /* Section 18.1.1 says that client local policy dictates
-        * what happens if a REQUEST fails.
-        * Of the possible scenarios listed, moving back to the
-        * DISCOVER phase makes more sense for us. */
-       dhcp6_startdiscover(ifp);
+       dhcp6_fail(ifp);
+}
+
+static void
+dhcp6_failinform(void *arg)
+{
+       struct interface *ifp;
+
+       ifp = arg;
+       logerrx("%s: failed to request information", ifp->name);
+       dhcp6_fail(ifp);
 }
 
 #ifdef SMALL
@@ -1486,11 +1569,7 @@ dhcp6_failrebind(void *arg)
 
        ifp = arg;
        logerrx("%s: failed to rebind prior delegation", ifp->name);
-       dhcp6_delete_delegates(ifp);
-       /* Section 18.1.2 says that we SHOULD use the last known
-        * IP address(s) and lifetimes if we didn't get a reply.
-        * I disagree with this. */
-       dhcp6_startdiscover(ifp);
+       dhcp6_fail(ifp);
 }
 
 static int
@@ -1542,6 +1621,7 @@ dhcp6_startrebind(void *arg)
        } else
 #endif
        {
+               state->IMD = REB_MAX_DELAY;
                state->IRT = REB_TIMEOUT;
                state->MRT = REB_MAX_RT;
        }
@@ -1569,6 +1649,7 @@ dhcp6_startrequest(struct interface *ifp)
        state = D6_STATE(ifp);
        state->state = DH6S_REQUEST;
        state->RTC = 0;
+       state->IMD = 0;
        state->IRT = REQ_TIMEOUT;
        state->MRT = REQ_MAX_RT;
        state->MRC = REQ_MAX_RC;
@@ -1593,7 +1674,7 @@ dhcp6_startconfirm(struct interface *ifp)
        state->IMD = CNF_MAX_DELAY;
        state->IRT = CNF_TIMEOUT;
        state->MRT = CNF_MAX_RT;
-       state->MRC = 0;
+       state->MRC = CNF_MAX_RC;
 
        loginfox("%s: confirming prior DHCPv6 lease", ifp->name);
        if (dhcp6_makemessage(ifp) == -1) {
@@ -1606,26 +1687,18 @@ dhcp6_startconfirm(struct interface *ifp)
 }
 
 static void
-dhcp6_startinform(void *arg)
+dhcp6_leaseextend(struct interface *ifp)
 {
-       struct interface *ifp;
-       struct dhcp6_state *state;
-
-       ifp = arg;
-       state = D6_STATE(ifp);
-       if (state->new == NULL || ifp->options->options & DHCPCD_DEBUG)
-               loginfox("%s: requesting DHCPv6 information", ifp->name);
-       state->state = DH6S_INFORM;
-       state->RTC = 0;
-       state->IMD = INF_MAX_DELAY;
-       state->IRT = INF_TIMEOUT;
-       state->MRT = state->inf_max_rt;
-       state->MRC = 0;
+       struct dhcp6_state *state = D6_STATE(ifp);
+       struct ipv6_addr *ia;
 
-       if (dhcp6_makemessage(ifp) == -1)
-               logerr("%s: %s", __func__, ifp->name);
-       else
-               dhcp6_sendinform(ifp);
+       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
@@ -1637,12 +1710,21 @@ dhcp6_startexpire(void *arg)
        eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp);
 
        logerrx("%s: DHCPv6 lease expired", ifp->name);
-       dhcp6_freedrop_addrs(ifp, 1, NULL);
+       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);
+               dhcp6_delete_delegates(ifp);
 #endif
-       script_runreason(ifp, "EXPIRE6");
-       if (ipv6nd_hasradhcp(ifp) || dhcp6_hasprefixdelegation(ifp))
+               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);
@@ -1672,8 +1754,9 @@ dhcp6_startrelease(struct interface *ifp)
 
        state->state = DH6S_RELEASE;
        state->RTC = 0;
+       state->IMD = REL_MAX_DELAY;
        state->IRT = REL_TIMEOUT;
-       state->MRT = 0;
+       state->MRT = REL_MAX_RT;
        /* MRC of REL_MAX_RC is optional in RFC 3315 18.1.6 */
 #if 0
        state->MRC = REL_MAX_RC;
@@ -1837,7 +1920,7 @@ dhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid,
                } else {
                        if (!(a->flags & IPV6_AF_ONLINK))
                                a->flags |= IPV6_AF_ONLINK | IPV6_AF_NEW;
-                       a->flags &= ~IPV6_AF_STALE;
+                       a->flags &= ~(IPV6_AF_STALE | IPV6_AF_EXTENDED);
                }
                a->acquired = *acquired;
                a->prefix_pltime = ia.pltime;
@@ -1919,7 +2002,9 @@ dhcp6_findpd(struct interface *ifp, const uint8_t *iaid,
                                a->flags |= IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
                                TAILQ_INIT(&a->pd_pfxs);
                        }
-                       a->flags &= ~(IPV6_AF_STALE | IPV6_AF_REQUEST);
+                       a->flags &= ~(IPV6_AF_STALE |
+                                     IPV6_AF_EXTENDED |
+                                     IPV6_AF_REQUEST);
                        if (a->prefix_vltime != ntohl(pdp.vltime))
                                a->flags |= IPV6_AF_NEW;
                }
@@ -2129,7 +2214,9 @@ dhcp6_deprecateaddrs(struct ipv6_addrhead *addrs)
        struct ipv6_addr *ia, *ian;
 
        TAILQ_FOREACH_SAFE(ia, addrs, next, ian) {
-               if (ia->flags & IPV6_AF_STALE) {
+               if (ia->flags & IPV6_AF_EXTENDED)
+                       ;
+               else if (ia->flags & IPV6_AF_STALE) {
                        if (ia->prefix_vltime != 0)
                                logdebugx("%s: %s: became stale",
                                    ia->iface->name, ia->saddr);
@@ -2170,6 +2257,8 @@ dhcp6_deprecateaddrs(struct ipv6_addrhead *addrs)
                        continue;
                }
                TAILQ_REMOVE(addrs, ia, next);
+               if (ia->flags & IPV6_AF_EXTENDED)
+                       ipv6_deleteaddr(ia);
                ipv6_freeaddr(ia);
        }
 }
@@ -2243,7 +2332,6 @@ dhcp6_readlease(struct interface *ifp, int validate)
        struct stat st;
        int fd;
        struct dhcp6_message *lease;
-       struct timespec acquired;
        time_t now;
        int retval;
        bool fd_opened;
@@ -2288,21 +2376,22 @@ dhcp6_readlease(struct interface *ifp, int validate)
                goto auth;
        }
 
-       clock_gettime(CLOCK_MONOTONIC, &acquired);
+       clock_gettime(CLOCK_MONOTONIC, &state->acquired);
        if ((now = time(NULL)) == -1)
                goto ex;
-       acquired.tv_sec -= now - st.st_mtime;
+       state->acquired.tv_sec -= now - st.st_mtime;
 
        /* Check to see if the lease is still valid */
        fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL,
-           &acquired);
+           &state->acquired);
        if (fd == -1)
                goto ex;
 
        if (state->expire != ND6_INFINITE_LIFETIME &&
            state->leasefile[0] != '\0')
        {
-               if ((time_t)state->expire < now - st.st_mtime) {
+               if ((time_t)state->expire < now - st.st_mtime &&
+                   !(ifp->options->options & DHCPCD_LASTLEASE_EXTEND)) {
                        logdebugx("%s: discarding expired lease", ifp->name);
                        retval = 0;
                        goto ex;
@@ -2700,6 +2789,253 @@ dhcp6_find_delegates(struct interface *ifp)
 }
 #endif
 
+static void
+dhcp6_bind(struct interface *ifp, const char *op)
+{
+       struct dhcp6_state *state = D6_STATE(ifp);
+       bool has_new = false;
+       struct ipv6_addr *ia;
+       logfunc_t *lognewinfo;
+       struct timespec now;
+
+       TAILQ_FOREACH(ia, &state->addrs, next) {
+               if (ia->flags & IPV6_AF_NEW) {
+                       has_new = true;
+                       break;
+               }
+       }
+       lognewinfo = has_new ? loginfox : logdebugx;
+       if (op != NULL)
+               lognewinfo("%s: %s received from %s",
+                   ifp->name, op, ifp->ctx->sfrom);
+
+       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";
+               o = dhcp6_findmoption(state->new, state->new_len,
+                                     D6_OPTION_INFO_REFRESH_TIME, &ol);
+               if (o == NULL || ol != sizeof(uint32_t))
+                       state->renew = IRT_DEFAULT;
+               else {
+                       memcpy(&state->renew, o, ol);
+                       state->renew = ntohl(state->renew);
+                       if (state->renew < IRT_MINIMUM)
+                               state->renew = IRT_MINIMUM;
+               }
+               state->rebind = 0;
+               state->expire = ND6_INFINITE_LIFETIME;
+               state->lowpl = ND6_INFINITE_LIFETIME;
+       }
+               break;
+
+       case DH6S_REQUEST:
+               if (state->reason == NULL)
+                       state->reason = "BOUND6";
+               /* FALLTHROUGH */
+       case DH6S_RENEW:
+               if (state->reason == NULL)
+                       state->reason = "RENEW6";
+               /* FALLTHROUGH */
+       case DH6S_REBIND:
+               if (state->reason == NULL)
+                       state->reason = "REBIND6";
+               /* FALLTHROUGH */
+       case DH6S_CONFIRM:
+               if (state->reason == NULL)
+                       state->reason = "REBOOT6";
+       case DH6S_TIMEDOUT:
+               if (state->reason == NULL)
+                       state->reason = "TIMEOUT6";
+               if (state->renew != 0) {
+                       bool all_expired = true;
+
+                       TAILQ_FOREACH(ia, &state->addrs, next) {
+                               loginfox("%s %d", ia->saddr, ia->flags);
+                               if (ia->flags & IPV6_AF_STALE)
+                                       continue;
+                               if (ia->prefix_vltime <= state->renew)
+                                       logwarnx(
+                                           "%s: %s will expire before renewal",
+                                           ifp->name, ia->saddr);
+                               else
+                                       all_expired = false;
+                       }
+                       if (all_expired) {
+                               /* All address's vltime happens at or before
+                                * the configured T1 in the IA.
+                                * This is a badly configured server and we
+                                * have to use our own notion of what
+                                * T1 and T2 should be as a result.
+                                *
+                                * Doing this violates RFC 3315 22.4:
+                                * In a message sent by a server to a client,
+                                * the client MUST use the values in the T1
+                                * and T2 fields for the T1 and T2 parameters,
+                                * unless those values in those fields are 0.
+                                */
+                               logwarnx("%s: ignoring T1 %"PRIu32
+                                   " to due address expiry",
+                                   ifp->name, state->renew);
+                               state->renew = state->rebind = 0;
+                       }
+               }
+               if (state->renew == 0 && state->lowpl != ND6_INFINITE_LIFETIME)
+                       state->renew = (uint32_t)(state->lowpl * 0.5);
+               if (state->rebind == 0 && state->lowpl != ND6_INFINITE_LIFETIME)
+                       state->rebind = (uint32_t)(state->lowpl * 0.8);
+               break;
+       default:
+               state->reason = "UNKNOWN6";
+               break;
+       }
+
+       clock_gettime(CLOCK_MONOTONIC, &now);
+       if (state->state == DH6S_TIMEDOUT || state->state == DH6S_ITIMEDOUT) {
+               struct timespec diff;
+
+               /* Reduce timers */
+               timespecsub(&now, &state->acquired, &diff);
+               if (state->renew && state->renew != ND6_INFINITE_LIFETIME) {
+                       if (state->renew > diff.tv_sec)
+                               state->renew -= diff.tv_sec;
+                       else
+                               state->renew = 0;
+               }
+               if (state->rebind && state->rebind != ND6_INFINITE_LIFETIME) {
+                       if (state->rebind > diff.tv_sec)
+                               state->rebind -= diff.tv_sec;
+                       else
+                               state->rebind = 0;
+               }
+               if (state->expire && state->expire != ND6_INFINITE_LIFETIME) {
+                       if (state->expire > diff.tv_sec)
+                               state->expire -= diff.tv_sec;
+                       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->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;
+       }
+
+       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:
+                       state->state = DH6S_INFORMED;
+                       break;
+               default:
+                       state->state = DH6S_BOUND;
+                       break;
+               }
+
+               if (state->renew && state->renew != ND6_INFINITE_LIFETIME)
+                       eloop_timeout_add_sec(ifp->ctx->eloop,
+                           (time_t)state->renew,
+                           state->state == DH6S_INFORMED ?
+                           dhcp6_startinform : dhcp6_startrenew, ifp);
+               if (state->rebind && state->rebind != ND6_INFINITE_LIFETIME)
+                       eloop_timeout_add_sec(ifp->ctx->eloop,
+                           (time_t)state->rebind, dhcp6_startrebind, ifp);
+               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 (state->state == DH6S_INFORMED)
+                       lognewinfo("%s: refresh in %"PRIu32" seconds",
+                           ifp->name, state->renew);
+               else if (state->renew || state->rebind)
+                       lognewinfo("%s: renew in %"PRIu32", "
+                           "rebind in %"PRIu32", "
+                           "expire in %"PRIu32" seconds",
+                           ifp->name,
+                           state->renew, state->rebind, state->expire);
+               else if (state->expire == 0)
+                       lognewinfo("%s: will expire", ifp->name);
+               else
+                       lognewinfo("%s: expire in %"PRIu32" seconds",
+                           ifp->name, state->expire);
+               if_initrt(ifp->ctx, AF_INET6);
+               rt_build(ifp->ctx, AF_INET6);
+               if (!timed_out)
+                       dhcp6_writelease(ifp);
+#ifndef SMALL
+               dhcp6_delegate_prefix(ifp);
+#endif
+               dhcp6_script_try_run(ifp, 0);
+       }
+
+       if (ifp->ctx->options & DHCPCD_TEST ||
+           (ifp->options->options & DHCPCD_INFORM &&
+           !(ifp->ctx->options & DHCPCD_MASTER)))
+       {
+               eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
+       }
+}
+
 /* ARGSUSED */
 static void
 dhcp6_handledata(void *arg)
@@ -2718,8 +3054,7 @@ dhcp6_handledata(void *arg)
        const struct dhcp_opt *opt;
        const struct if_options *ifo;
        struct ipv6_addr *ap;
-       bool valid_op, has_new;
-       logfunc_t *lognewinfo;
+       bool valid_op;
 #ifdef AUTH
        uint8_t *auth;
        uint16_t auth_len;
@@ -2879,17 +3214,6 @@ dhcp6_handledata(void *arg)
                case DH6S_INFORM:
                        if (dhcp6_checkstatusok(ifp, r, NULL, len) == -1)
                                return;
-                       /* RFC4242 */
-                       o = dhcp6_findmoption(r, len,
-                                             D6_OPTION_INFO_REFRESH_TIME, &ol);
-                       if (o == NULL || ol != sizeof(uint32_t))
-                               state->renew = IRT_DEFAULT;
-                       else {
-                               memcpy(&state->renew, o, ol);
-                               state->renew = ntohl(state->renew);
-                               if (state->renew < IRT_MINIMUM)
-                                       state->renew = IRT_MINIMUM;
-                       }
                        break;
                case DH6S_CONFIRM:
                        if (dhcp6_validatelease(ifp, r, len,
@@ -3052,7 +3376,7 @@ dhcp6_handledata(void *arg)
                if (state->state == DH6S_REQUEST) /* rapid commit */
                        break;
                TAILQ_FOREACH(ap, &state->addrs, next) {
-                       if (!(ap->flags & IPV6_AF_REQUEST))
+                       if (!(ap->flags & (IPV6_AF_STALE | IPV6_AF_REQUEST)))
                                break;
                }
                if (ap == NULL)
@@ -3069,140 +3393,7 @@ dhcp6_handledata(void *arg)
                return;
        }
 
-       has_new = false;
-       TAILQ_FOREACH(ap, &state->addrs, next) {
-               if (ap->flags & IPV6_AF_NEW) {
-                       has_new = true;
-                       break;
-               }
-       }
-       lognewinfo = has_new ? loginfox : logdebugx;
-       lognewinfo("%s: %s received from %s", ifp->name, op, ctx->sfrom);
-
-       state->reason = NULL;
-       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-       switch(state->state) {
-       case DH6S_INFORM:
-               state->rebind = 0;
-               state->expire = ND6_INFINITE_LIFETIME;
-               state->lowpl = ND6_INFINITE_LIFETIME;
-               state->reason = "INFORM6";
-               break;
-       case DH6S_REQUEST:
-               if (state->reason == NULL)
-                       state->reason = "BOUND6";
-               /* FALLTHROUGH */
-       case DH6S_RENEW:
-               if (state->reason == NULL)
-                       state->reason = "RENEW6";
-               /* FALLTHROUGH */
-       case DH6S_REBIND:
-               if (state->reason == NULL)
-                       state->reason = "REBIND6";
-               /* FALLTHROUGH */
-       case DH6S_CONFIRM:
-               if (state->reason == NULL)
-                       state->reason = "REBOOT6";
-               if (state->renew != 0) {
-                       int all_expired = 1;
-
-                       TAILQ_FOREACH(ap, &state->addrs, next) { 
-                               if (ap->flags & IPV6_AF_STALE)
-                                       continue;
-                               if (ap->prefix_vltime <= state->renew)
-                                       logwarnx(
-                                           "%s: %s will expire before renewal",
-                                           ifp->name, ap->saddr);
-                               else
-                                       all_expired = 0;
-                       }
-                       if (all_expired) {
-                               /* All address's vltime happens at or before
-                                * the configured T1 in the IA.
-                                * This is a badly configured server and we
-                                * have to use our own notion of what
-                                * T1 and T2 should be as a result.
-                                *
-                                * Doing this violates RFC 3315 22.4:
-                                * In a message sent by a server to a client,
-                                * the client MUST use the values in the T1
-                                * and T2 fields for the T1 and T2 parameters,
-                                * unless those values in those fields are 0.
-                                */
-                               logwarnx("%s: ignoring T1 %"PRIu32
-                                   " to due address expiry",
-                                   ifp->name, state->renew);
-                               state->renew = state->rebind = 0;
-                       }
-               }
-               if (state->renew == 0 && state->lowpl != ND6_INFINITE_LIFETIME)
-                       state->renew = (uint32_t)(state->lowpl * 0.5);
-               if (state->rebind == 0 && state->lowpl != ND6_INFINITE_LIFETIME)
-                       state->rebind = (uint32_t)(state->lowpl * 0.8);
-               break;
-       default:
-               state->reason = "UNKNOWN6";
-               break;
-       }
-
-       if (state->state != DH6S_CONFIRM) {
-               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;
-       }
-
-       if (ifp->ctx->options & DHCPCD_TEST)
-               script_runreason(ifp, "TEST");
-       else {
-               if (state->state == DH6S_INFORM)
-                       state->state = DH6S_INFORMED;
-               else
-                       state->state = DH6S_BOUND;
-               if (state->renew && state->renew != ND6_INFINITE_LIFETIME)
-                       eloop_timeout_add_sec(ifp->ctx->eloop,
-                           (time_t)state->renew,
-                           state->state == DH6S_INFORMED ?
-                           dhcp6_startinform : dhcp6_startrenew, ifp);
-               if (state->rebind && state->rebind != ND6_INFINITE_LIFETIME)
-                       eloop_timeout_add_sec(ifp->ctx->eloop,
-                           (time_t)state->rebind, dhcp6_startrebind, ifp);
-               if (state->expire != ND6_INFINITE_LIFETIME)
-                       eloop_timeout_add_sec(ifp->ctx->eloop,
-                           (time_t)state->expire, dhcp6_startexpire, ifp);
-
-               dhcp6_deprecateaddrs(&state->addrs);
-               ipv6_addaddrs(&state->addrs);
-
-               if (state->state == DH6S_INFORMED)
-                       lognewinfo("%s: refresh in %"PRIu32" seconds",
-                           ifp->name, state->renew);
-               else if (state->renew || state->rebind)
-                       lognewinfo("%s: renew in %"PRIu32", "
-                           "rebind in %"PRIu32", "
-                           "expire in %"PRIu32" seconds",
-                           ifp->name,
-                           state->renew, state->rebind, state->expire);
-               else if (state->expire == 0)
-                       lognewinfo("%s: will expire", ifp->name);
-               if_initrt(ifp->ctx, AF_INET6);
-               rt_build(ifp->ctx, AF_INET6);
-               dhcp6_writelease(ifp);
-#ifndef SMALL
-               dhcp6_delegate_prefix(ifp);
-#endif
-               dhcp6_script_try_run(ifp, 0);
-       }
-
-       if (ifp->ctx->options & DHCPCD_TEST ||
-           (ifp->options->options & DHCPCD_INFORM &&
-           !(ifp->ctx->options & DHCPCD_MASTER)))
-       {
-               eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
-       }
+       dhcp6_bind(ifp, op);
 }
 
 static int
index 081d53df27dc19f982c8431090480304cad26af0..7d9b6de7a0d73190d4edcb9bed1f44e53d2605b2 100644 (file)
 #define D6_STATUS_NOTONLINK    4
 #define D6_STATUS_USEMULTICAST 5
 
-#define SOL_MAX_DELAY          1
-#define SOL_TIMEOUT            1
-#define SOL_MAX_RT             3600 /* RFC7083 */
-#define REQ_TIMEOUT            1
-#define REQ_MAX_RT             30
-#define REQ_MAX_RC             10
-#define CNF_MAX_DELAY          1
-#define CNF_TIMEOUT            1
-#define CNF_MAX_RT             4
-#define CNF_MAX_RD             10
-#define REN_TIMEOUT            10
-#define REN_MAX_RT             600
-#define REB_TIMEOUT            10
-#define REB_MAX_RT             600
-#define INF_MAX_DELAY          1
-#define INF_TIMEOUT            1
-#define INF_MAX_RT             3600 /* RFC7083 */
-#define REL_TIMEOUT            1
-#define REL_MAX_RC             5
-#define DEC_TIMEOUT            1
-#define DEC_MAX_RC             5
-#define REC_TIMEOUT            2
-#define REC_MAX_RC             8
-#define HOP_COUNT_LIMIT                32
+#define        SOL_MAX_DELAY           1
+#define        SOL_TIMEOUT             1
+#define        SOL_MAX_RT              3600 /* RFC7083 */
+#define        SOL_MAX_RC              0
+#define        REQ_MAX_DELAY           0
+#define        REQ_TIMEOUT             1
+#define        REQ_MAX_RT              30
+#define        REQ_MAX_RC              10
+#define        CNF_MAX_DELAY           1
+#define        CNF_TIMEOUT             1
+#define        CNF_MAX_RT              4
+#define        CNF_MAX_RC              0
+#define        CNF_MAX_RD              10
+#define        REN_MAX_DELAY           0
+#define        REN_TIMEOUT             10
+#define        REN_MAX_RT              600
+#define        REB_MAX_DELAY           0
+#define        REB_TIMEOUT             10
+#define        REB_MAX_RT              600
+#define        INF_MAX_DELAY           1
+#define        INF_TIMEOUT             1
+#define        INF_MAX_RD              CNF_MAX_RD /* NOT RFC defined */
+#define        INF_MAX_RT              3600 /* RFC7083 */
+#define        REL_MAX_DELAY           0
+#define        REL_TIMEOUT             1
+#define        REL_MAX_RT              0
+#define        REL_MAX_RC              5
+#define        DEC_MAX_DELAY           0
+#define        DEC_TIMEOUT             1
+#define        DEC_MAX_RC              5
+#define        REC_MAX_DELAY           0
+#define        REC_TIMEOUT             2
+#define        REC_MAX_RC              8
+#define        HOP_COUNT_LIMIT         32
 
 /* RFC4242 3.1 */
 #define IRT_DEFAULT            86400
@@ -153,6 +163,8 @@ enum DH6S {
        DH6S_RENEW_REQUESTED,
        DH6S_PROBE,
        DH6S_DELEGATED,
+       DH6S_TIMEDOUT,
+       DH6S_ITIMEDOUT,
        DH6S_RELEASE,
        DH6S_RELEASED
 };
@@ -181,6 +193,7 @@ struct dhcp6_state {
        struct dhcp6_message *old;
        size_t old_len;
 
+       struct timespec acquired;
        uint32_t renew;
        uint32_t rebind;
        uint32_t expire;
index dfc7a3cb210329f43a774748c08b001cd18a1d92..e94a1253f8ca0d28d39321be1ffa63f4ceaa8168 100644 (file)
@@ -594,7 +594,7 @@ ipv6_deletedaddr(struct ipv6_addr *ia)
 #endif
 }
 
-static void
+void
 ipv6_deleteaddr(struct ipv6_addr *ia)
 {
        struct ipv6_state *state;
@@ -1447,7 +1447,7 @@ ipv6_tryaddlinklocal(struct interface *ifp)
 
 struct ipv6_addr *
 ipv6_newaddr(struct interface *ifp, struct in6_addr *addr, uint8_t prefix_len,
-    short flags)
+    int flags)
 {
        struct ipv6_addr *ia;
        char buf[INET6_ADDRSTRLEN];
index 1855cc2b7ef749d38402b5255db42b6fb734609b..5ae481933f1d48eacb232a0d5a84bc2f6f90d88e 100644 (file)
@@ -161,7 +161,7 @@ struct ipv6_addr {
        struct timespec acquired;
        struct in6_addr addr;
        int addr_flags;
-       short flags;
+       int flags;
        char saddr[INET6_ADDRSTRLEN];
        uint8_t iaid[4];
        uint16_t ia_type;
@@ -201,8 +201,9 @@ struct ipv6_addr {
 #define        IPV6_AF_STATIC          0x0800
 #define IPV6_AF_DELEGATEDLOG   0x1000
 #define IPV6_AF_RAPFX          0x2000
+#define IPV6_AF_EXTENDED       0x4000
 #ifdef IPV6_MANAGETEMPADDR
-#define        IPV6_AF_TEMPORARY       0X4000
+#define        IPV6_AF_TEMPORARY       0X8000
 #endif
 
 struct ll_callback {
@@ -245,6 +246,7 @@ int ipv6_userprefix( const struct in6_addr *, short prefix_len,
 void ipv6_checkaddrflags(void *);
 int ipv6_addaddr(struct ipv6_addr *, const struct timespec *);
 ssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs);
+void ipv6_deleteaddr(struct ipv6_addr *);
 void ipv6_freedrop_addrs(struct ipv6_addrhead *, int,
     const struct interface *);
 void ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *,
@@ -262,7 +264,7 @@ struct ipv6_addr *ipv6_findmaskaddr(struct dhcpcd_ctx *,
 #define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL, IN6_IFF_NOTUSEABLE)
 int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
 struct ipv6_addr *ipv6_newaddr(struct interface *, struct in6_addr *, uint8_t,
-    short);
+    int);
 void ipv6_freeaddr(struct ipv6_addr *);
 void ipv6_freedrop(struct interface *, int);
 #define ipv6_free(ifp) ipv6_freedrop((ifp), 0)