Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 14 Sep 2015 19:17:26 +0000 (UTC)
From:      Eric van Gyzen <vangyzen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r287798 - in head/sys: net netinet6
Message-ID:  <201509141917.t8EJHQIg026210@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: vangyzen
Date: Mon Sep 14 19:17:25 2015
New Revision: 287798
URL: https://svnweb.freebsd.org/changeset/base/287798

Log:
  Fix the handling of IPv6 On-Link Redirects.
  
  On receipt of a redirect message, install an interface route for the
  redirected destination.  On removal of the corresponding Neighbor Cache
  entry, remove the interface route.
  
  This requires changes in rtredirect_fib() to cope with an AF_LINK
  address for the gateway and with the absence of RTF_GATEWAY.
  
  This fixes the "Redirected On-Link" test cases in the Tahi IPv6 Ready Logo
  Phase 2 test suite.
  
  Unrelated to the above, fix a recursion on the radix node head lock
  triggered by the Tahi Redirected to Alternate Router test cases.
  
  When I first wrote this patch in October 2012, all Section 2
  (Neighbor Discovery) test cases passed on 10-CURRENT, 9-STABLE,
  and 8-STABLE.  cem@ recently rebased the 10.x patch onto head and reported
  that it passes Tahi.  (Thanks!)
  
  These other test cases also passed in 2012:
  
  * the RTF_MODIFIED case, with IPv4 and IPv6 (using a
    RTF_HOST|RTF_GATEWAY route for the destination)
  
  * the redirected-to-self case, with IPv4 and IPv6
  
  * a valid IPv4 redirect
  
  All testing in 2012 was done with WITNESS and INVARIANTS.
  
  Tested by:    EMC / Isilon Storage Division via Conrad Meyer (cem) in 2015,
                Mark Kelley <mark_kelley@dell.com> in 2012,
                TC Telkamp <terence_telkamp@dell.com> in 2012
  PR:           152791
  Reviewed by:  melifaro (current rev), bz (earlier rev)
  Approved by:  kib (mentor)
  MFC after:    1 month
  Relnotes:     yes
  Sponsored by: Dell Inc.
  Differential Revision: https://reviews.freebsd.org/D3602

Modified:
  head/sys/net/if_llatbl.h
  head/sys/net/route.c
  head/sys/netinet6/icmp6.c
  head/sys/netinet6/nd6.c
  head/sys/netinet6/nd6_rtr.c

Modified: head/sys/net/if_llatbl.h
==============================================================================
--- head/sys/net/if_llatbl.h	Mon Sep 14 18:59:01 2015	(r287797)
+++ head/sys/net/if_llatbl.h	Mon Sep 14 19:17:25 2015	(r287798)
@@ -183,6 +183,7 @@ MALLOC_DECLARE(M_LLTABLE);
 #define	LLE_STATIC	0x0002	/* entry is static */
 #define	LLE_IFADDR	0x0004	/* entry is interface addr */
 #define	LLE_VALID	0x0008	/* ll_addr is valid */
+#define	LLE_REDIRECT	0x0010	/* installed by redirect; has host rtentry */
 #define	LLE_PUB		0x0020	/* publish entry ??? */
 #define	LLE_LINKED	0x0040	/* linked to lookup structure */
 /* LLE request flags */

