Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 8 Dec 2015 10:50:03 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r291993 - in head/sys: conf net netinet netinet6 netpfil/ipfw
Message-ID:  <201512081050.tB8Ao3XG042975@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Tue Dec  8 10:50:03 2015
New Revision: 291993
URL: https://svnweb.freebsd.org/changeset/base/291993

Log:
  Merge helper fib* functions used for basic lookups.
  
  Vast majority of rtalloc(9) users require only basic info from
  route table (e.g. "does the rtentry interface match with the interface
    I have?". "what is the MTU?", "Give me the IPv4 source address to use",
    etc..).
  Instead of hand-rolling lookups, checking if rtentry is up, valid,
    dealing with IPv6 mtu, finding "address" ifp (almost never done right),
    provide easy-to-use API hiding all the complexity and returning the
    needed info into small on-stack structure.
  
  This change also helps hiding route subsystem internals (locking, direct
    rtentry accesses).
  Additionaly, using this API improves lookup performance since rtentry is not
    locked.
  (This is safe, since all the rtentry changes happens under both radix WLOCK
    and rtentry WLOCK).
  
  Sponsored by:	Yandex LLC

Added:
  head/sys/netinet/in_fib.c   (contents, props changed)
  head/sys/netinet/in_fib.h   (contents, props changed)
  head/sys/netinet6/in6_fib.c   (contents, props changed)
  head/sys/netinet6/in6_fib.h   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/net/route.h
  head/sys/netinet/in_gif.c
  head/sys/netinet/ip_options.c
  head/sys/netinet6/in6_gif.c
  head/sys/netinet6/scope6.c
  head/sys/netinet6/scope6_var.h
  head/sys/netpfil/ipfw/ip_fw2.c

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/conf/files	Tue Dec  8 10:50:03 2015	(r291993)
@@ -3643,6 +3643,7 @@ netinet/in.c			optional inet
 netinet/in_debug.c		optional inet ddb
 netinet/in_kdtrace.c		optional inet | inet6
 netinet/ip_carp.c		optional inet carp | inet6 carp
+netinet/in_fib.c		optional inet
 netinet/in_gif.c		optional gif inet | netgraph_gif inet
 netinet/ip_gre.c		optional gre inet
 netinet/ip_id.c			optional inet
@@ -3709,6 +3710,7 @@ netinet6/frag6.c		optional inet6
 netinet6/icmp6.c		optional inet6
 netinet6/in6.c			optional inet6
 netinet6/in6_cksum.c		optional inet6
+netinet6/in6_fib.c		optional inet6
 netinet6/in6_gif.c		optional gif inet6 | netgraph_gif inet6
 netinet6/in6_ifattach.c		optional inet6
 netinet6/in6_mcast.c		optional inet6

