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)
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;
}
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);
* 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);
}
}
dhcp6_fail(ifp);
}
-#ifdef SMALL
-#define dhcp6_hasprefixdelegation(a) (0)
-#else
+#ifndef SMALL
static void
dhcp6_failrebind(void *arg)
{
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)
{
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
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);
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;
}
}
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))
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;
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) {
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,
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",
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);
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;
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);
}