changeset 5042:dac9291f82f0 draft

DHCP6: Clean up old lease when we fail to confirm/rebind, etc Also removed the TIMEOUT states which makes things easier to read.
author Roy Marples <roy@marples.name>
date Thu, 06 Feb 2020 12:58:43 +0000
parents c44b319ae036
children f51d310db3b7
files src/dhcp6.c src/dhcp6.h src/ipv6.c
diffstat 3 files changed, 93 insertions(+), 153 deletions(-) [+]
line wrap: on
line diff
--- a/src/dhcp6.c	Thu Feb 06 12:54:11 2020 +0000
+++ b/src/dhcp6.c	Thu Feb 06 12:58:43 2020 +0000
@@ -174,6 +174,12 @@
 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 @@
 	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 @@
 }
 
 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 @@
 	 * 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;
+		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);
 	}
 
-	switch (state->state) {
-	case DH6S_INFORM:
-	case DH6S_INFORMED:
-		state->state = DH6S_ITIMEDOUT;
-		break;
-	default:
-		state->state = DH6S_TIMEDOUT;
-		break;
-	}
-
-	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_fail(ifp);
 }
 
-#ifdef SMALL
-#define dhcp6_hasprefixdelegation(a)	(0)
-#else
+#ifndef SMALL
 static void
 dhcp6_failrebind(void *arg)
 {
@@ -1838,21 +1858,6 @@
 }
 
 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)
 {
 	struct interface *ifp;
@@ -1861,24 +1866,7 @@
 	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 @@
 		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 @@
 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 @@
 		}
 	}
 	lognewinfo = has_new ? loginfox : logdebugx;
-	if (op != NULL)
+	if (!timedout) {
 		lognewinfo("%s: %s received from %s", ifp->name, op, sfrom);
-
-	state->reason = NULL;
-	if (state->state != DH6S_ITIMEDOUT)
-		eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+		/* 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);
+
 	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 @@
 	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 @@
 		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 @@
 		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;
-			}
+			else
+				state->expire = 0;
 		}
-		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;
+		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 @@
 		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 @@
 			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 @@
 	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 @@
 		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);
 }
 
--- a/src/dhcp6.h	Thu Feb 06 12:54:11 2020 +0000
+++ b/src/dhcp6.h	Thu Feb 06 12:58:43 2020 +0000
@@ -169,8 +169,6 @@
 	DH6S_RENEW_REQUESTED,
 	DH6S_PROBE,
 	DH6S_DELEGATED,
-	DH6S_TIMEDOUT,
-	DH6S_ITIMEDOUT,
 	DH6S_RELEASE,
 	DH6S_RELEASED
 };
--- a/src/ipv6.c	Thu Feb 06 12:54:11 2020 +0000
+++ b/src/ipv6.c	Thu Feb 06 12:58:43 2020 +0000
@@ -1528,9 +1528,10 @@
 	/* 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));