Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Oct 2013 11:51:09 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-user@freebsd.org
Subject:   svn commit: r256620 - user/ae/inet6/sys/netinet6
Message-ID:  <201310161151.r9GBp91D082386@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Wed Oct 16 11:51:08 2013
New Revision: 256620
URL: http://svnweb.freebsd.org/changeset/base/256620

Log:
  Introduce new flag for ip6_output() - IPV6_USEROIF. It means, that
  top level already determined outgoing interface and ip6_output()
  can use it. But in some cases ip6_output() will ignore it (e.g. packet
  forwarded by pfil(4), routing header, etc).
  
  Rework route lookup part of ip6_output() to properly handle the
  fact, that now we don't keep routes for link-local addresses.
  Also, since in6_selectroute_fib() disappeared, now we need use
  in6_rtalloc(). But in some cases it isn't needed.

Modified:
  user/ae/inet6/sys/netinet6/ip6_output.c
  user/ae/inet6/sys/netinet6/ip6_var.h

Modified: user/ae/inet6/sys/netinet6/ip6_output.c
==============================================================================
--- user/ae/inet6/sys/netinet6/ip6_output.c	Wed Oct 16 11:30:47 2013	(r256619)
+++ user/ae/inet6/sys/netinet6/ip6_output.c	Wed Oct 16 11:51:08 2013	(r256620)
@@ -143,8 +143,8 @@ static int ip6_insertfraghdr(struct mbuf
 	struct ip6_frag **);
 static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
 static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
-static int ip6_getpmtu(struct route_in6 *, struct route_in6 *,
-	struct ifnet *, struct in6_addr *, u_long *, int *, u_int);
+static int ip6_getpmtu(const struct in6_addr *, struct ifnet *,
+    struct route_in6 *, struct route_in6 *, u_int, u_long *, int *);
 static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
 
 
