From owner-svn-src-user@FreeBSD.ORG Wed Oct 16 11:51:09 2013 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id A3DD9AA6; Wed, 16 Oct 2013 11:51:09 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 82E812731; Wed, 16 Oct 2013 11:51:09 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9GBp9OT082388; Wed, 16 Oct 2013 11:51:09 GMT (envelope-from ae@svn.freebsd.org) Received: (from ae@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9GBp91D082386; Wed, 16 Oct 2013 11:51:09 GMT (envelope-from ae@svn.freebsd.org) Message-Id: <201310161151.r9GBp91D082386@svn.freebsd.org> From: "Andrey V. Elsukov" Date: Wed, 16 Oct 2013 11:51:09 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r256620 - user/ae/inet6/sys/netinet6 X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 16 Oct 2013 11:51:09 -0000 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.