dhcpcd-discuss

Re: ICMPv6 RA with router lifetime 0

Roy Marples

Thu Mar 06 10:21:47 2014

Hi

On 04/03/2014 22:30, Maarten de Vries wrote:
I have a set up at home with one host sending ICMPv6 router
advertisements to configure my internal IPv6 network (and actually
also doing DHCPv6, but this is irrelevant for the issue at hand I
believe). However, I don't have a public IPv6 address, so I just want
IPv6 on my internal network, while still configuring a custom prefix
(in the fd00::/8 range) and properly configuring DNS information.
Everything seems to work, but the host sending the RAs becomes the
default gateway for every other host on the network. That default
route is bogus, since the host in question doesn't have a public IPv6
address. I just want IPv6 on my internal network.

So, I read some more, and it would seem that setting the router
lifetime field of the RA to 0 should do exactly what I want: prevent
the host from becoming a default route. RFC4861 says:

The lifetime associated with the default router in units of seconds.
 The field can contain values up to 65535 and receivers should
handle any value, while the sending rules in Section 6 limit the
lifetime to 9000 seconds.  A Lifetime of 0 indicates that the
router is not a default router and SHOULD NOT appear on the default
router list. The Router Lifetime applies only to the router's
usefulness as a default router; it does not apply to information
contained in other message fields or options.  Options that need
time limits for their information include their own lifetime fields.

However, when I set that field to zero, dhcpcd doesn't seem to process
the RA at all and ignores all information, including prefix and dns
information. If I understand the RFC correctly, this behavior is wrong
and the prefix and dns (and other) information should still be
processed. For the moment I "worked around it" by setting the lifetime
to 1, meaning the route becomes invalid after one second. However, the
route does stay in the routing table for one second, so it's quite an
ugly workaround.

Hmmmm, I can't fault your reasoning.
This patch should fix it. I need to do some testing on it first though.
Let me know his it works for you.

Thanks

Roy
Index: ipv6.c
==================================================================
--- ipv6.c
+++ ipv6.c
@@ -980,18 +980,19 @@
 	TAILQ_FOREACH(rap, ctx->ra_routers, next) {
 		if (rap->expired != expired)
 			continue;
 		if (rap->iface->options->options & DHCPCD_IPV6RA_OWN) {
 			TAILQ_FOREACH(addr, &rap->addrs, next) {
-				if ((addr->flags & IPV6_AF_ONLINK) == 0)
+				if (addr->prefix_vltime == 0 ||
+				    (addr->flags & IPV6_AF_ONLINK) == 0)
 					continue;
 				rt = make_prefix(rap->iface, rap, addr);
 				if (rt)
 					TAILQ_INSERT_TAIL(dnr, rt, next);
 			}
 		}
-		if (rap->iface->options->options &
+		if (rap->lifetime && rap->iface->options->options &
 		    (DHCPCD_IPV6RA_OWN | DHCPCD_IPV6RA_OWN_DEFAULT))
 		{
 			rt = make_router(rap);
 			if (rt)
 				TAILQ_INSERT_TAIL(dnr, rt, next);

Index: ipv6nd.c
==================================================================
--- ipv6nd.c
+++ ipv6nd.c
@@ -606,11 +606,11 @@
 				}
 				if (rapap == ap)
 					found = 1;
 			}
 
-			if (wascompleted && found && rap->lifetime) {
+			if (wascompleted && found) {
 				syslog(LOG_DEBUG,
 				    "%s: Router Advertisement DAD completed",
 				    rap->iface->name);
 				if (ipv6nd_scriptrun(rap))
 					return;
@@ -682,15 +682,10 @@
 		    sizeof(rap->from.s6_addr)) == 0)
 			break;
 	}
 
 	nd_ra = (struct nd_router_advert *)icp;
-	/* Don't bother doing anything if we don't know about a router
-	 * expiring */
-	if ((rap == NULL || rap->lifetime == 0)
-	    && nd_ra->nd_ra_router_lifetime == 0)
-		return;
 
 	/* We don't want to spam the log with the fact we got an RA every
 	 * 30 seconds or so, so only spam the log if it's different. */
 	if (rap == NULL || (rap->data_len != len ||
 	     memcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0))
@@ -998,16 +993,12 @@
 		script_runreason(ifp, "TEST");
 		goto handle_flag;
 	}
 	ipv6nd_probeaddrs(&rap->addrs);
 	ipv6_buildroutes(ifp->ctx);
-
-	/* We will get run by the expire function */
-	if (rap->lifetime) {
-		if (ipv6nd_scriptrun(rap))
-			return;
-	}
+	if (ipv6nd_scriptrun(rap))
+		return;
 
 	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
 	eloop_timeout_delete(ifp->ctx->eloop, NULL, rap); /* reachable timer */
 
 	/* If we're owning the RA then we need to try and ensure the
@@ -1198,27 +1189,30 @@
 	timerclear(&next);
 
 	TAILQ_FOREACH_SAFE(rap, ifp->ctx->ipv6->ra_routers, next, ran) {
 		if (rap->iface != ifp)
 			continue;
-		lt.tv_sec = rap->lifetime;
-		lt.tv_usec = 0;
-		timeradd(&rap->received, &lt, &expire);
-		if (rap->lifetime == 0 || timercmp(&now, &expire, >)) {
-			valid = 0;
-			if (!rap->expired) {
-				syslog(LOG_WARNING,
-				    "%s: %s: router expired",
-				    ifp->name, rap->sfrom);
-				rap->expired = expired = 1;
-				ipv6nd_cancelproberouter(rap);
+		if (rap->lifetime) {
+			lt.tv_sec = rap->lifetime;
+			lt.tv_usec = 0;
+			timeradd(&rap->received, &lt, &expire);
+			if (rap->lifetime == 0 || timercmp(&now, &expire, >)) {
+				valid = 0;
+				if (!rap->expired) {
+					syslog(LOG_WARNING,
+					    "%s: %s: router expired",
+					    ifp->name, rap->sfrom);
+					rap->expired = expired = 1;
+					ipv6nd_cancelproberouter(rap);
+				}
+			} else {
+				valid = 1;
+				timersub(&expire, &now, &lt);
+				if (!timerisset(&next) ||
+				    timercmp(&next, &lt, >))
+					next = lt;
 			}
-		} else {
-			valid = 1;
-			timersub(&expire, &now, &lt);
-			if (!timerisset(&next) || timercmp(&next, &lt, >))
-				next = lt;
 		}
 
 		/* Addresses are expired in ipv6ns_probeaddrs
 		 * so that DHCPv6 addresses can be removed also. */
 		TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) {


Follow-Ups:
Re: ICMPv6 RA with router lifetime 0Maarten de Vries
References:
ICMPv6 RA with router lifetime 0Maarten de Vries
Archive administrator: postmaster@marples.name