Modified: head/sys/net/route.h
==============================================================================
--- head/sys/net/route.h	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/net/route.h	Tue Dec  8 10:50:03 2015	(r291993)
@@ -171,6 +171,37 @@ struct rtentry {
 	 RTF_REJECT | RTF_STATIC | RTF_STICKY)
 
 /*
+ * fib_ nexthop API flags.
+ */
+
+/* Consumer-visible nexthop info flags */
+#define	NHF_REJECT		0x0010	/* RTF_REJECT */
+#define	NHF_BLACKHOLE		0x0020	/* RTF_BLACKHOLE */
+#define	NHF_REDIRECT		0x0040	/* RTF_DYNAMIC|RTF_MODIFIED */
+#define	NHF_DEFAULT		0x0080	/* Default route */
+#define	NHF_BROADCAST		0x0100	/* RTF_BROADCAST */
+#define	NHF_GATEWAY		0x0200	/* RTF_GATEWAY */
+
+/* Nexthop request flags */
+#define	NHR_IFAIF		0x01	/* Return ifa_ifp interface */
+#define	NHR_REF			0x02	/* For future use */
+
+/* rte<>nhop translation */
+static inline uint16_t
+fib_rte_to_nh_flags(int rt_flags)
+{
+	uint16_t res;
+
+	res = (rt_flags & RTF_REJECT) ? NHF_REJECT : 0;
+	res |= (rt_flags & RTF_BLACKHOLE) ? NHF_BLACKHOLE : 0;
+	res |= (rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) ? NHF_REDIRECT : 0;
+	res |= (rt_flags & RTF_BROADCAST) ? NHF_BROADCAST : 0;
+	res |= (rt_flags & RTF_GATEWAY) ? NHF_GATEWAY : 0;
+
+	return (res);
+}
+
+/*
  * Routing statistics.
  */
 struct	rtstat {

Added: head/sys/netinet/in_fib.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netinet/in_fib.c	Tue Dec  8 10:50:03 2015	(r291993)
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 2015
+ * 	Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+#include "opt_route.h"
+#include "opt_mpath.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/rwlock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/vnet.h>
+
+#ifdef RADIX_MPATH
+#include <net/radix_mpath.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_fib.h>
+
+#ifdef INET
+static void fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst,
+    uint32_t flags, struct nhop4_basic *pnh4);
+static void fib4_rte_to_nh_extended(struct rtentry *rte, struct in_addr dst,
+    uint32_t flags, struct nhop4_extended *pnh4);
+
+#define RNTORT(p)	((struct rtentry *)(p))
+
+static void
+fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst,
+    uint32_t flags, struct nhop4_basic *pnh4)
+{
+	struct sockaddr_in *gw;
+
+	if ((flags & NHR_IFAIF) != 0)
+		pnh4->nh_ifp = rte->rt_ifa->ifa_ifp;
+	else
+		pnh4->nh_ifp = rte->rt_ifp;
+	pnh4->nh_mtu = min(rte->rt_mtu, rte->rt_ifp->if_mtu);
+	if (rte->rt_flags & RTF_GATEWAY) {
+		gw = (struct sockaddr_in *)rte->rt_gateway;
+		pnh4->nh_addr = gw->sin_addr;
+	} else
+		pnh4->nh_addr = dst;
+	/* Set flags */
+	pnh4->nh_flags = fib_rte_to_nh_flags(rte->rt_flags);
+	gw = (struct sockaddr_in *)rt_key(rte);
+	if (gw->sin_addr.s_addr == 0)
+		pnh4->nh_flags |= NHF_DEFAULT;
+	/* TODO: Handle RTF_BROADCAST here */
+}
+
+static void
+fib4_rte_to_nh_extended(struct rtentry *rte, struct in_addr dst,
+    uint32_t flags, struct nhop4_extended *pnh4)
+{
+	struct sockaddr_in *gw;
+	struct in_ifaddr *ia;
+
+	pnh4->nh_ifp = rte->rt_ifa->ifa_ifp;
+	pnh4->nh_mtu = min(rte->rt_mtu, rte->rt_ifp->if_mtu);
+	if (rte->rt_flags & RTF_GATEWAY) {
+		gw = (struct sockaddr_in *)rte->rt_gateway;
+		pnh4->nh_addr = gw->sin_addr;
+	} else
+		pnh4->nh_addr = dst;
+	/* Set flags */
+	pnh4->nh_flags = fib_rte_to_nh_flags(rte->rt_flags);
+	gw = (struct sockaddr_in *)rt_key(rte);
+	if (gw->sin_addr.s_addr == 0)
+		pnh4->nh_flags |= NHF_DEFAULT;
+	/* XXX: Set RTF_BROADCAST if GW address is broadcast */
+
+	ia = ifatoia(rte->rt_ifa);
+	pnh4->nh_src = IA_SIN(ia)->sin_addr;
+}
+
+/*
+ * Performs IPv4 route table lookup on @dst. Returns 0 on success.
+ * Stores nexthop info provided @pnh4 structure.
+ * Note that
+ * - nh_ifp cannot be safely dereferenced
+ * - nh_ifp represents logical transmit interface (rt_ifp) (e.g. if
+ *   looking up address on interface "ix0" pointer to "lo0" interface
+ *   will be returned instead of "ix0")
+ * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
+ * - howewer mtu from "transmit" interface will be returned.
+ */
+int
+fib4_lookup_nh_basic(uint32_t fibnum, struct in_addr dst, uint32_t flags,
+    uint32_t flowid, struct nhop4_basic *pnh4)
+{
+	struct radix_node_head *rh;
+	struct radix_node *rn;
+	struct sockaddr_in sin;
+	struct rtentry *rte;
+
+	KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_basic: bad fibnum"));
+	rh = rt_tables_get_rnh(fibnum, AF_INET);
+	if (rh == NULL)
+		return (ENOENT);
+
+	/* Prepare lookup key */
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_len = sizeof(struct sockaddr_in);
+	sin.sin_addr = dst;
+
+	RADIX_NODE_HEAD_RLOCK(rh);
+	rn = rh->rnh_matchaddr((void *)&sin, rh);
+	if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
+		rte = RNTORT(rn);
+		/* Ensure route & ifp is UP */
+		if (RT_LINK_IS_UP(rte->rt_ifp)) {
+			fib4_rte_to_nh_basic(rte, dst, flags, pnh4);
+			RADIX_NODE_HEAD_RUNLOCK(rh);
+
+			return (0);
+		}
+	}
+	RADIX_NODE_HEAD_RUNLOCK(rh);
+
+	return (ENOENT);
+}
+
+/*
+ * Performs IPv4 route table lookup on @dst. Returns 0 on success.
+ * Stores extende nexthop info provided @pnh4 structure.
+ * Note that
+ * - nh_ifp cannot be safely dereferenced unless NHR_REF is specified.
+ * - in that case you need to call fib4_free_nh_ext()
+ * - nh_ifp represents logical transmit interface (rt_ifp) (e.g. if
+ *   looking up address of interface "ix0" pointer to "lo0" interface
+ *   will be returned instead of "ix0")
+ * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
+ * - howewer mtu from "transmit" interface will be returned.
+ */
+int
+fib4_lookup_nh_ext(uint32_t fibnum, struct in_addr dst, uint32_t flowid,
+    uint32_t flags, struct nhop4_extended *pnh4)
+{
+	struct radix_node_head *rh;
+	struct radix_node *rn;
+	struct sockaddr_in sin;
+	struct rtentry *rte;
+
+	KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_ext: bad fibnum"));
+	rh = rt_tables_get_rnh(fibnum, AF_INET);
+	if (rh == NULL)
+		return (ENOENT);
+
+	/* Prepare lookup key */
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_len = sizeof(struct sockaddr_in);
+	sin.sin_addr = dst;
+
+	RADIX_NODE_HEAD_RLOCK(rh);
+	rn = rh->rnh_matchaddr((void *)&sin, rh);
+	if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
+		rte = RNTORT(rn);
+		/* Ensure route & ifp is UP */
+		if (RT_LINK_IS_UP(rte->rt_ifp)) {
+			fib4_rte_to_nh_extended(rte, dst, flags, pnh4);
+			if ((flags & NHR_REF) != 0) {
+				/* TODO: lwref on egress ifp's ? */
+			}
+			RADIX_NODE_HEAD_RUNLOCK(rh);
+
+			return (0);
+		}
+	}
+	RADIX_NODE_HEAD_RUNLOCK(rh);
+
+	return (ENOENT);
+}
+
+void
+fib4_free_nh_ext(uint32_t fibnum, struct nhop4_extended *pnh4)
+{
+
+}
+
+#endif

