changeset 4175:2a14528ebe96 draft

DHCPv6: listen on all IPv6 addresses for active interface Simplify the process of selecting addresses to listen for DHCPv6 replies on by listening on all addresses for active interfaces when not in master mode. Always send from a socket not bound to an address to ensure unicast in non master mode works fine. The downside of this approach is that we no longer send from the DHCPv6 client port, but this seems to work fine, at least with ISC DHCPd.
author Roy Marples <roy@marples.name>
date Wed, 25 Oct 2017 14:56:23 +0100
parents 79133872da0b
children 612d80156afd
files src/dhcp6.c src/ipv6.c src/ipv6.h
diffstat 3 files changed, 32 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/src/dhcp6.c	Wed Oct 25 11:45:25 2017 +0100
+++ b/src/dhcp6.c	Wed Oct 25 14:56:23 2017 +0100
@@ -1358,11 +1358,6 @@
 	if (!wascompleted) {
 		ifp = ia->iface;
 
-		/* If not in master mode, we need to listen on each address
-		 * so we can receive unicast a message. */
-		if (ia->dhcp6_fd == -1 && !(ifp->ctx->options & DHCPCD_MASTER))
-			dhcp6_listen(ifp->ctx, ia);
-
 		state = D6_STATE(ifp);
 		if (state->state == DH6S_BOUND ||
 		    state->state == DH6S_DELEGATED)
@@ -1711,35 +1706,6 @@
 }
 
 static void
-dhcp6_addaddrs(struct interface *ifp)
-{
-	struct dhcp6_state *state = D6_STATE(ifp);
-	struct ipv6_addr *ia;
-
-	ipv6_addaddrs(&state->addrs);
-
-	/* If not in master mode, we need to listen on each address
-	 * so we can receive unicast a message. */
-	if (ifp->ctx->options & DHCPCD_MASTER)
-		return;
-
-	state = D6_STATE(ifp);
-	TAILQ_FOREACH(ia, &state->addrs, next) {
-		if (ia->prefix_vltime == 0 ||
-		    ia->flags & IPV6_AF_STALE ||
-		    ia->addr_flags & IN6_IFF_NOTUSEABLE)
-			continue;
-		if (ia->dhcp6_fd != -1)
-			continue;
-		ia->dhcp6_fd = dhcp6_listen(ifp->ctx, ia);
-		if (ia->dhcp6_fd == -1)
-			continue;
-		eloop_event_add(ifp->ctx->eloop,
-		    ia->dhcp6_fd, dhcp6_recvaddr, ia);
-	}
-}
-
-static void
 dhcp6_startexpire(void *arg)
 {
 	struct interface *ifp;
@@ -2736,7 +2702,10 @@
 				break;
 		}
 		if (k && !carrier_warned) {
-			dhcp6_addaddrs(ifd);
+			struct dhcp6_state *s = D6_STATE(ifd);
+
+			ipv6_addaddrs(&s->addrs);
+
 			/*
 			 * Can't add routes here because that will trigger
 			 * interface sorting which may break the current
@@ -3040,7 +3009,7 @@
 			eloop_timeout_add_sec(ifp->ctx->eloop,
 			    (time_t)state->expire, dhcp6_startdiscover, ifp);
 
-		dhcp6_addaddrs(ifp);
+		ipv6_addaddrs(&state->addrs);
 		dhcp6_deprecateaddrs(&state->addrs);
 
 		if (state->state == DH6S_INFORMED)
@@ -3481,10 +3450,13 @@
 #ifdef BSD
 	sa.sin6_len = sizeof(sa);
 #endif
+
 	if (ia != NULL) {
 		memcpy(&sa.sin6_addr, &ia->addr, sizeof(sa.sin6_addr));
 		sa.sin6_scope_id = ia->iface->index;
-	}
+	} else if (!(ctx->options & DHCPCD_MASTER))
+		/* This socket is only used for sending. */
+		return s;
 
 	if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)
 		goto errexit;
@@ -3494,12 +3466,11 @@
 		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
 		    &n, sizeof(n)) == -1)
 			goto errexit;
-	}
-
-	if (ia != NULL) {
+	} else {
 		ia->dhcp6_fd = s;
 		eloop_event_add(ctx->eloop, s, dhcp6_recvaddr, ia);
 	}
+
 	return s;
 
 errexit:
