changeset 5171:5943646f9dd3 draft

DHCP6: Implement DECLINE support for duplicated addresses This is the final piece of DHCP6 to implement! Part of this change drops the use of the IPV6_AF_DUPLICATED flag and we just use IN6_IFF_DUPLICATED now.
author Roy Marples <roy@marples.name>
date Sun, 26 Apr 2020 17:22:42 +0100
parents 8c6a66d852f4
children 6efcf294d3a0
files src/dhcp6.c src/dhcp6.h src/ipv6.c src/ipv6.h src/ipv6nd.c
diffstat 5 files changed, 144 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/src/dhcp6.c	Fri Apr 24 12:23:05 2020 +0100
+++ b/src/dhcp6.c	Sun Apr 26 17:22:42 2020 +0100
@@ -26,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-/* TODO: We should decline dupliate addresses detected */
-
 #include <sys/stat.h>
 #include <sys/utsname.h>
 
@@ -139,6 +137,7 @@
 	{ DHCP6_INFORMATION_REQ, "INFORM6" },
 	{ DHCP6_RELEASE, "RELEASE6" },
 	{ DHCP6_RECONFIGURE, "RECONFIGURE6" },
+	{ DHCP6_DECLINE, "DECLINE6" },
 	{ 0, NULL }
 };
 
@@ -174,6 +173,7 @@
 static void dhcp6_bind(struct interface *, const char *, const char *);
 static void dhcp6_failinform(void *);
 static void dhcp6_recvaddr(void *);
+static void dhcp6_startdecline(struct interface *);
 
 #ifdef SMALL
 #define dhcp6_hasprefixdelegation(a)	(0)
@@ -181,6 +181,12 @@
 static int dhcp6_hasprefixdelegation(struct interface *);
 #endif
 
+#define DECLINE_IA(ia) \
+	((ia)->addr_flags & IN6_IFF_DUPLICATED && \
+	(ia)->ia_type != 0 && (ia)->ia_type != D6_OPTION_IA_PD && \
+	!((ia)->flags & IPV6_AF_STALE) && \
+	(ia)->prefix_vltime != 0)
+
 void
 dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
     const struct dhcp_opt *opts, size_t opts_len)
@@ -672,7 +678,7 @@
 	len = 0;
 	si = NULL;
 	hl = 0; /* Appease gcc */
-	if (state->state != DH6S_RELEASE) {
+	if (state->state != DH6S_RELEASE && state->state != DH6S_DECLINE) {
 		for (l = 0, opt = ifp->ctx->dhcp6_opts;
 		    l < ifp->ctx->dhcp6_opts_len;
 		    l++, opt++)
@@ -750,6 +756,8 @@
 		m = state->recv;
 		ml = state->recv_len;
 		/* FALLTHROUGH */
+	case DH6S_DECLINE:
+		/* FALLTHROUGH */
 	case DH6S_RELEASE:
 		/* FALLTHROUGH */
 	case DH6S_RENEW:
@@ -778,6 +786,8 @@
 			    (ap->prefix_vltime == 0 ||
 			    state->state == DH6S_DISCOVER))
 				continue;
+			if (DECLINE_IA(ap) && state->state != DH6S_DECLINE)
+				continue;
 			if (ap->ia_type == D6_OPTION_IA_PD) {
 #ifndef SMALL
 				len += sizeof(o) + sizeof(struct dhcp6_pd_addr);
@@ -836,6 +846,9 @@
 	case DH6S_RELEASE:
 		type = DHCP6_RELEASE;
 		break;
+	case DH6S_DECLINE:
+		type = DHCP6_DECLINE;
+		break;
 	default:
 		errno = EINVAL;
 		return -1;
@@ -949,6 +962,8 @@
 			    (ap->prefix_vltime == 0 ||
 			    state->state == DH6S_DISCOVER))
 				continue;
+			if (DECLINE_IA(ap) && state->state != DH6S_DECLINE)
+				continue;
 			if (ap->ia_type != ifia->ia_type)
 				continue;
 			if (memcmp(ap->iaid, ifia->iaid, sizeof(ap->iaid)))
@@ -1010,7 +1025,10 @@
 		memcpy(o_lenp, &ia_na_len, sizeof(ia_na_len));
 	}
 