Added: head/sys/netinet/in_fib.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netinet/in_fib.h	Tue Dec  8 10:50:03 2015	(r291993)
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2015
+ * 	Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET_IN_FIB_H_
+#define	_NETINET_IN_FIB_H_
+
+/* Basic nexthop info used for uRPF/mtu checks */
+struct nhop4_basic {
+	struct ifnet	*nh_ifp;	/* Logical egress interface */
+	uint16_t	nh_mtu;		/* nexthop mtu */
+	uint16_t	nh_flags;	/* nhop flags */
+	struct in_addr	nh_addr;	/* GW/DST IPv4 address */
+};
+
+/* Extended nexthop info used for control protocols */
+struct nhop4_extended {
+	struct ifnet	*nh_ifp;	/* Logical egress interface */
+	uint16_t	nh_mtu;		/* nexthop mtu */
+	uint16_t	nh_flags;	/* nhop flags */
+	uint8_t		spare[4];
+	struct in_addr	nh_addr;	/* GW/DST IPv4 address */
+	struct in_addr	nh_src;		/* default source IPv4 address */
+	uint64_t	spare2[2];
+};
+
+int fib4_lookup_nh_basic(uint32_t fibnum, struct in_addr dst, uint32_t flags,
+    uint32_t flowid, struct nhop4_basic *pnh4);
+int fib4_lookup_nh_ext(uint32_t fibnum, struct in_addr dst, uint32_t flags,
+    uint32_t flowid, struct nhop4_extended *pnh4);
+void fib4_free_nh_ext(uint32_t fibnum, struct nhop4_extended *pnh4);
+
+#endif
+

Modified: head/sys/netinet/in_gif.c
==============================================================================
--- head/sys/netinet/in_gif.c	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/netinet/in_gif.c	Tue Dec  8 10:50:03 2015	(r291993)
@@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/in_var.h>
 #include <netinet/ip_encap.h>
 #include <netinet/ip_ecn.h>
+#include <netinet/in_fib.h>
 
 #ifdef INET6
 #include <netinet/ip6.h>
