changeset 4532:23730414d0a6 draft

Merge branch 'master' into rbtree
author Roy Marples <roy@marples.name>
date Fri, 14 Jun 2019 15:22:43 +0100
parents 578ac745c627 (current diff) a44c7ef76f2d (diff)
children 33a7edb3a44a
files src/dhcp6.c src/dhcpcd.c src/ipv6.c src/ipv6nd.c
diffstat 7 files changed, 128 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/hooks/20-resolv.conf	Fri Jun 07 17:15:00 2019 +0100
+++ b/hooks/20-resolv.conf	Fri Jun 14 15:22:43 2019 +0100
@@ -69,30 +69,26 @@
 }
 
 # Extract any ND DNS options from the RA
-# For now, we ignore the lifetime of the DNS options unless they
-# are absent or zero.
-# In this case they are removed from consideration.
-# See draft-gont-6man-slaac-dns-config-issues-01 for issues
-# regarding DNS option lifetime in ND messages.
+# Obey the lifetimes
 eval_nd_dns()
 {
-	eval ltime=\$nd${i}_rdnss${j}_lifetime
-	if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
-		rdnss=
-	else
+
+	eval rdnsstime=\$nd${i}_rdnss${j}_lifetime
+	[ -z "$rdnsstime" ] && return 1
+	ltime=$(($rdnsstime - $offset))
+	if [ "$ltime" -gt 0 ]; then
 		eval rdnss=\$nd${i}_rdnss${j}_servers
-	fi
-	eval ltime=\$nd${i}_dnssl${j}_lifetime
-	if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
-		dnssl=
-	else
-		eval dnssl=\$nd${i}_dnssl${j}_search
+		[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
 	fi
 
-	[ -z "${rdnss}${dnssl}" ] && return 1
+	eval dnssltime=\$nd${i}_dnssl${j}_lifetime
+	[ -z "$dnssltime" ] && return 1
+	ltime=$(($dnssltime - $offset))
+	if [ "$ltime" -gt 0 ]; then
+		eval dnssl=\$nd${i}_dnssl${j}_search
+		[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
+	fi
 
-	[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
-	[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
 	j=$(($j + 1))
 	return 0
 }
@@ -106,12 +102,16 @@
 	i=1
 	j=1
 	while true; do
+		eval acquired=\$nd${i}_acquired
+		[ -z "$acquired" ] && break
+		eval now=\$nd${i}_now
+		[ -z "$now" ] && break
+		offset=$(($now - $acquired))
 		while true; do
 			eval_nd_dns || break
 		done
 		i=$(($i + 1))
 		j=1
-		eval_nd_dns || break
 	done
 	[ -n "$new_rdnss" ] && \
 	    new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
--- a/hooks/30-hostname	Fri Jun 07 17:15:00 2019 +0100
+++ b/hooks/30-hostname	Fri Jun 14 15:22:43 2019 +0100
@@ -80,7 +80,7 @@
 	set_hostname_vars
 
 	if [ -n "$old_fqdn" ]; then
-		if ${hfqdn} || ! ${hsort}; then
+		if ${hfqdn} || ! ${hshort}; then
 			[ "$hostname" = "$old_fqdn" ]
 		else
 			[ "$hostname" = "${old_fqdn%%.*}" ]
--- a/src/dhcp6.c	Fri Jun 07 17:15:00 2019 +0100
+++ b/src/dhcp6.c	Fri Jun 14 15:22:43 2019 +0100
@@ -3918,21 +3918,6 @@
 }
 
 void
-dhcp6_dropnondelegates(struct interface *ifp)
-{
-
-#ifndef SMALL
-	if (dhcp6_hasprefixdelegation(ifp))
-		return;
-#endif
-	if (D6_CSTATE(ifp) == NULL)
-		return;
-
-	loginfox("%s: dropping DHCPv6 due to no valid routers", ifp->name);
-	dhcp6_drop(ifp, "EXPIRE6");
-}
-
-void
 dhcp6_abort(struct interface *ifp)
 {
 	struct dhcp6_state *state;
--- a/src/dhcp6.h	Fri Jun 07 17:15:00 2019 +0100
+++ b/src/dhcp6.h	Fri Jun 14 15:22:43 2019 +0100
@@ -235,7 +235,6 @@
 int dhcp6_dadcompleted(const struct interface *);
 void dhcp6_abort(struct interface *);
 void dhcp6_drop(struct interface *, const char *);
-void dhcp6_dropnondelegates(struct interface *ifp);
 int dhcp6_dump(struct interface *);
 #endif /* DHCP6 */
 
--- a/src/dhcpcd.c	Fri Jun 07 17:15:00 2019 +0100
+++ b/src/dhcpcd.c	Fri Jun 14 15:22:43 2019 +0100
@@ -738,9 +738,6 @@
 #ifdef INET
 				dhcp_abort(ifp);
 #endif
-#ifdef INET6
-				ipv6nd_expire(ifp, 0);
-#endif
 #ifdef DHCP6
 				dhcp6_abort(ifp);
 #endif
--- a/src/ipv6.c	Fri Jun 07 17:15:00 2019 +0100
+++ b/src/ipv6.c	Fri Jun 14 15:22:43 2019 +0100
@@ -2235,15 +2235,14 @@
 }
 
 static int
-inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx, int expired,
-    bool *have_default)
+inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
 {
 	struct rt *rt;
 	struct ra *rap;
 	const struct ipv6_addr *addr;
 
 	TAILQ_FOREACH(rap, ctx->ra_routers, next) {
-		if (rap->expired != expired)
+		if (rap->expired)
 			continue;
 		TAILQ_FOREACH(addr, &rap->addrs, next) {
 			if (addr->prefix_vltime == 0)
@@ -2254,14 +2253,13 @@
 				rt_proto_add(routes, rt);
 			}
 		}
-		if (rap->lifetime) {
-			rt = inet6_makerouter(rap);
-			if (rt) {
-				rt->rt_dflags |= RTDF_RA;
-				if (rt_proto_add(routes, rt) && have_default)
-					*have_default = true;
-			}
-		}
+		if (rap->lifetime == 0)
+			continue;
+		rt = inet6_makerouter(rap);
+		if (rt == NULL)
+			continue;
+		rt->rt_dflags |= RTDF_RA;
+		rt_proto_add(routes, rt);
 	}
 	return 0;
 }
@@ -2295,15 +2293,13 @@
 bool
 inet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
 {
-	bool have_default;
 
 	/* Should static take priority? */
 	if (inet6_staticroutes(routes, ctx) == -1)
 		return false;
 
 	/* First add reachable routers and their prefixes */
-	have_default = false;
-	if (inet6_raroutes(routes, ctx, 0, &have_default) == -1)
+	if (inet6_raroutes(routes, ctx) == -1)
 		return false;
 
 #ifdef DHCP6
@@ -2316,21 +2312,5 @@
 		return false;
 #endif
 
-#ifdef HAVE_ROUTE_METRIC
-	/* If we have an unreachable router, we really do need to remove the
-	 * route to it beause it could be a lower metric than a reachable
-	 * router. Of course, we should at least have some routers if all
-	 * are unreachable. */
-	if (!have_default) {
-#endif
-	/* Add our non-reachable routers and prefixes
-	 * Unsure if this is needed, but it's a close match to kernel
-	 * behaviour */
-		if (inet6_raroutes(routes, ctx, 1, NULL) == -1)
-			return false;
-#ifdef HAVE_ROUTE_METRIC
-	}
-#endif
-
 	return true;
 }
--- a/src/ipv6nd.c	Fri Jun 07 17:15:00 2019 +0100
+++ b/src/ipv6nd.c	Fri Jun 14 15:22:43 2019 +0100
@@ -105,6 +105,9 @@
 #define RTPREF_RESERVED	(-2)
 #define RTPREF_INVALID	(-3)	/* internal */
 
+#define	EXPIRED_MAX	5	/* Remember 5 expired routers to avoid
+				   logspam. */
+
 #define MIN_RANDOM_FACTOR	500				/* millisecs */
 #define MAX_RANDOM_FACTOR	1500				/* millisecs */
 #define MIN_RANDOM_FACTOR_U	MIN_RANDOM_FACTOR * 1000	/* usecs */
@@ -382,9 +385,6 @@
 	else {
 		logwarnx("%s: no IPv6 Routers available", ifp->name);
 		ipv6nd_drop(ifp);
-#ifdef DHCP6
-		dhcp6_dropnondelegates(ifp);
-#endif
 	}
 }
 