Modified: head/sys/net/route.c
==============================================================================
--- head/sys/net/route.c	Mon Sep 14 18:59:01 2015	(r287797)
+++ head/sys/net/route.c	Mon Sep 14 19:17:25 2015	(r287798)
@@ -584,13 +584,20 @@ rtredirect_fib(struct sockaddr *dst,
 	 * we have a routing loop, perhaps as a result of an interface
 	 * going down recently.
 	 */
-	if (!(flags & RTF_DONE) && rt &&
-	     (!sa_equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
-		error = EINVAL;
-	else if (ifa_ifwithaddr_check(gateway))
+	if (!(flags & RTF_DONE) && rt) {
+		if (!sa_equal(src, rt->rt_gateway)) {
+			error = EINVAL;
+			goto done;
+		}
+		if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
+			error = EINVAL;
+			goto done;
+		}
+	}
+	if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) {
 		error = EHOSTUNREACH;
-	if (error)
 		goto done;
+	}
 	/*
 	 * Create a new entry if we just got back a wildcard entry
 	 * or the lookup failed.  This is necessary for hosts
@@ -613,7 +620,7 @@ rtredirect_fib(struct sockaddr *dst,
 			rt0 = rt;
 			rt = NULL;
 		
-			flags |=  RTF_GATEWAY | RTF_DYNAMIC;
+			flags |= RTF_DYNAMIC;
 			bzero((caddr_t)&info, sizeof(info));
 			info.rti_info[RTAX_DST] = dst;
 			info.rti_info[RTAX_GATEWAY] = gateway;
@@ -640,6 +647,8 @@ rtredirect_fib(struct sockaddr *dst,
 			 * Smash the current notion of the gateway to
 			 * this destination.  Should check about netmask!!!
 			 */
+			if ((flags & RTF_GATEWAY) == 0)
+				rt->rt_flags &= ~RTF_GATEWAY;
 			rt->rt_flags |= RTF_MODIFIED;
 			flags |= RTF_MODIFIED;
 			stat = &V_rtstat.rts_newgateway;
@@ -653,7 +662,8 @@ rtredirect_fib(struct sockaddr *dst,
 			gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED);
 			RADIX_NODE_HEAD_UNLOCK(rnh);
 			EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst);
-			RTFREE_LOCKED(gwrt);
+			if (gwrt)
+				RTFREE_LOCKED(gwrt);
 		}
 	} else
 		error = EHOSTUNREACH;