@@ -188,22 +189,16 @@ in_gif_encapcheck(const struct mbuf *m, 
 
 	/* ingress filters on outer source */
 	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
-		struct sockaddr_in sin;
-		struct rtentry *rt;
+		struct nhop4_basic nh4;
+		struct in_addr dst;
 
-		bzero(&sin, sizeof(sin));
-		sin.sin_family = AF_INET;
-		sin.sin_len = sizeof(struct sockaddr_in);
-		sin.sin_addr = ip->ip_src;
-		/* XXX MRT  check for the interface we would use on output */
-		rt = in_rtalloc1((struct sockaddr *)&sin, 0,
-		    0UL, sc->gif_fibnum);
-		if (rt == NULL || rt->rt_ifp != m->m_pkthdr.rcvif) {
-			if (rt != NULL)
-				RTFREE_LOCKED(rt);
+		dst = ip->ip_src;
+
+		if (fib4_lookup_nh_basic(sc->gif_fibnum, dst, 0, 0, &nh4) != 0)
+			return (0);
+
+		if (nh4.nh_ifp != m->m_pkthdr.rcvif)
 			return (0);
-		}
-		RTFREE_LOCKED(rt);
 	}
 	return (ret);
 }

Modified: head/sys/netinet/ip_options.c
==============================================================================
--- head/sys/netinet/ip_options.c	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/netinet/ip_options.c	Tue Dec  8 10:50:03 2015	(r291993)
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
 #include <net/vnet.h>
 
 #include <netinet/in.h>
+#include <netinet/in_fib.h>
 #include <netinet/in_systm.h>
 #include <netinet/in_var.h>
 #include <netinet/ip.h>
@@ -104,6 +105,7 @@ ip_dooptions(struct mbuf *m, int pass)
 	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0;
 	struct in_addr *sin, dst;
 	uint32_t ntime;
+	struct nhop4_extended nh_ext;
 	struct	sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET };
 
 	/* Ignore or reject packets with IP options. */
@@ -227,6 +229,9 @@ dropit:
 			(void)memcpy(&ipaddr.sin_addr, cp + off,
 			    sizeof(ipaddr.sin_addr));
 
+			type = ICMP_UNREACH;
+			code = ICMP_UNREACH_SRCFAIL;
+
 			if (opt == IPOPT_SSRR) {
 #define	INA	struct in_ifaddr *
 #define	SA	struct sockaddr *
@@ -235,18 +240,23 @@ dropit:
 			    if (ia == NULL)
 				    ia = (INA)ifa_ifwithnet((SA)&ipaddr, 0,
 						    RT_ALL_FIBS);
-			} else
-/* XXX MRT 0 for routing */
-				ia = ip_rtaddr(ipaddr.sin_addr, M_GETFIB(m));
-			if (ia == NULL) {
-				type = ICMP_UNREACH;
-				code = ICMP_UNREACH_SRCFAIL;
-				goto bad;
+				if (ia == NULL)
+					goto bad;
+
+				memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
+				    sizeof(struct in_addr));
+				ifa_free(&ia->ia_ifa);
+			} else {
+				/* XXX MRT 0 for routing */
+				if (fib4_lookup_nh_ext(M_GETFIB(m),
+				    ipaddr.sin_addr, 0, 0, &nh_ext) != 0)
+					goto bad;
+
+				memcpy(cp + off, &nh_ext.nh_src,
+				    sizeof(struct in_addr));
 			}
+
 			ip->ip_dst = ipaddr.sin_addr;
