changeset 5040:c44b319ae036 draft

inet6: Ensure expired routers are cleared after a carrier loss When we lose carrier, mark all RAs as willexpire and add the timeout to expire to it's own queue. On receipt of a matching RA, clear the willexpire flag. When the above timeout occours, set doexpire on all RA's with withexpire and then call the general expirera function. This is needed because expirera can be called at any point.
author Roy Marples <roy@marples.name>
date Thu, 06 Feb 2020 12:54:11 +0000
parents 1e35e845790a
children dac9291f82f0
files src/ipv6nd.c src/ipv6nd.h
diffstat 2 files changed, 37 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/ipv6nd.c	Thu Feb 06 12:50:31 2020 +0000
+++ b/src/ipv6nd.c	Thu Feb 06 12:54:11 2020 +0000
@@ -391,10 +391,8 @@
 	if (state->rsprobes++ < MAX_RTR_SOLICITATIONS)
 		eloop_timeout_add_sec(ifp->ctx->eloop,
 		    RTR_SOLICITATION_INTERVAL, ipv6nd_sendrsprobe, ifp);
-	else {
+	else
 		logwarnx("%s: no IPv6 Routers available", ifp->name);
-		ipv6nd_drop(ifp);
-	}
 }
 
 #ifdef ND6_ADVERTISE
@@ -554,19 +552,13 @@
 {
 	struct interface *ifp = arg;
 	struct ra *rap;
-	struct ipv6_addr *ia;
-	struct timespec now = { .tv_sec = 1 };
 
 	if (ifp->ctx->ra_routers == NULL)
 		return;
 
 	TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
-		if (rap->iface != ifp)
-			continue;
-		rap->acquired = now;
-		TAILQ_FOREACH(ia, &rap->addrs, next) {
-			ia->acquired = now;
-		}
+		if (rap->iface == ifp && rap->willexpire)
+			rap->doexpire = true;
 	}
 	ipv6nd_expirera(ifp);
 }
@@ -574,9 +566,17 @@
 void
 ipv6nd_startexpire(struct interface *ifp)
 {
+	struct ra *rap;
 
-	eloop_timeout_add_sec(ifp->ctx->eloop, RTR_CARRIER_EXPIRE,
-	    ipv6nd_expire, ifp);
+	if (ifp->ctx->ra_routers == NULL)
+		return;
+
+	TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
+		if (rap->iface == ifp)
+			rap->willexpire = true;
+	}
+	eloop_q_timeout_add_sec(ifp->ctx->eloop, ELOOP_IPV6RA_EXPIRE,
+	    RTR_CARRIER_EXPIRE, ipv6nd_expire, ifp);
 }
 
 static int
@@ -607,10 +607,12 @@
 	while ((ra1 = TAILQ_FIRST(ctx->ra_routers)) != NULL) {
 		TAILQ_REMOVE(ctx->ra_routers, ra1, next);
 		TAILQ_FOREACH(ra2, &sorted_routers, next) {
-			if (ra1->iface->metric < ra2->iface->metric)
+			if (ra1->iface->metric > ra2->iface->metric)
 				continue;
 			if (ra1->expired && !ra2->expired)
 				continue;
+			if (ra1->willexpire && !ra2->willexpire)
+				continue;
 			if (ra1->lifetime == 0 && ra2->lifetime != 0)
 				continue;
 			if (!ra1->isreachable && ra2->reachable)
@@ -1137,7 +1139,7 @@
 		rap->retrans = ntohl(nd_ra->nd_ra_retransmit);
 	else
 		rap->retrans = RETRANS_TIMER;
-	rap->expired = false;
+	rap->expired = rap->willexpire = rap->doexpire = false;
 	rap->hasdns = false;
 	rap->isreachable = true;
 	has_address = false;
@@ -1416,7 +1418,8 @@
 
 	if (ifp->ctx->ra_routers) {
 		TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next)
-			if (rap->iface == ifp && !rap->expired &&
+			if (rap->iface == ifp &&
+			    !rap->expired &&
 			    (!lifetime ||rap->lifetime))
 				return true;
 	}
@@ -1424,15 +1427,16 @@
 }
 
 bool
-ipv6nd_hasradhcp(const struct interface *ifp)
+ipv6nd_hasradhcp(const struct interface *ifp, bool managed)
 {
 	const struct ra *rap;
 
 	if (ifp->ctx->ra_routers) {
 		TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
 			if (rap->iface == ifp &&
-			    !rap->expired &&
-			    (rap->flags &(ND_RA_FLAG_MANAGED|ND_RA_FLAG_OTHER)))
+			    !rap->expired && !rap->willexpire &&
+			    ((managed && rap->flags & ND_RA_FLAG_MANAGED) ||
+			    (!managed && rap->flags & ND_RA_FLAG_OTHER)))
 				return true;
 		}
 	}
