From owner-svn-src-head@freebsd.org Sun Jan 3 09:54:04 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 8CF57A600C6; Sun, 3 Jan 2016 09:54:04 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4ECBE1783; Sun, 3 Jan 2016 09:54:04 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u039s3jk041112; Sun, 3 Jan 2016 09:54:03 GMT (envelope-from melifaro@FreeBSD.org) Received: (from melifaro@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u039s3fS041109; Sun, 3 Jan 2016 09:54:03 GMT (envelope-from melifaro@FreeBSD.org) Message-Id: <201601030954.u039s3fS041109@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: melifaro set sender to melifaro@FreeBSD.org using -f From: "Alexander V. Chernikov" Date: Sun, 3 Jan 2016 09:54:03 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r293098 - in head/sys: net netinet6 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 03 Jan 2016 09:54:04 -0000 Author: melifaro Date: Sun Jan 3 09:54:03 2016 New Revision: 293098 URL: https://svnweb.freebsd.org/changeset/base/293098 Log: Handle IPV6_PATHMTU option by spliting ip6_getpmtu_ctl() from ip6_getpmtu(). Add ro_mtu field to 'struct route' to be able to pass lookup MTU back to the caller. Currently, ip6_getpmtu() has 2 totally different use cases: 1) control plane (IPV6_PATHMTU req), where we just need to calculate MTU and return it, w/o any reusability. 2) Actual ip6_output() data path where we (nearly) always use the provided route lookup data. If this data is not 'valid' we need to perform another lookup and save the result (which cannot be re-used by ip6_output()). Given that, handle 1) by calling separate function doing rte lookup itself. Resulting MTU is calculated by (newly-added) ip6_calcmtu() used by both ip6_getpmtu_ctl() and ip6_getpmtu(). For 2) instead of storing ref'ed rte, store mtu (the only needed data from the lookup result) inside newly-added ro_mtu field. 'struct route' was shrinked by 8(or 4 bytes) in r292978. Grow it again by 4 bytes. New ro_mtu field will be used in other places like ip/tcp_output (EMSGSIZE handling from output routines). Reviewed by: ae Modified: head/sys/net/route.h head/sys/netinet6/in6.h head/sys/netinet6/ip6_output.c Modified: head/sys/net/route.h ============================================================================== --- head/sys/net/route.h Sun Jan 3 09:44:26 2016 (r293097) +++ head/sys/net/route.h Sun Jan 3 09:54:03 2016 (r293098) @@ -44,16 +44,17 @@ */ /* - * A route consists of a destination address, a reference - * to a routing entry, and a reference to an llentry. - * These are often held by protocols in their control - * blocks, e.g. inpcb. + * Struct route consiste of a destination address, + * a route entry pointer, link-layer prepend data pointer along + * with its length. */ struct route { struct rtentry *ro_rt; char *ro_prepend; uint16_t ro_plen; uint16_t ro_flags; + uint16_t ro_mtu; /* saved ro_rt mtu */ + uint16_t spare; struct sockaddr ro_dst; }; Modified: head/sys/netinet6/in6.h ============================================================================== --- head/sys/netinet6/in6.h Sun Jan 3 09:44:26 2016 (r293097) +++ head/sys/netinet6/in6.h Sun Jan 3 09:54:03 2016 (r293098) @@ -378,6 +378,8 @@ struct route_in6 { char *ro_prepend; uint16_t ro_plen; uint16_t ro_flags; + uint16_t ro_mtu; /* saved ro_rt mtu */ + uint16_t spare; struct sockaddr_in6 ro_dst; }; #endif Modified: head/sys/netinet6/ip6_output.c ============================================================================== --- head/sys/netinet6/ip6_output.c Sun Jan 3 09:44:26 2016 (r293097) +++ head/sys/netinet6/ip6_output.c Sun Jan 3 09:54:03 2016 (r293098) @@ -147,8 +147,11 @@ 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 *, +static int ip6_getpmtu(struct route_in6 *, int, struct ifnet *, struct in6_addr *, u_long *, int *, u_int); +static int ip6_calcmtu(struct ifnet *, const struct in6_addr *, u_long, + u_long *, int *); +static int ip6_getpmtu_ctl(u_int, struct in6_addr *, u_long *); static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); @@ -712,7 +715,7 @@ again: *ifpp = ifp; /* Determine path MTU. */ - if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu, + if ((error = ip6_getpmtu(ro_pmtu, ro != ro_pmtu, ifp, &finaldst, &mtu, &alwaysfrag, fibnum)) != 0) goto bad; @@ -1045,8 +1048,6 @@ sendorfree: done: if (ro == &ip6route) RO_RTFREE(ro); - if (ro_pmtu == &ip6route) - RO_RTFREE(ro_pmtu); return (error); freehdrs: @@ -1215,35 +1216,104 @@ ip6_insertfraghdr(struct mbuf *m0, struc return (0); } +/* + * Calculates IPv6 path mtu for destination @dst. + * Resulting MTU is stored in @mtup. + * + * Returns 0 on success. + */ +static int +ip6_getpmtu_ctl(u_int fibnum, struct in6_addr *dst, u_long *mtup) +{ + struct route_in6 ro_pmtu; + struct ifnet *ifp; + struct sockaddr_in6 *sa6_dst; + u_long mtu; + + sa6_dst = (struct sockaddr_in6 *)&ro_pmtu.ro_dst; + 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_rtalloc(&ro_pmtu, fibnum); + + if (ro_pmtu.ro_rt == NULL) + return (EHOSTUNREACH); + + ifp = ro_pmtu.ro_rt->rt_ifp; + mtu = ro_pmtu.ro_rt->rt_mtu; + RO_RTFREE(&ro_pmtu); + + return (ip6_calcmtu(ifp, dst, mtu, mtup, NULL)); +} + +/* + * Calculates IPv6 path MTU for @dst based on transmit @ifp, + * and cached data in @ro_pmtu. + * MTU from (successful) route lookup is saved (along with dst) + * inside @ro_pmtu to avoid subsequent route lookups after packet + * filter processing. + * + * Stores mtu and always-frag value into @mtup and @alwaysfragp. + * Returns 0 on success. + */ static int -ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro, +ip6_getpmtu(struct route_in6 *ro_pmtu, int do_lookup, struct ifnet *ifp, struct in6_addr *dst, u_long *mtup, int *alwaysfragp, u_int fibnum) { - u_int32_t mtu = 0; - int alwaysfrag = 0; - int error = 0; + struct sockaddr_in6 *sa6_dst; + u_long mtu; - 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) { + mtu = 0; + if (do_lookup) { + + /* + * Here ro_pmtu has final destination address, while + * ro might represent immediate destination. + * Use ro_pmtu destination since mtu might differ. + */ + sa6_dst = (struct sockaddr_in6 *)&ro_pmtu->ro_dst; + if (!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst)) + ro_pmtu->ro_mtu = 0; + + if (ro_pmtu->ro_mtu == 0) { 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_rtalloc(ro_pmtu, fibnum); + if (ro_pmtu->ro_rt) { + mtu = ro_pmtu->ro_rt->rt_mtu; + RO_RTFREE(ro_pmtu); + } } } - if (ro_pmtu->ro_rt) { + + if (ro_pmtu->ro_rt) + mtu = ro_pmtu->ro_rt->rt_mtu; + + return (ip6_calcmtu(ifp, dst, mtu, mtup, alwaysfragp)); +} + +/* + * Calculate MTU based on transmit @ifp, route mtu @rt_mtu and + * hostcache data for @dst. + * Stores mtu and always-frag value into @mtup and @alwaysfragp. + * + * Returns 0 on success. + */ +static int +ip6_calcmtu(struct ifnet *ifp, const struct in6_addr *dst, u_long rt_mtu, + u_long *mtup, int *alwaysfragp) +{ + u_long mtu = 0; + int alwaysfrag = 0; + int error = 0; + + if (rt_mtu > 0) { u_int32_t ifmtu; struct in_conninfo inc; @@ -1251,14 +1321,12 @@ ip6_getpmtu(struct route_in6 *ro_pmtu, s inc.inc_flags |= INC_ISIPV6; inc.inc6_faddr = *dst; - if (ifp == NULL) - ifp = ro_pmtu->ro_rt->rt_ifp; ifmtu = IN6_LINKMTU(ifp); mtu = tcp_hc_getmtu(&inc); if (mtu) - mtu = min(mtu, ro_pmtu->ro_rt->rt_mtu); + mtu = min(mtu, rt_mtu); else - mtu = ro_pmtu->ro_rt->rt_mtu; + mtu = rt_mtu; if (mtu == 0) mtu = ifmtu; else if (mtu < IPV6_MMTU) { @@ -1936,9 +2004,6 @@ do { \ { u_long pmtu = 0; struct ip6_mtuinfo mtuinfo; - struct route_in6 sro; - - bzero(&sro, sizeof(sro)); if (!(so->so_state & SS_ISCONNECTED)) return (ENOTCONN); @@ -1947,11 +2012,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); - if (sro.ro_rt) - RTFREE(sro.ro_rt); + error = ip6_getpmtu_ctl(so->so_fibnum, + &in6p->in6p_faddr, &pmtu); if (error) break; if (pmtu > IPV6_MAXPACKET)