-			(void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr),
-			    sizeof(struct in_addr));
-			ifa_free(&ia->ia_ifa);
 			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
 			/*
 			 * Let ip_intr's mcast routing check handle mcast pkts

Added: head/sys/netinet6/in6_fib.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netinet6/in6_fib.c	Tue Dec  8 10:50:03 2015	(r291993)
@@ -0,0 +1,264 @@
+/*-
+ * Copyright (c) 2015
+ * 	Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_route.h"
+#include "opt_mpath.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/rwlock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <net/vnet.h>
+
+#ifdef RADIX_MPATH
+#include <net/radix_mpath.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/ip6.h>
+#include <netinet6/in6_fib.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/scope6_var.h>
+
+#include <net/if_types.h>
+
+#ifdef INET6
+static void fib6_rte_to_nh_extended(struct rtentry *rte,
+    const struct in6_addr *dst, uint32_t flags, struct nhop6_extended *pnh6);
+static void fib6_rte_to_nh_basic(struct rtentry *rte, const struct in6_addr *dst,
+    uint32_t flags, struct nhop6_basic *pnh6);
+static struct ifnet *fib6_get_ifaifp(struct rtentry *rte);
+#define RNTORT(p)	((struct rtentry *)(p))
+
+/*
+ * Gets real interface for the @rte.
+ * Returns rt_ifp for !IFF_LOOPBACK routers.
+ * Extracts "real" address interface from interface address
+ * loopback routes.
+ */
+static struct ifnet *
+fib6_get_ifaifp(struct rtentry *rte)
+{
+	struct ifnet *ifp;
+	struct sockaddr_dl *sdl;
+
+	ifp = rte->rt_ifp;
+	if ((ifp->if_flags & IFF_LOOPBACK) &&
+	    rte->rt_gateway->sa_family == AF_LINK) {
+		sdl = (struct sockaddr_dl *)rte->rt_gateway;
+		return (ifnet_byindex(sdl->sdl_index));
+	}
+
+	return (ifp);
+}
+
+static void
+fib6_rte_to_nh_basic(struct rtentry *rte, const struct in6_addr *dst,
+    uint32_t flags, struct nhop6_basic *pnh6)
+{
+	struct sockaddr_in6 *gw;
+
+	/* Do explicit nexthop zero unless we're copying it */
+	memset(pnh6, 0, sizeof(*pnh6));
+
+	if ((flags & NHR_IFAIF) != 0)
+		pnh6->nh_ifp = fib6_get_ifaifp(rte);
+	else
+		pnh6->nh_ifp = rte->rt_ifp;
+
+	pnh6->nh_mtu = min(rte->rt_mtu, IN6_LINKMTU(rte->rt_ifp));
+	if (rte->rt_flags & RTF_GATEWAY) {
+		gw = (struct sockaddr_in6 *)rte->rt_gateway;
+		pnh6->nh_addr = gw->sin6_addr;
+		in6_clearscope(&pnh6->nh_addr);
+	} else
+		pnh6->nh_addr = *dst;
+	/* Set flags */
+	pnh6->nh_flags = fib_rte_to_nh_flags(rte->rt_flags);
+	gw = (struct sockaddr_in6 *)rt_key(rte);
+	if (IN6_IS_ADDR_UNSPECIFIED(&gw->sin6_addr))
+		pnh6->nh_flags |= NHF_DEFAULT;
+}
+
+static void
+fib6_rte_to_nh_extended(struct rtentry *rte, const struct in6_addr *dst,
+    uint32_t flags, struct nhop6_extended *pnh6)
+{
+	struct sockaddr_in6 *gw;
+
+	/* Do explicit nexthop zero unless we're copying it */
+	memset(pnh6, 0, sizeof(*pnh6));
+
+	if ((flags & NHR_IFAIF) != 0)
+		pnh6->nh_ifp = fib6_get_ifaifp(rte);
+	else
+		pnh6->nh_ifp = rte->rt_ifp;
+
+	pnh6->nh_mtu = min(rte->rt_mtu, IN6_LINKMTU(rte->rt_ifp));
+	if (rte->rt_flags & RTF_GATEWAY) {
+		gw = (struct sockaddr_in6 *)rte->rt_gateway;
+		pnh6->nh_addr = gw->sin6_addr;
+		in6_clearscope(&pnh6->nh_addr);
+	} else
+		pnh6->nh_addr = *dst;
+	/* Set flags */
+	pnh6->nh_flags = fib_rte_to_nh_flags(rte->rt_flags);
+	gw = (struct sockaddr_in6 *)rt_key(rte);
+	if (IN6_IS_ADDR_UNSPECIFIED(&gw->sin6_addr))
+		pnh6->nh_flags |= NHF_DEFAULT;
+}
+
+/*
+ * Performs IPv6 route table lookup on @dst. Returns 0 on success.
+ * Stores basic nexthop info into provided @pnh6 structure.
+ * Note that
+ * - nh_ifp represents logical transmit interface (rt_ifp) by default
+ * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
+ * - mtu from logical transmit interface will be returned.
+ * - nh_ifp cannot be safely dereferenced
+ * - nh_ifp represents rt_ifp (e.g. if looking up address on
+ *   interface "ix0" pointer to "ix0" interface will be returned instead
+ *   of "lo0")
+ * - howewer mtu from "transmit" interface will be returned.
+ */
+int
+fib6_lookup_nh_basic(uint32_t fibnum, const struct in6_addr *dst, uint32_t scopeid,
+    uint32_t flags, uint32_t flowid, struct nhop6_basic *pnh6)
+{
+	struct radix_node_head *rh;
+	struct radix_node *rn;
+	struct sockaddr_in6 sin6;
+	struct rtentry *rte;
+
+	KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_basic: bad fibnum"));
+	rh = rt_tables_get_rnh(fibnum, AF_INET6);
+	if (rh == NULL)
+		return (ENOENT);
+
+	/* Prepare lookup key */
+	memset(&sin6, 0, sizeof(sin6));
+	sin6.sin6_addr = *dst;
+	/* Assume scopeid is valid and embed it directly */
+	if (IN6_IS_SCOPE_LINKLOCAL(dst))
+		sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
+
+	RADIX_NODE_HEAD_RLOCK(rh);
+	rn = rh->rnh_matchaddr((void *)&sin6, rh);
+	if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
+		rte = RNTORT(rn);
+		/* Ensure route & ifp is UP */
+		if (RT_LINK_IS_UP(rte->rt_ifp)) {
+			fib6_rte_to_nh_basic(rte, dst, flags, pnh6);
+			RADIX_NODE_HEAD_RUNLOCK(rh);
+			return (0);
+		}
+	}
+	RADIX_NODE_HEAD_RUNLOCK(rh);
+
+	return (ENOENT);
+}
+
+/*
+ * Performs IPv6 route table lookup on @dst. Returns 0 on success.
+ * Stores extended nexthop info into provided @pnh6 structure.
+ * Note that
+ * - nh_ifp cannot be safely dereferenced unless NHR_REF is specified.
+ * - in that case you need to call fib6_free_nh_ext()
+ * - nh_ifp represents logical transmit interface (rt_ifp) by default
+ * - nh_ifp represents "address" interface if NHR_IFAIF flag is passed
+ * - mtu from logical transmit interface will be returned.
+ */
+int
+fib6_lookup_nh_ext(uint32_t fibnum, const struct in6_addr *dst,uint32_t scopeid,
+    uint32_t flags, uint32_t flowid, struct nhop6_extended *pnh6)
+{
+	struct radix_node_head *rh;
+	struct radix_node *rn;
+	struct sockaddr_in6 sin6;
+	struct rtentry *rte;
+
+	KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_ext: bad fibnum"));
+	rh = rt_tables_get_rnh(fibnum, AF_INET6);
+	if (rh == NULL)
+		return (ENOENT);
+
+	/* Prepare lookup key */
+	memset(&sin6, 0, sizeof(sin6));
+	sin6.sin6_len = sizeof(struct sockaddr_in6);
+	sin6.sin6_addr = *dst;
+	/* Assume scopeid is valid and embed it directly */
+	if (IN6_IS_SCOPE_LINKLOCAL(dst))
+		sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff);
+
+	RADIX_NODE_HEAD_RLOCK(rh);
+	rn = rh->rnh_matchaddr((void *)&sin6, rh);
+	if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
+		rte = RNTORT(rn);
+		/* Ensure route & ifp is UP */
+		if (RT_LINK_IS_UP(rte->rt_ifp)) {
+			fib6_rte_to_nh_extended(rte, dst, flags, pnh6);
+			if ((flags & NHR_REF) != 0) {
+				/* TODO: Do lwref on egress ifp's */
+			}
+			RADIX_NODE_HEAD_RUNLOCK(rh);
+
+			return (0);
+		}
+	}
+	RADIX_NODE_HEAD_RUNLOCK(rh);
+
+	return (ENOENT);
+}
+
+void
+fib6_free_nh_ext(uint32_t fibnum, struct nhop6_extended *pnh6)
+{
+
+}
+
+#endif
+