-	if (state->send->type != DHCP6_RELEASE && n_options) {
+	if (state->send->type != DHCP6_RELEASE &&
+	    state->send->type != DHCP6_DECLINE &&
+	    n_options)
+	{
 		o_lenp = NEXTLEN;
 		o.len = 0;
 		COPYIN1(D6_OPTION_ORO, 0);
@@ -1072,7 +1090,9 @@
 	if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
 		p += dhcp6_makevendor(p, ifp);
 
-	if (state->send->type != DHCP6_RELEASE) {
+	if (state->send->type != DHCP6_RELEASE &&
+	    state->send->type != DHCP6_DECLINE)
+	{
 		if (fqdn != FQDN_DISABLE) {
 			o_lenp = NEXTLEN;
 			COPYIN1(D6_OPTION_FQDN, 0);
@@ -1432,6 +1452,13 @@
 }
 
 static void
+dhcp6_senddecline(void *arg)
+{
+
+	dhcp6_sendmessage(arg, dhcp6_senddecline);
+}
+
+static void
 dhcp6_sendrelease(void *arg)
 {
 
@@ -1496,54 +1523,60 @@
 	struct ipv6_addr *ia = arg;
 	struct interface *ifp;
 	struct dhcp6_state *state;
-	int wascompleted, valid;
-
-	wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
+	struct ipv6_addr *ia2;
+	bool completed, valid, oneduplicated;
+
+	completed = (ia->flags & IPV6_AF_DADCOMPLETED);
 	ia->flags |= IPV6_AF_DADCOMPLETED;
-	if (ia->flags & IPV6_AF_DUPLICATED) {
-		/* XXX FIXME
-		 * We should decline the address */
+	if (ia->addr_flags & IN6_IFF_DUPLICATED)
 		logwarnx("%s: DAD detected %s", ia->iface->name, ia->saddr);
-	}
-
-	if (!wascompleted) {
-		ifp = ia->iface;
-
-		state = D6_STATE(ifp);
-		if (state->state == DH6S_BOUND ||
-		    state->state == DH6S_DELEGATED)
-		{
-			struct ipv6_addr *ia2;
+
+#ifdef ND6_ADVERTISE
+	else
+		ipv6nd_advertise(ia);
+#endif
+	if (completed)
+		return;
+
+	ifp = ia->iface;
+	state = D6_STATE(ifp);
+	if (state->state != DH6S_BOUND && state->state != DH6S_DELEGATED)
+		return;
 
 #ifdef SMALL
-			valid = true;
+	valid = true;
 #else
-			valid = (ia->delegating_prefix == NULL);
+	valid = (ia->delegating_prefix == NULL);
 #endif
-			TAILQ_FOREACH(ia2, &state->addrs, next) {
-				if (ia2->flags & IPV6_AF_ADDED &&
-				    !(ia2->flags & IPV6_AF_DADCOMPLETED))
-				{
-					wascompleted = 1;
-					break;
-				}
-			}
-			if (!wascompleted) {
-				logdebugx("%s: DHCPv6 DAD completed",
-				    ifp->name);
-				script_runreason(ifp,
+	completed = true;
+	oneduplicated = false;
+	TAILQ_FOREACH(ia2, &state->addrs, next) {
+		if (ia2->flags & IPV6_AF_ADDED &&
+		    !(ia2->flags & IPV6_AF_DADCOMPLETED))
+		{
+			completed = false;
+			break;
+		}
+		if (DECLINE_IA(ia))
+			oneduplicated = true;
+	}
+	if (!completed)
+		return;
+
+	logdebugx("%s: DHCPv6 DAD completed", ifp->name);
+
+	if (oneduplicated && state->state == DH6S_BOUND) {
+		dhcp6_startdecline(ifp);
+		return;
+	}
+
+	script_runreason(ifp,
 #ifndef SMALL
-				    ia->delegating_prefix ? "DELEGATED6" :
+	    ia->delegating_prefix ? "DELEGATED6" :
 #endif
-				    state->reason);
-				if (valid)
-					dhcpcd_daemonise(ifp->ctx);
-			}
-#ifdef ND6_ADVERTISE
-			ipv6nd_advertise(ia);
-#endif
-		}
-	}
+	    state->reason);
+	if (valid)
+		dhcpcd_daemonise(ifp->ctx);
 }
 
 static void
@@ -1682,7 +1715,7 @@
 }
 
 static void
-dhcp6_fail(struct interface* ifp)
+dhcp6_fail(struct interface *ifp)
 {
 	struct dhcp6_state *state = D6_STATE(ifp);
 
@@ -1867,8 +1900,18 @@
 dhcp6_startconfirm(struct interface *ifp)
 {
 	struct dhcp6_state *state;
+	struct ipv6_addr *ia;
 
 	state = D6_STATE(ifp);
+
+	TAILQ_FOREACH(ia, &state->addrs, next) {
+		if (!DECLINE_IA(ia))
+			continue;
+		logerrx("%s: prior DHCPv6 has a duplicated address", ifp->name);
+		dhcp6_startdecline(ifp);
+		return;
+	}
+
 	state->state = DH6S_CONFIRM;
 	state->RTC = 0;
 	state->IMD = CNF_MAX_DELAY;
@@ -1877,6 +1920,7 @@
 	state->MRC = CNF_MAX_RC;
 
 	loginfox("%s: confirming prior DHCPv6 lease", ifp->name);
+
 	if (dhcp6_makemessage(ifp) == -1) {
 		logerr("%s: %s", __func__, ifp->name);
 		return;
@@ -1899,6 +1943,36 @@
 }
 
 static void
+dhcp6_faildecline(void *arg)
+{
+	struct interface *ifp = arg;
+
+	logerrx("%s: failed to decline duplicated DHCPv6 addresses", ifp->name);
+	dhcp6_fail(ifp);
+}
+
+static void
+dhcp6_startdecline(struct interface *ifp)
+{
+	struct dhcp6_state *state;
+
+	state = D6_STATE(ifp);
+	loginfox("%s: declining failed DHCPv6 addresses", ifp->name);
+	state->state = DH6S_DECLINE;
+	state->RTC = 0;
+	state->IMD = 0;
+	state->IRT = DEC_TIMEOUT;
+	state->MRT = 0;
+	state->MRC = DEC_MAX_RC;
+	state->MRCcallback = dhcp6_faildecline;
+
+	if (dhcp6_makemessage(ifp) == -1)
+		logerr("%s: %s", __func__, ifp->name);
+	else
+		dhcp6_senddecline(ifp);
+}
+
+static void
 dhcp6_finishrelease(void *arg)
 {
 	struct interface *ifp;
@@ -3350,6 +3424,13 @@
 			if (state->state == DH6S_DISCOVER)
 				state->state = DH6S_REQUEST;
 			break;
+		case DH6S_DECLINE:
+			/* This isnt really a failure, but an
+			 * acknowledgement of one. */
+			loginfox("%s: %s acknowledged DECLINE6",
+			    ifp->name, sfrom);
+			dhcp6_fail(ifp);
+			return;
 		default:
 			valid_op = false;
 			break;
--- a/src/dhcp6.h	Fri Apr 24 12:23:05 2020 +0100
+++ b/src/dhcp6.h	Sun Apr 26 17:22:42 2020 +0100
@@ -168,9 +168,10 @@
 	DH6S_INFORMED,
 	DH6S_RENEW_REQUESTED,
 	DH6S_PROBE,
+	DH6S_DECLINE,
 	DH6S_DELEGATED,
 	DH6S_RELEASE,
-	DH6S_RELEASED
+	DH6S_RELEASED,
 };
 
 struct dhcp6_state {
--- a/src/ipv6.c	Fri Apr 24 12:23:05 2020 +0100
+++ b/src/ipv6.c	Sun Apr 26 17:22:42 2020 +0100
@@ -1635,7 +1635,7 @@
 
 	wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
 	ia->flags |= IPV6_AF_DADCOMPLETED;
-	if (ia->flags & IPV6_AF_DUPLICATED)
+	if (ia->addr_flags & IN6_IFF_DUPLICATED)
 		logwarnx("%s: DAD detected %s", ia->iface->name,
 		    ia->saddr);
 	else if (!wascompleted) {
@@ -1838,16 +1838,13 @@
 			}
 			break;
 		case RTM_NEWADDR:
+			ia->addr_flags = addr->addr_flags;
 			/* Safety - ignore tentative announcements */
-			if (addr->addr_flags &
+			if (ia->addr_flags &
 			    (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE))
 				break;
 			if ((ia->flags & IPV6_AF_DADCOMPLETED) == 0) {
 				found++;
-				if (addr->addr_flags & IN6_IFF_DUPLICATED)
-					ia->flags |= IPV6_AF_DUPLICATED;
-				else
-					ia->flags &= ~IPV6_AF_DUPLICATED;
 				if (ia->dadcallback)
 					ia->dadcallback(ia);
 				/* We need to set this here in-case the
@@ -1890,7 +1887,7 @@
 {
 	struct ipv6_addr *ia = arg;
 
-	if (ia->flags & IPV6_AF_DUPLICATED) {
+	if (ia->addr_flags & IN6_IFF_DUPLICATED) {
 		struct ipv6_addr *ia1;
 		struct timespec tv;
 
--- a/src/ipv6.h	Fri Apr 24 12:23:05 2020 +0100
+++ b/src/ipv6.h	Sun Apr 26 17:22:42 2020 +0100
@@ -216,18 +216,17 @@
 #define	IPV6_AF_STALE		(1U << 2)
 #define	IPV6_AF_ADDED		(1U << 3)
 #define	IPV6_AF_AUTOCONF	(1U << 4)
-#define	IPV6_AF_DUPLICATED	(1U << 5)
-#define	IPV6_AF_DADCOMPLETED	(1U << 6)
-#define	IPV6_AF_DELEGATED	(1U << 7)
-#define	IPV6_AF_DELEGATEDPFX	(1U << 8)
-#define	IPV6_AF_NOREJECT	(1U << 9)
-#define	IPV6_AF_REQUEST		(1U << 10)
-#define	IPV6_AF_STATIC		(1U << 11)
-#define	IPV6_AF_DELEGATEDLOG	(1U << 12)
-#define	IPV6_AF_RAPFX		(1U << 13)
-#define	IPV6_AF_EXTENDED	(1U << 14)
+#define	IPV6_AF_DADCOMPLETED	(1U << 5)
+#define	IPV6_AF_DELEGATED	(1U << 6)
+#define	IPV6_AF_DELEGATEDPFX	(1U << 7)
+#define	IPV6_AF_NOREJECT	(1U << 8)
+#define	IPV6_AF_REQUEST		(1U << 9)
+#define	IPV6_AF_STATIC		(1U << 10)
+#define	IPV6_AF_DELEGATEDLOG	(1U << 11)
+#define	IPV6_AF_RAPFX		(1U << 12)
+#define	IPV6_AF_EXTENDED	(1U << 13)
 #ifdef IPV6_MANAGETEMPADDR
-#define	IPV6_AF_TEMPORARY	(1U << 15)
+#define	IPV6_AF_TEMPORARY	(1U << 14)
 #endif
 
 struct ll_callback {
--- a/src/ipv6nd.c	Fri Apr 24 12:23:05 2020 +0100
+++ b/src/ipv6nd.c	Sun Apr 26 17:22:42 2020 +0100
@@ -927,7 +927,7 @@
 	ifp = ia->iface;
 	wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);
 	ia->flags |= IPV6_AF_DADCOMPLETED;
-	if (ia->flags & IPV6_AF_DUPLICATED) {
+	if (ia->addr_flags & IN6_IFF_DUPLICATED) {
 		ia->dadcounter++;
 		logwarnx("%s: DAD detected %s", ifp->name, ia->saddr);