git: 95b5ff22a93c - stable/13 - netinet6: allow ND entries creation for all directly-reachable destinations.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Fri, 13 Jan 2023 21:25:40 UTC
The branch stable/13 has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=95b5ff22a93c50911f167ad11ffc25d6a9413fc9

commit 95b5ff22a93c50911f167ad11ffc25d6a9413fc9
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2022-08-10 11:51:58 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-01-13 21:24:11 +0000

    netinet6: allow ND entries creation for all directly-reachable
    destinations.
    
    The current assumption is that kernel-handled rtadv prefixes along with
     the interface address prefixes are the only prefixes considered in
     the ND neighbor eligibility code.
    Change this by allowing any non-gatewaye routes to be eligible. This
     will allow DHCPv6-controlled routes to be correctly handled by
     the ND code.
    Refactor nd6_is_new_addr_neighbor() to enable more deterministic
     performance in "found" case and remove non-needed
     V_rt_add_addr_allfibs handling logic.
    
    Reviewed By: kbowling
    Differential Revision: https://reviews.freebsd.org/D23695
    MFC after:      1 month
    
    (cherry picked from commit f998535a66b986f51dd65b5153d1a580d50ddfbe)
---
 sys/netinet6/nd6.c | 90 +++++++++++++-----------------------------------------
 1 file changed, 22 insertions(+), 68 deletions(-)

diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index b772fb74b257..e4d657feed98 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/in_kdtrace.h>
 #include <net/if_llatbl.h>
 #include <netinet/if_ether.h>
+#include <netinet6/in6_fib.h>
 #include <netinet6/in6_var.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
@@ -129,7 +130,7 @@ VNET_DEFINE(int, nd6_recalc_reachtm_interval) = ND6_RECALC_REACHTM_INTERVAL;
 
 int	(*send_sendso_input_hook)(struct mbuf *, struct ifnet *, int, int);
 
-static int nd6_is_new_addr_neighbor(const struct sockaddr_in6 *,
+static bool nd6_is_new_addr_neighbor(const struct sockaddr_in6 *,
 	struct ifnet *);
 static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *);
 static void nd6_slowtimo(void *);
@@ -1225,20 +1226,11 @@ nd6_alloc(const struct in6_addr *addr6, int flags, struct ifnet *ifp)
 }
 
 /*
- * Test whether a given IPv6 address is a neighbor or not, ignoring
- * the actual neighbor cache.  The neighbor cache is ignored in order
- * to not reenter the routing code from within itself.
+ * Test whether a given IPv6 address can be a neighbor.
  */
-static int
+static bool
 nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
 {
-	struct nd_prefix *pr;
-	struct ifaddr *ifa;
-	struct rt_addrinfo info;
-	struct sockaddr_in6 rt_key;
-	const struct sockaddr *dst6;
-	uint64_t genid;
-	int error, fibnum;
 
 	/*
 	 * A link-local address is always a neighbor.
@@ -1262,89 +1254,51 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
 		else
 			return (0);
 	}
+	/* Checking global unicast */
 
-	bzero(&rt_key, sizeof(rt_key));
-	bzero(&info, sizeof(info));
-	info.rti_info[RTAX_DST] = (struct sockaddr *)&rt_key;
+	/* If an address is directly reachable, it is a neigbor */
+	struct nhop_object *nh;
+	nh = fib6_lookup(ifp->if_fib, &addr->sin6_addr, 0, NHR_NONE, 0);
+	if (nh != NULL && nh->nh_aifp == ifp && (nh->nh_flags & NHF_GATEWAY) == 0)
+		return (true);
 
 	/*
-	 * If the address matches one of our addresses,
-	 * it should be a neighbor.
-	 * If the address matches one of our on-link prefixes, it should be a
-	 * neighbor.
+	 * Check prefixes with desired on-link state, as some may be not
+	 * installed in the routing table.
 	 */
+	bool matched = false;
+	struct nd_prefix *pr;
 	ND6_RLOCK();
-restart:
 	LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
 		if (pr->ndpr_ifp != ifp)
 			continue;
-
-		if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) {
-			dst6 = (const struct sockaddr *)&pr->ndpr_prefix;
-
-			/*
-			 * We only need to check all FIBs if add_addr_allfibs
-			 * is unset. If set, checking any FIB will suffice.
-			 */
-			fibnum = V_rt_add_addr_allfibs ? rt_numfibs - 1 : 0;
-			for (; fibnum < rt_numfibs; fibnum++) {
-				genid = V_nd6_list_genid;
-				ND6_RUNLOCK();
-
-				/*
-				 * Restore length field before
-				 * retrying lookup
-				 */
-				rt_key.sin6_len = sizeof(rt_key);
-				error = rib_lookup_info(fibnum, dst6, 0, 0,
-						        &info);
-
-				ND6_RLOCK();
-				if (genid != V_nd6_list_genid)
-					goto restart;
-				if (error == 0)
-					break;
-			}
-			if (error != 0)
-				continue;
-
-			/*
-			 * This is the case where multiple interfaces
-			 * have the same prefix, but only one is installed 
-			 * into the routing table and that prefix entry
-			 * is not the one being examined here.
-			 */
-			if (!IN6_ARE_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,
-			    &rt_key.sin6_addr))
-				continue;
-		}
-
+		if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0)
+			continue;
 		if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr,
 		    &addr->sin6_addr, &pr->ndpr_mask)) {
-			ND6_RUNLOCK();
-			return (1);
+			matched = true;
+			break;
 		}
 	}
 	ND6_RUNLOCK();
+	if (matched)
+		return (true);
 
 	/*
 	 * If the address is assigned on the node of the other side of
 	 * a p2p interface, the address should be a neighbor.
 	 */
 	if (ifp->if_flags & IFF_POINTOPOINT) {
-		struct epoch_tracker et;
+		struct ifaddr *ifa;
 
-		NET_EPOCH_ENTER(et);
 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
 			if (ifa->ifa_addr->sa_family != addr->sin6_family)
 				continue;
 			if (ifa->ifa_dstaddr != NULL &&
 			    sa_equal(addr, ifa->ifa_dstaddr)) {
-				NET_EPOCH_EXIT(et);
-				return 1;
+				return (true);
 			}
 		}
-		NET_EPOCH_EXIT(et);
 	}
 
 	/*