Added: head/sys/netinet6/in6_fib.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netinet6/in6_fib.h	Tue Dec  8 10:50:03 2015	(r291993)
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2015
+ * 	Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET6_IN6_FIB_H_
+#define	_NETINET6_IN6_FIB_H_
+
+/* Basic nexthop info used for uRPF/mtu checks */
+struct nhop6_basic {
+	struct ifnet	*nh_ifp;	/* Logical egress interface */
+	uint16_t	nh_mtu;		/* nexthop mtu */
+	uint16_t	nh_flags;	/* nhop flags */
+	uint8_t		spare[4];
+	struct in6_addr	nh_addr;	/* GW/DST IPv4 address */
+};
+
+/* Does not differ from nhop6_basic */
+struct nhop6_extended {
+	struct ifnet	*nh_ifp;	/* Logical egress interface */
+	uint16_t	nh_mtu;		/* nexthop mtu */
+	uint16_t	nh_flags;	/* nhop flags */
+	uint8_t		spare[4];
+	struct in6_addr	nh_addr;	/* GW/DST IPv6 address */
+	uint64_t	spare2[2];
+};
+
+int fib6_lookup_nh_basic(uint32_t fibnum, const struct in6_addr *dst,
+    uint32_t scopeid, uint32_t flags, uint32_t flowid,struct nhop6_basic *pnh6);
+int fib6_lookup_nh_ext(uint32_t fibnum, const struct in6_addr *dst,
+    uint32_t scopeid, uint32_t flags, uint32_t flowid,
+    struct nhop6_extended *pnh6);
+void fib6_free_nh_ext(uint32_t fibnum, struct nhop6_extended *pnh6);
+#endif
+