@@ -221,8 +221,6 @@ in6_delayed_cksum(struct mbuf *m, uint32
  * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and
  * nd_ifinfo.linkmtu is u_int32_t.  so we use u_long to hold largest one,
  * which is rt_rmx.rmx_mtu.
- *
- * ifpp - XXX: just for statistics
  */
 int
 ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
@@ -230,13 +228,12 @@ ip6_output(struct mbuf *m0, struct ip6_p
     struct ifnet **ifpp, struct inpcb *inp)
 {
 	struct ip6_hdr *ip6, *mhip6;
-	struct ifnet *ifp, *origifp;
+	struct ifnet *ifp = NULL, *origifp;
 	struct mbuf *m = m0;
 	struct mbuf *mprev = NULL;
 	int hlen, tlen, len, off;
 	struct route_in6 ip6route;
-	struct rtentry *rt = NULL;
-	struct sockaddr_in6 *dst, src_sa, dst_sa;
+	struct sockaddr_in6 *dst, dst_sa;
 	struct in6_addr odst;
 	int error = 0;
 	struct in6_ifaddr *ia = NULL;
@@ -244,8 +241,7 @@ ip6_output(struct mbuf *m0, struct ip6_p
 	int alwaysfrag, dontfrag;
 	u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
 	struct ip6_exthdrs exthdrs;
-	struct in6_addr finaldst, src0, dst0;
-	u_int32_t zone;
+	struct in6_addr finaldst;
 	struct route_in6 *ro_pmtu = NULL;
 	int hdrsplit = 0;
 	int needipsec = 0;
@@ -258,6 +254,7 @@ ip6_output(struct mbuf *m0, struct ip6_p
 	struct secpolicy *sp = NULL;
 #endif /* IPSEC */
 	struct m_tag *fwd_tag = NULL;
+	u_int fibnum;
 
 	ip6 = mtod(m, struct ip6_hdr *);
 	if (ip6 == NULL) {
@@ -265,8 +262,11 @@ ip6_output(struct mbuf *m0, struct ip6_p
 		goto bad;
 	}
 
-	if (inp != NULL)
+	if (inp != NULL) {
 		M_SETFIB(m, inp->inp_inc.inc_fibnum);
+		fibnum = inp->inp_inc.inc_fibnum;
+	} else
+		fibnum = M_GETFIB(m);
 
 	finaldst = ip6->ip6_dst;
 	bzero(&exthdrs, sizeof(exthdrs));
@@ -512,14 +512,22 @@ skip_ipsec2:;
 	/*
 	 * Route packet.
 	 */
-	if (ro == 0) {
-		ro = &ip6route;
-		bzero((caddr_t)ro, sizeof(*ro));
+	if (ro == NULL || ro->ro_rt == NULL) {
+		if (ro == NULL)
+			ro = &ip6route;
+		bzero(ro, sizeof(*ro));
+		ro->ro_dst.sin6_family = AF_INET6;
+		ro->ro_dst.sin6_len = sizeof(ro->ro_dst);
+		ro->ro_dst.sin6_addr = ip6->ip6_dst;
 	}
 	ro_pmtu = ro;
-	if (opt && opt->ip6po_rthdr)
-		ro = &opt->ip6po_route;
-	dst = (struct sockaddr_in6 *)&ro->ro_dst;
+	if (opt != NULL) {
+		if (opt->ip6po_nexthop != NULL)
+			ro = &opt->ip6po_nextroute;
+		else if (opt->ip6po_rthdr != NULL)
+			ro = &opt->ip6po_route;
+	}
+	dst = &ro->ro_dst;
 #ifdef FLOWTABLE
 	if (ro->ro_rt == NULL) {
 		struct flentry *fle;
@@ -636,102 +644,136 @@ again:
 	/* adjust pointer */
 	ip6 = mtod(m, struct ip6_hdr *);
 
-	if (ro->ro_rt && fwd_tag == NULL) {
-		rt = ro->ro_rt;
+	if (fwd_tag != NULL) {
+		/*
+		 * We have changed destination by packet filter.
+		 * Check that we have valid route and free it, if
+		 * new destination is different one.
+		 *
+		 * dst_sa contains the new destination.
+		 */
+		if (ro != &ip6route) {
+			/*
+			 * XXX: check that we do not leak route here.
+			 */
+			ro = &ip6route;
+			bzero(ro, sizeof(*ro));
+		} else if (ro->ro_rt != NULL && (
+		    (ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+		    ro->ro_dst.sin6_family != AF_INET6 ||
+		    IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr,
+			&dst_sa.sin6_addr) == 0))
+			RO_RTFREE(ro);
+		/*
+		 * Copy new destination to prepare new route lookup.
+		 */
+		bcopy(&dst_sa, &ro->ro_dst, sizeof(dst_sa));
+		dst = &ro->ro_dst;
+	} else {
+		/*
+		 * fwd_tag is NULL.
+		 * Check that route exists and is valid.
+		 */
+		if (ro->ro_rt != NULL && (
+		    (ro->ro_rt->rt_flags & RTF_UP) == 0 ||
+		    ro->ro_dst.sin6_family != AF_INET6))
+			RO_RTFREE(ro);
+	}
+	if (ro->ro_rt != NULL) {
 		ifp = ro->ro_rt->rt_ifp;
 	} else {
-		if (fwd_tag == NULL) {
-			bzero(&dst_sa, sizeof(dst_sa));
-			dst_sa.sin6_family = AF_INET6;
-			dst_sa.sin6_len = sizeof(dst_sa);
-			dst_sa.sin6_addr = ip6->ip6_dst;
-		}
-		error = in6_selectroute_fib(&dst_sa, opt, im6o, ro, &ifp,
-		    &rt, inp ? inp->inp_inc.inc_fibnum : M_GETFIB(m));
-		if (error != 0) {
-			if (ifp != NULL)
-				in6_ifstat_inc(ifp, ifs6_out_discard);
-			goto bad;
+		/*
+		 * We can just use specified by user outgoing interface when
+		 * IPV6_USEROIF flag is set. But we should determine new
+		 * outgoing interface if PFIL has changed destination address,
+		 * or some packet options is set.
+		 */
+		if ((flags & IPV6_USEROIF) != 0 &&
+		    (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
+		    (fwd_tag == NULL && (opt == NULL ||
+		    (opt->ip6po_nexthop == NULL &&
+		    opt->ip6po_rthdr == NULL)))))
+			ifp = *ifpp;
+		else {
+			/*
+			 * We ignore next hop and routing header when
+			 * destination IP address is multicast. So, first
+			 * look into multicast options to determine outgoing
+			 * interface.
+			 */
+			if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+				if (im6o != NULL &&
+				    im6o->im6o_multicast_ifp != NULL) {
+					ifp = im6o->im6o_multicast_ifp;
+					goto oif_found;
+				}
+				/*
+				 * If address is from interface-local or
+				 * link-local scope, then we can not
+				 * disambiguate it without scope zone id.
+				 */
+				if (IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst) ||
+				    IN6_IS_ADDR_MC_INTFACELOCAL(
+				    &ip6->ip6_dst)) {
+					error = EHOSTUNREACH;
+					IP6STAT_INC(ip6s_noroute);
+					goto bad;
+				}
+			} else if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr) ||
+			    IN6_IS_ADDR_MC_INTFACELOCAL(&dst->sin6_addr) ||
+			    IN6_IS_ADDR_MC_LINKLOCAL(&dst->sin6_addr)) {
+				/*
+				 * Check that destination has correct zone id
+				 */
+				if (dst->sin6_scope_id == 0 || !(ifp =
+				    in6_getlinkifnet(dst->sin6_scope_id))) {
+					error = EHOSTUNREACH;
+					IP6STAT_INC(ip6s_noroute);
+					goto bad;
+				}
+				goto oif_found;
+			}
+			/*
+			 * Check that destination address is our own.
+			 */
+			ia = in6ifa_ifwithaddr(&dst->sin6_addr, 0);
+			if (ia != NULL) {
+				ifp = ia->ia_ifp;
+				ifa_free(&ia->ia_ifa);
+				goto oif_found;
+			}
+			/*
+			 * Destination address is not our, try to find a route.
+			 */
+			in6_rtalloc(ro, fibnum);
+			if (ro->ro_rt)
+				ifp = ro->ro_rt->rt_ifp;
 		}
 	}
-	if (rt == NULL) {
-		/*
-		 * If in6_selectroute() does not return a route entry,
-		 * dst may not have been updated.
-		 */
-		*dst = dst_sa;	/* XXX */
+	if (ifp == NULL) {
+		error = EHOSTUNREACH;
+		IP6STAT_INC(ip6s_noroute);
+		goto bad;
 	}
-
-	/*
-	 * then rt (for unicast) and ifp must be non-NULL valid values.
-	 */
+oif_found:
 	if ((flags & IPV6_FORWARDING) == 0) {
 		/* XXX: the FORWARDING flag can be set for mrouting. */
 		in6_ifstat_inc(ifp, ifs6_out_request);
 	}
-	if (rt != NULL) {
-		ia = (struct in6_ifaddr *)(rt->rt_ifa);
-		rt->rt_use++;
-	}
-
-
 	/*
-	 * The outgoing interface must be in the zone of source and
+	 * XXX: The outgoing interface must be in the zone of source and
 	 * destination addresses.
 	 */
 	origifp = ifp;
-
-	src0 = ip6->ip6_src;
-	if (in6_setscope(&src0, origifp, &zone))
-		goto badscope;
-	bzero(&src_sa, sizeof(src_sa));
-	src_sa.sin6_family = AF_INET6;
-	src_sa.sin6_len = sizeof(src_sa);
-	src_sa.sin6_addr = ip6->ip6_src;
-	if (sa6_recoverscope(&src_sa) || zone != src_sa.sin6_scope_id)
-		goto badscope;
-
-	dst0 = ip6->ip6_dst;
-	if (in6_setscope(&dst0, origifp, &zone))
-		goto badscope;
-	/* re-initialize to be sure */
-	bzero(&dst_sa, sizeof(dst_sa));
-	dst_sa.sin6_family = AF_INET6;
-	dst_sa.sin6_len = sizeof(dst_sa);
-	dst_sa.sin6_addr = ip6->ip6_dst;
-	if (sa6_recoverscope(&dst_sa) || zone != dst_sa.sin6_scope_id) {
-		goto badscope;
-	}
-
-	/* We should use ia_ifp to support the case of
-	 * sending packets to an address of our own.
-	 */
-	if (ia != NULL && ia->ia_ifp)
-		ifp = ia->ia_ifp;
-
-	/* scope check is done. */
-	goto routefound;
-
-  badscope:
-	IP6STAT_INC(ip6s_badscope);
-	in6_ifstat_inc(origifp, ifs6_out_discard);
-	if (error == 0)
-		error = EHOSTUNREACH; /* XXX */
-	goto bad;
-
-  routefound:
-	if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
-		if (opt && opt->ip6po_nextroute.ro_rt) {
-			/*
-			 * The nexthop is explicitly specified by the
-			 * application.  We assume the next hop is an IPv6
-			 * address.
-			 */
-			dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
-		}
-		else if ((rt->rt_flags & RTF_GATEWAY))
-			dst = (struct sockaddr_in6 *)rt->rt_gateway;
-	}
+	/*
+	 * If we have valid route and it is marked as gateway, then
+	 * use rt_gateway as dst. Otherwise dst will point to the destination
+	 * address (it can be ip6->ip6_dst, opt->ip6po_nexthop,
+	 * ip6route.ro_dst from fwd_tag or opt->ip6po_route.ro_dst).
+	 */
+	if (ro->ro_rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
+	    (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0)
+		dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
 
 	if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
 		m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
@@ -806,8 +848,8 @@ again:
 		*ifpp = ifp;
 
 	/* Determine path MTU. */
-	if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu,
-	    &alwaysfrag, inp ? inp->inp_inc.inc_fibnum : M_GETFIB(m))) != 0)
+	if ((error = ip6_getpmtu(&finaldst, ifp, ro, ro_pmtu,
+	    fibnum, &mtu, &alwaysfrag)) != 0)
 		goto bad;
 
 	/*
@@ -927,7 +969,6 @@ again:
 	/* Or forward to some other address? */
 	if ((m->m_flags & M_IP6_NEXTHOP) &&
 	    (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
-		dst = (struct sockaddr_in6 *)&ro->ro_dst;
 		bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6));
 		m->m_flags |= M_SKIP_FIREWALL;
 		m->m_flags &= ~M_IP6_NEXTHOP;
@@ -1367,9 +1408,8 @@ ip6_insertfraghdr(struct mbuf *m0, struc
 }
 
 static int
-ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro,
-    struct ifnet *ifp, struct in6_addr *dst, u_long *mtup,
-    int *alwaysfragp, u_int fibnum)
+ip6_getpmtu(const struct in6_addr *dst, struct ifnet *ifp, struct route_in6 *ro,
+    struct route_in6 *ro_pmtu, u_int fibnum, u_long *mtup, int *alwaysfragp)
 {
 	u_int32_t mtu = 0;
 	int alwaysfrag = 0;
@@ -1377,20 +1417,17 @@ ip6_getpmtu(struct route_in6 *ro_pmtu, s
 
 	if (ro_pmtu != ro) {
 		/* The first hop and the final destination may differ. */
-		struct sockaddr_in6 *sa6_dst =
-		    (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
 		if (ro_pmtu->ro_rt &&
 		    ((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 ||
-		     !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
-			RTFREE(ro_pmtu->ro_rt);
-			ro_pmtu->ro_rt = (struct rtentry *)NULL;
-		}
-		if (ro_pmtu->ro_rt == NULL) {
-			bzero(sa6_dst, sizeof(*sa6_dst));
-			sa6_dst->sin6_family = AF_INET6;
-			sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
-			sa6_dst->sin6_addr = *dst;
-
+		     !IN6_ARE_ADDR_EQUAL(&ro_pmtu->ro_dst.sin6_addr, dst)))
+			RO_RTFREE(ro_pmtu);
+		if (ro_pmtu->ro_rt == NULL &&
+		    !IN6_IS_ADDR_LINKLOCAL(dst) &&
+		    !IN6_IS_ADDR_MULTICAST(dst)) {
+			bzero(&ro_pmtu->ro_dst, sizeof(ro_pmtu->ro_dst));
+			ro_pmtu->ro_dst.sin6_family = AF_INET6;
+			ro_pmtu->ro_dst.sin6_len = sizeof(ro_pmtu->ro_dst);
+			ro_pmtu->ro_dst.sin6_addr = *dst;
 			in6_rtalloc(ro_pmtu, fibnum);
 		}
 	}
@@ -2031,9 +2068,8 @@ do { \
 				 * routing, or optional information to specify
 				 * the outgoing interface.
 				 */
-				error = ip6_getpmtu(&sro, NULL, NULL,
-				    &in6p->in6p_faddr, &pmtu, NULL,
-				    so->so_fibnum);
+				error = ip6_getpmtu(&in6p->in6p_faddr, NULL,
+				    NULL, &sro, so->so_fibnum, &pmtu, NULL);
 				if (sro.ro_rt)
 					RTFREE(sro.ro_rt);
 				if (error)

Modified: user/ae/inet6/sys/netinet6/ip6_var.h
==============================================================================
--- user/ae/inet6/sys/netinet6/ip6_var.h	Wed Oct 16 11:30:47 2013	(r256619)
+++ user/ae/inet6/sys/netinet6/ip6_var.h	Wed Oct 16 11:51:08 2013	(r256620)
@@ -292,6 +292,7 @@ struct ip6aux {
 #define	IPV6_UNSPECSRC		0x01	/* allow :: as the source address */
 #define	IPV6_FORWARDING		0x02	/* most of IPv6 header exists */
 #define	IPV6_MINMTU		0x04	/* use minimum MTU (IPV6_USE_MIN_MTU) */
+#define	IPV6_USEROIF		0x08	/* use interface specified by user */
 
 /*
  * IPv6 protocol layer specific mbuf flags.



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