@@ -1622,7 +1626,7 @@
 		if (rap->lifetime) {
 			elapsed = (uint32_t)eloop_timespec_diff(&now,
 			    &rap->acquired, NULL);
-			if (elapsed > rap->lifetime) {
+			if (elapsed > rap->lifetime || rap->doexpire) {
 				if (!rap->expired) {
 					logwarnx("%s: %s: router expired",
 					    ifp->name, rap->sfrom);
@@ -1643,13 +1647,15 @@
 		TAILQ_FOREACH(ia, &rap->addrs, next) {
 			if (ia->prefix_vltime == 0)
 				continue;
-			if (ia->prefix_vltime == ND6_INFINITE_LIFETIME) {
+			if (ia->prefix_vltime == ND6_INFINITE_LIFETIME &&
+			    !rap->doexpire)
+			{
 				valid = true;
 				continue;
 			}
 			elapsed = (uint32_t)eloop_timespec_diff(&now,
 			    &ia->acquired, NULL);
-			if (elapsed > ia->prefix_vltime) {
+			if (elapsed > ia->prefix_vltime || rap->doexpire) {
 				if (ia->flags & IPV6_AF_ADDED) {
 					logwarnx("%s: expired %s %s",
 					    ia->iface->name,
@@ -1720,6 +1726,10 @@
 
 			if (ltime == 0)
 				continue;
+			if (rap->doexpire) {
+				expired = true;
+				continue;
+			}
 			if (ltime == ND6_INFINITE_LIFETIME) {
 				valid = true;
 				continue;
@@ -1750,7 +1760,8 @@
 		eloop_timeout_add_sec(ifp->ctx->eloop,
 		    next, ipv6nd_expirera, ifp);
 	if (expired) {
-		logwarnx("%s: part of Router Advertisement expired", ifp->name);
+		logwarnx("%s: part of a Router Advertisement expired",
+		    ifp->name);
 		rt_build(ifp->ctx, AF_INET6);
 		script_runreason(ifp, "ROUTERADVERT");
 	}
--- a/src/ipv6nd.h	Thu Feb 06 12:50:31 2020 +0000
+++ b/src/ipv6nd.h	Thu Feb 06 12:54:11 2020 +0000
@@ -54,6 +54,8 @@
 	struct ipv6_addrhead addrs;
 	bool hasdns;
 	bool expired;
+	bool willexpire;
+	bool doexpire;
 	bool isreachable;
 };
 
@@ -114,7 +116,7 @@
 void ipv6nd_expirera(void *arg);
 bool ipv6nd_hasralifetime(const struct interface *, bool);
 #define	ipv6nd_hasra(i)		ipv6nd_hasralifetime((i), false)
-bool ipv6nd_hasradhcp(const struct interface *);
+bool ipv6nd_hasradhcp(const struct interface *, bool);
 void ipv6nd_handleifa(int, struct ipv6_addr *, pid_t);
 int ipv6nd_dadcompleted(const struct interface *);
 void ipv6nd_advertise(struct ipv6_addr *);