Modified: head/sys/netinet6/in6_gif.c
==============================================================================
--- head/sys/netinet6/in6_gif.c	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/netinet6/in6_gif.c	Tue Dec  8 10:50:03 2015	(r291993)
@@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/ip_ecn.h>
 #ifdef INET6
 #include <netinet6/ip6_ecn.h>
+#include <netinet6/in6_fib.h>
 #endif
 
 #include <net/if_gif.h>
@@ -203,23 +204,15 @@ in6_gif_encapcheck(const struct mbuf *m,
 
 	/* ingress filters on outer source */
 	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
-		struct sockaddr_in6 sin6;
-		struct rtentry *rt;
+		struct nhop6_basic nh6;
+		struct in6_addr *dst;
 
-		bzero(&sin6, sizeof(sin6));
-		sin6.sin6_family = AF_INET6;
-		sin6.sin6_len = sizeof(struct sockaddr_in6);
-		sin6.sin6_addr = ip6->ip6_src;
-		sin6.sin6_scope_id = 0; /* XXX */
-
-		rt = in6_rtalloc1((struct sockaddr *)&sin6, 0, 0UL,
-		    sc->gif_fibnum);
-		if (rt == NULL || rt->rt_ifp != m->m_pkthdr.rcvif) {
-			if (rt != NULL)
-				RTFREE_LOCKED(rt);
+		/* XXX empty scope id */
+		if (fib6_lookup_nh_basic(sc->gif_fibnum, dst, 0, 0, 0, &nh6)!=0)
+			return (0);
+
+		if (nh6.nh_ifp != m->m_pkthdr.rcvif)
 			return (0);
-		}
-		RTFREE_LOCKED(rt);
 	}
 	return (ret);
 }

Modified: head/sys/netinet6/scope6.c
==============================================================================
--- head/sys/netinet6/scope6.c	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/netinet6/scope6.c	Tue Dec  8 10:50:03 2015	(r291993)
@@ -487,6 +487,22 @@ in6_getscopezone(const struct ifnet *ifp
 }
 
 /*
+ * Extracts scope from adddress @dst, stores cleared address
+ * inside @dst and zone inside @scopeid
+ */
+void
+in6_splitscope(const struct in6_addr *src, struct in6_addr *dst,
+    uint32_t *scopeid)
+{
+	uint32_t zoneid;
+
+	*dst = *src;
+	zoneid = ntohs(in6_getscope(dst));
+	in6_clearscope(dst);
+	*scopeid = zoneid;
+}
+
+/*
  * This function is for checking sockaddr_in6 structure passed
  * from the application level (usually).
  *

Modified: head/sys/netinet6/scope6_var.h
==============================================================================
--- head/sys/netinet6/scope6_var.h	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/netinet6/scope6_var.h	Tue Dec  8 10:50:03 2015	(r291993)
@@ -63,6 +63,7 @@ int	in6_setscope(struct in6_addr *, stru
 int	in6_clearscope(struct in6_addr *);
 uint16_t in6_getscope(struct in6_addr *);
 uint32_t in6_getscopezone(const struct ifnet *, int);
+void	in6_splitscope(const struct in6_addr *, struct in6_addr *, uint32_t *);
 struct ifnet* in6_getlinkifnet(uint32_t);
 #endif /* _KERNEL */
 

Modified: head/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw2.c	Tue Dec  8 08:31:00 2015	(r291992)
+++ head/sys/netpfil/ipfw/ip_fw2.c	Tue Dec  8 10:50:03 2015	(r291993)
@@ -84,7 +84,9 @@ __FBSDID("$FreeBSD$");
 
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
+#include <netinet/in_fib.h>
 #ifdef INET6