Modified: head/sys/netinet6/icmp6.c
==============================================================================
--- head/sys/netinet6/icmp6.c	Mon Sep 14 18:59:01 2015	(r287797)
+++ head/sys/netinet6/icmp6.c	Mon Sep 14 19:17:25 2015	(r287798)
@@ -2434,27 +2434,39 @@ icmp6_redirect_input(struct mbuf *m, int
 	nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
 	    is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
 
-	if (!is_onlink) {	/* better router case.  perform rtredirect. */
-		/* perform rtredirect */
+	/*
+	 * Install a gateway route in the better-router case or an interface
+	 * route in the on-link-destination case.
+	 */
+	{
 		struct sockaddr_in6 sdst;
 		struct sockaddr_in6 sgw;
 		struct sockaddr_in6 ssrc;
+		struct sockaddr *gw;
+		int rt_flags;
 		u_int fibnum;
 
 		bzero(&sdst, sizeof(sdst));
-		bzero(&sgw, sizeof(sgw));
 		bzero(&ssrc, sizeof(ssrc));
-		sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
-		sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
-			sizeof(struct sockaddr_in6);
-		bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
+		sdst.sin6_family = ssrc.sin6_family = AF_INET6;
+		sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6);
 		bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
 		bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
+		rt_flags = RTF_HOST;
+		if (is_router) {
+			bzero(&sgw, sizeof(sgw));
+			sgw.sin6_family = AF_INET6;
+			sgw.sin6_len = sizeof(struct sockaddr_in6);
+			bcopy(&redtgt6, &sgw.sin6_addr,
+				sizeof(struct in6_addr));
+			gw = (struct sockaddr *)&sgw;
+			rt_flags |= RTF_GATEWAY;
+		} else
+			gw = ifp->if_addr->ifa_addr;
 		for (fibnum = 0; fibnum < rt_numfibs; fibnum++)
-			in6_rtredirect((struct sockaddr *)&sdst,
-			    (struct sockaddr *)&sgw, (struct sockaddr *)NULL,
-			    RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc,
-			    fibnum);
+			in6_rtredirect((struct sockaddr *)&sdst, gw,
+			    (struct sockaddr *)NULL, rt_flags,
+			    (struct sockaddr *)&ssrc, fibnum);
 	}
 	/* finally update cached route in each socket via pfctlinput */
     {

Modified: head/sys/netinet6/nd6.c
==============================================================================
--- head/sys/netinet6/nd6.c	Mon Sep 14 18:59:01 2015	(r287797)
+++ head/sys/netinet6/nd6.c	Mon Sep 14 19:17:25 2015	(r287798)
@@ -132,6 +132,7 @@ static void nd6_setmtu0(struct ifnet *, 
 static void nd6_slowtimo(void *);
 static int regen_tmpaddr(struct in6_ifaddr *);
 static struct llentry *nd6_free(struct llentry *, int);
+static void nd6_free_redirect(const struct llentry *);
 static void nd6_llinfo_timer(void *);
 static void clear_llinfo_pqueue(struct llentry *);
 static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
@@ -1223,6 +1224,13 @@ nd6_free(struct llentry *ln, int gc)
 			defrouter_select();
 		}
 
+		/*
+		 * If this entry was added by an on-link redirect, remove the
+		 * corresponding host route.
+		 */
+		if (ln->la_flags & LLE_REDIRECT)
+			nd6_free_redirect(ln);
+
 		if (ln->ln_router || dr)
 			LLE_WLOCK(ln);
 	}
@@ -1256,6 +1264,36 @@ nd6_free(struct llentry *ln, int gc)
 }
 
 /*
+ * Remove the rtentry for the given llentry,
+ * both of which were installed by a redirect.
+ */
+static void
+nd6_free_redirect(const struct llentry *ln)
+{
+	int fibnum;
+	struct rtentry *rt;
+	struct radix_node_head *rnh;
+	struct sockaddr_in6 sin6;
+
+	lltable_fill_sa_entry(ln, (struct sockaddr *)&sin6);
+	for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
+		rnh = rt_tables_get_rnh(fibnum, AF_INET6);
+		if (rnh == NULL)
+			continue;
+
+		RADIX_NODE_HEAD_LOCK(rnh);
+		rt = in6_rtalloc1((struct sockaddr *)&sin6, 0,
+		    RTF_RNH_LOCKED, fibnum);
+		if (rt) {
+			if (rt->rt_flags == (RTF_UP | RTF_HOST | RTF_DYNAMIC))
+				rt_expunge(rnh, rt);
+			RTFREE_LOCKED(rt);
+		}
+		RADIX_NODE_HEAD_UNLOCK(rnh);
+	}
+}
+
+/*
  * Upper-layer reachability hint for Neighbor Unreachability Detection.
  *
  * XXX cost-effective methods?
@@ -1746,8 +1784,11 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
 		 */
 		if (code == ND_REDIRECT_ROUTER)
 			ln->ln_router = 1;
-		else if (is_newentry) /* (6-7) */
-			ln->ln_router = 0;
+		else {
+			if (is_newentry) /* (6-7) */
+				ln->ln_router = 0;
+			ln->la_flags |= LLE_REDIRECT;
+		}
 		break;
 	case ND_ROUTER_SOLICIT:
 		/*

Modified: head/sys/netinet6/nd6_rtr.c
==============================================================================
--- head/sys/netinet6/nd6_rtr.c	Mon Sep 14 18:59:01 2015	(r287797)
+++ head/sys/netinet6/nd6_rtr.c	Mon Sep 14 19:17:25 2015	(r287798)
@@ -2105,7 +2105,7 @@ rt6_deleteroute(struct rtentry *rt, void
 		return (0);
 
 	return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
-	    rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum));
+	    rt_mask(rt), rt->rt_flags | RTF_RNH_LOCKED, NULL, rt->rt_fibnum));
 #undef SIN6
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201509141917.t8EJHQIg026210>