@@ -1231,6 +1231,10 @@
 			break;
 
 		case ND_OPT_MTU:
+			if (len < sizeof(mtu)) {
+				logerrx("%s: short MTU option", ifp->name);
+				break;
+			}
 			memcpy(&mtu, p, sizeof(mtu));
 			mtu.nd_opt_mtu_mtu = ntohl(mtu.nd_opt_mtu_mtu);
 			if (mtu.nd_opt_mtu_mtu < IPV6_MMTU) {
@@ -1242,6 +1246,10 @@
 			break;
 
 		case ND_OPT_RDNSS:
+			if (len < sizeof(rdnss)) {
+				logerrx("%s: short RDNSS option", ifp->name);
+				break;
+			}
 			memcpy(&rdnss, p, sizeof(rdnss));
 			if (rdnss.nd_opt_rdnss_lifetime &&
 			    rdnss.nd_opt_rdnss_len > 1)
@@ -1478,11 +1486,12 @@
 		 * from the prefix information options as well. */
 		j = 0;
 		TAILQ_FOREACH(ia, &rap->addrs, next) {
-			if (!(ia->flags & IPV6_AF_AUTOCONF)
+			if (!(ia->flags & IPV6_AF_AUTOCONF) ||
 #ifdef IPV6_AF_TEMPORARY
-			    || ia->flags & IPV6_AF_TEMPORARY
+			    ia->flags & IPV6_AF_TEMPORARY ||
 #endif
-			    )
+			    !(ia->flags & IPV6_AF_ADDED) ||
+			    ia->prefix_vltime == 0)
 				continue;
 			j++;
 			if (env) {
@@ -1520,9 +1529,16 @@
 	struct timespec now, lt, expire, next;
 	bool expired, valid, validone;
 	struct ipv6_addr *ia;
-#ifdef DHCP6
-	bool anyvalid = false;
+	size_t len, olen;
+	uint8_t *p;
+	struct nd_opt_hdr ndo;
+#if 0
+	struct nd_opt_prefix_info pi;
 #endif
+	struct nd_opt_dnssl dnssl;
+	struct nd_opt_rdnss rdnss;
+	uint32_t ltime;
+	size_t nexpired = 0;
 
 	ifp = arg;
 	clock_gettime(CLOCK_MONOTONIC, &now);
@@ -1537,8 +1553,7 @@
 			lt.tv_sec = (time_t)rap->lifetime;
 			lt.tv_nsec = 0;
 			timespecadd(&rap->acquired, &lt, &expire);
-			if (rap->lifetime == 0 || timespeccmp(&now, &expire, >))
-			{
+			if (timespeccmp(&now, &expire, >)) {
 				if (!rap->expired) {
 					logwarnx("%s: %s: router expired",
 					    ifp->name, rap->sfrom);
@@ -1589,34 +1604,90 @@
 			}
 		}
 
-		/* XXX FixMe!
-		 * We need to extract the lifetime from each option and check
-		 * if that has expired or not.
-		 * If it has, zero the option out in the returned data. */
+		/* Work out expiry for ND options */
+		len = rap->data_len - sizeof(struct nd_router_advert);
+		for (p = rap->data + sizeof(struct nd_router_advert);
+		    len >= sizeof(ndo);
+		    p += olen, len -= olen)
+		{
+			memcpy(&ndo, p, sizeof(ndo));
+			olen = (size_t)(ndo.nd_opt_len * 8);
+			if (olen > len) {
+				errno =	EINVAL;
+				break;
+			}
+
+			if (has_option_mask(rap->iface->options->nomasknd,
+			    ndo.nd_opt_type))
+				continue;
 
-		/* No valid lifetimes are left on the RA, so we might
-		 * as well punt it. */
-		if (!valid && !validone)
+			switch (ndo.nd_opt_type) {
+			/* Prefix info is already checked in the above loop. */
+#if 0
+			case ND_OPT_PREFIX_INFORMATION:
+				if (len < sizeof(pi))
+					break;
+				memcpy(&pi, p, sizeof(pi));
+				ltime = pi.nd_opt_pi_valid_time;
+				break;
+#endif
+			case ND_OPT_DNSSL:
+				if (len < sizeof(dnssl))
+					break;
+				memcpy(&dnssl, p, sizeof(dnssl));
+				ltime = dnssl.nd_opt_dnssl_lifetime;
+				break;
+			case ND_OPT_RDNSS:
+				if (len < sizeof(rdnss))
+					break;
+				memcpy(&rdnss, p, sizeof(rdnss));
+				ltime = rdnss.nd_opt_rdnss_lifetime;
+				break;
+			default:
+				continue;
+			}
+
+			if (ltime == 0)
+				continue;
+			if (ltime == ND6_INFINITE_LIFETIME) {
+				validone = true;
+				continue;
+			}
+
+			lt.tv_sec = (time_t)ntohl(ltime);
+			lt.tv_nsec = 0;
+			timespecadd(&rap->acquired, &lt, &expire);
+			if (timespeccmp(&now, &expire, >)) {
+				expired = true;
+				continue;
+			}
+
+			timespecsub(&expire, &now, &lt);
+			if (!timespecisset(&next) ||
+			    timespeccmp(&next, &lt, >))
+			{
+				next = lt;
+				validone = true;
+			}
+		}
+
+		if (valid || validone)
+			continue;
+
+		/* Router has expired. Let's not keep a lot of them.
+		 * We should work out if all the options have expired .... */
+		if (++nexpired > EXPIRED_MAX)
 			ipv6nd_free_ra(rap);
-#ifdef DHCP6
-		else
-			anyvalid = true;
-#endif
 	}
 
 	if (timespecisset(&next))
 		eloop_timeout_add_tv(ifp->ctx->eloop,
 		    &next, ipv6nd_expirera, ifp);
 	if (expired) {
+		logwarnx("%s: part of Router Advertisement expired", ifp->name);
 		rt_build(ifp->ctx, AF_INET6);
 		script_runreason(ifp, "ROUTERADVERT");
 	}
-
-#ifdef DHCP6
-	/* No valid routers? Kill any DHCPv6. */
-	if (!anyvalid)
-		dhcp6_dropnondelegates(ifp);
-#endif
 }
 
 void