+#include <netinet6/in6_fib.h>
 #include <netinet6/in6_pcb.h>
 #include <netinet6/scope6_var.h>
 #include <netinet6/ip6_var.h>
@@ -437,19 +439,10 @@ verify_path(struct in_addr src, struct i
 #if defined(USERSPACE) || !defined(__FreeBSD__)
 	return 0;
 #else
-	struct route ro;
-	struct sockaddr_in *dst;
+	struct nhop4_basic nh4;
 
-	bzero(&ro, sizeof(ro));
-
-	dst = (struct sockaddr_in *)&(ro.ro_dst);
-	dst->sin_family = AF_INET;
-	dst->sin_len = sizeof(*dst);
-	dst->sin_addr = src;
-	in_rtalloc_ign(&ro, 0, fib);
-
-	if (ro.ro_rt == NULL)
-		return 0;
+	if (fib4_lookup_nh_basic(fib, src, NHR_IFAIF, 0, &nh4) != 0)
+		return (0);
 
 	/*
 	 * If ifp is provided, check for equality with rtentry.
@@ -458,26 +451,18 @@ verify_path(struct in_addr src, struct i
 	 * routing entry (via lo0) for our own address
 	 * may exist, so we need to handle routing assymetry.
 	 */
-	if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) {
-		RTFREE(ro.ro_rt);
-		return 0;
-	}
+	if (ifp != NULL && ifp != nh4.nh_ifp)
+		return (0);
 
 	/* if no ifp provided, check if rtentry is not default route */
-	if (ifp == NULL &&
-	     satosin(rt_key(ro.ro_rt))->sin_addr.s_addr == INADDR_ANY) {
-		RTFREE(ro.ro_rt);
-		return 0;
-	}
+	if (ifp == NULL && (nh4.nh_flags & NHF_DEFAULT) != 0)
+		return (0);
 
 	/* or if this is a blackhole/reject route */
-	if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
-		RTFREE(ro.ro_rt);
-		return 0;
-	}
+	if (ifp == NULL && (nh4.nh_flags & (NHF_REJECT|NHF_BLACKHOLE)) != 0)
+		return (0);
 
 	/* found valid route */
-	RTFREE(ro.ro_rt);
 	return 1;
 #endif /* __FreeBSD__ */
 }
@@ -537,49 +522,28 @@ ipfw_localip6(struct in6_addr *in6)
 static int
 verify_path6(struct in6_addr *src, struct ifnet *ifp, u_int fib)
 {
-	struct route_in6 ro;
-	struct sockaddr_in6 *dst;
-
-	bzero(&ro, sizeof(ro));
+	struct nhop6_basic nh6;
 
-	dst = (struct sockaddr_in6 * )&(ro.ro_dst);
-	dst->sin6_family = AF_INET6;
-	dst->sin6_len = sizeof(*dst);
-	dst->sin6_addr = *src;
+	if (IN6_IS_SCOPE_LINKLOCAL(src))
+		return (1);
 
-	in6_rtalloc_ign(&ro, 0, fib);
-	if (ro.ro_rt == NULL)
-		return 0;
+	if (fib6_lookup_nh_basic(fib, src, 0, NHR_IFAIF, 0, &nh6) != 0)
+		return (0);
 
-	/* 
-	 * if ifp is provided, check for equality with rtentry
-	 * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp,
-	 * to support the case of sending packets to an address of our own.
-	 * (where the former interface is the first argument of if_simloop()
-	 *  (=ifp), the latter is lo0)
-	 */
-	if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) {
-		RTFREE(ro.ro_rt);
-		return 0;
-	}
+	/* If ifp is provided, check for equality with route table. */
+	if (ifp != NULL && ifp != nh6.nh_ifp)
+		return (0);
 
 	/* if no ifp provided, check if rtentry is not default route */
-	if (ifp == NULL &&
-	    IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(ro.ro_rt))->sin6_addr)) {
-		RTFREE(ro.ro_rt);
-		return 0;
-	}
+	if (ifp == NULL && (nh6.nh_flags & NHF_DEFAULT) != 0)
+		return (0);
 
 	/* or if this is a blackhole/reject route */
-	if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
-		RTFREE(ro.ro_rt);
-		return 0;
-	}
+	if (ifp == NULL && (nh6.nh_flags & (NHF_REJECT|NHF_BLACKHOLE)) != 0)
+		return (0);
 
 	/* found valid route */
-	RTFREE(ro.ro_rt);
 	return 1;
-
 }
 
 static int



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