@@ -3512,28 +3483,13 @@
 static int
 dhcp6_open(struct dhcpcd_ctx *ctx)
 {
-	struct ipv6_addr *ia = NULL;
-	int s;
-
-	if (!(ctx->options & DHCPCD_MASTER)) {
-		/* Bind to the link-local address to allow more than one
-		 * DHCPv6 client to work. */
-		struct interface *ifp;
-
-		TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-			if (!ifp->active)
-				continue;
-			ia = ipv6_linklocal(ifp);
-			if (ia != NULL)
-				break;
-		}
-	}
-	s = dhcp6_listen(ctx, ia);
-	ctx->dhcp6_fd = s;
-	if (s != -1 && ia == NULL)
-		eloop_event_add(ctx->eloop, s, dhcp6_recvctx, ctx);
-
-	return s;
+
+	/* Open an unbound socket to send from. */
+	ctx->dhcp6_fd = dhcp6_listen(ctx, NULL);
+	if (ctx->dhcp6_fd != -1 && (ctx->options & DHCPCD_MASTER))
+		eloop_event_add(ctx->eloop, ctx->dhcp6_fd, dhcp6_recvctx, ctx);
+
+	return ctx->dhcp6_fd;
 }
 
 #ifndef SMALL
@@ -3809,6 +3765,14 @@
 {
 	struct dhcp6_state *state;
 
+	/* If not running in master mode, listen to this address */
+	if (cmd == RTM_NEWADDR &&
+	    !(ia->addr_flags & IN6_IFF_NOTUSEABLE) &&
+	    ia->iface->active == IF_ACTIVE_USER &&
+	    !(ia->iface->ctx->options & DHCPCD_MASTER) &&
+	    ia->dhcp6_fd == -1)
+		dhcp6_listen(ia->iface->ctx, ia);
+
 	if ((state = D6_STATE(ia->iface)) != NULL)
 		ipv6_handleifa_addrs(cmd, &state->addrs, ia);
 }
--- a/src/ipv6.c	Wed Oct 25 11:45:25 2017 +0100
+++ b/src/ipv6.c	Wed Oct 25 14:56:23 2017 +0100
@@ -1103,26 +1103,10 @@
 		break;
 	case RTM_NEWADDR:
 		if (ia == NULL) {
-			char buf[INET6_ADDRSTRLEN];
-			const char *cbp;
-
-			if ((ia = calloc(1, sizeof(*ia))) == NULL) {
-				logerr(__func__);
-				break;
-			}
+			ia = ipv6_newaddr(ifp, addr, prefix_len, 0);
 #ifdef ALIAS_ADDR
 			strlcpy(ia->alias, ifname, sizeof(ia->alias));
 #endif
-			ia->iface = ifp;
-			ia->addr = *addr;
-			ia->prefix_len = prefix_len;
-			ipv6_makeprefix(&ia->prefix, &ia->addr,
-			    ia->prefix_len);
-			cbp = inet_ntop(AF_INET6, &addr->s6_addr,
-			    buf, sizeof(buf));
-			if (cbp)
-				snprintf(ia->saddr, sizeof(ia->saddr),
-				    "%s/%d", cbp, prefix_len);
 			if (if_getlifetime6(ia) == -1) {
 				/* No support or address vanished.
 				 * Either way, just set a deprecated
@@ -1190,10 +1174,8 @@
 	}
 
 	if (ia != NULL) {
-		if (!IN6_IS_ADDR_LINKLOCAL(&ia->addr)) {
-			ipv6nd_handleifa(cmd, ia);
-			dhcp6_handleifa(cmd, ia);
-		}
+		ipv6nd_handleifa(cmd, ia);
+		dhcp6_handleifa(cmd, ia);
 
 		/* Done with the ia now, so free it. */
 		if (cmd == RTM_DELADDR)
@@ -1453,8 +1435,8 @@
 }
 
 struct ipv6_addr *
-ipv6_newaddr(struct interface *ifp, struct in6_addr *addr, uint8_t prefix_len,
-    unsigned int flags)
+ipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,
+    uint8_t prefix_len, unsigned int flags)
 {
 	struct ipv6_addr *ia;
 	char buf[INET6_ADDRSTRLEN];
--- a/src/ipv6.h	Wed Oct 25 11:45:25 2017 +0100
+++ b/src/ipv6.h	Wed Oct 25 14:56:23 2017 +0100
@@ -250,7 +250,7 @@
     const struct in6_addr *);
 #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,
+struct ipv6_addr *ipv6_newaddr(struct interface *, const struct in6_addr *, uint8_t,
     unsigned int);
 void ipv6_freeaddr(struct ipv6_addr *);
 void ipv6_freedrop(struct interface *, int);