From: Roy Marples Date: Thu, 6 Feb 2020 12:58:43 +0000 (+0000) Subject: DHCP6: Clean up old lease when we fail to confirm/rebind, etc X-Git-Tag: v8.1.7~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b9934f63e18ec81dd806c2a6e5ec0664de576f51;p=thirdparty%2Fdhcpcd.git DHCP6: Clean up old lease when we fail to confirm/rebind, etc Also removed the TIMEOUT states which makes things easier to read. --- diff --git a/src/dhcp6.c b/src/dhcp6.c index d1adbbcc..16b0b05a 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -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); } diff --git a/src/dhcp6.h b/src/dhcp6.h index ce6ad8d5..6df41c34 100644 --- a/src/dhcp6.h +++ b/src/dhcp6.h @@ -167,8 +167,6 @@ enum DH6S { DH6S_RENEW_REQUESTED, DH6S_PROBE, DH6S_DELEGATED, - DH6S_TIMEDOUT, - DH6S_ITIMEDOUT, DH6S_RELEASE, DH6S_RELEASED }; diff --git a/src/ipv6.c b/src/ipv6.c index 9092a6c9..e09c3376 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -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));