From owner-svn-src-stable-12@freebsd.org Mon Mar 2 13:16:19 2020 Return-Path: Delivered-To: svn-src-stable-12@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 387531AE6EB; Mon, 2 Mar 2020 13:16:19 +0000 (UTC) (envelope-from bz@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 48WLJ26xPWz4YWS; Mon, 2 Mar 2020 13:16:18 +0000 (UTC) (envelope-from bz@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 mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id A24231CE32; Mon, 2 Mar 2020 13:16:18 +0000 (UTC) (envelope-from bz@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 022DGIlE004714; Mon, 2 Mar 2020 13:16:18 GMT (envelope-from bz@FreeBSD.org) Received: (from bz@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 022DGImd004713; Mon, 2 Mar 2020 13:16:18 GMT (envelope-from bz@FreeBSD.org) Message-Id: <202003021316.022DGImd004713@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: bz set sender to bz@FreeBSD.org using -f From: "Bjoern A. Zeeb" Date: Mon, 2 Mar 2020 13:16:18 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r358540 - stable/12/sys/netinet6 X-SVN-Group: stable-12 X-SVN-Commit-Author: bz X-SVN-Commit-Paths: stable/12/sys/netinet6 X-SVN-Commit-Revision: 358540 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-12@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for only the 12-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 02 Mar 2020 13:16:19 -0000 Author: bz Date: Mon Mar 2 13:16:18 2020 New Revision: 358540 URL: https://svnweb.freebsd.org/changeset/base/358540 Log: MFC r358167,358311: ip6_output: improve extension header handling Move IPv6 source address checks from after extension header heandling to the top of the function. If we do not pass these checks there is no reason to do a lot of work upfront. Fold extension header preparations and length calculations together into a single branch and macro rather than doing them sequentially. Likewise move extension header concatination into a single branch block only doing it if we recorded any extension header length length. Sponsored by: Netflix (partially, originally) Modified: stable/12/sys/netinet6/ip6_output.c Directory Properties: stable/12/ (props changed) Modified: stable/12/sys/netinet6/ip6_output.c ============================================================================== --- stable/12/sys/netinet6/ip6_output.c Mon Mar 2 13:13:42 2020 (r358539) +++ stable/12/sys/netinet6/ip6_output.c Mon Mar 2 13:16:18 2020 (r358540) @@ -154,10 +154,10 @@ static int copypktopts(struct ip6_pktopts *, struct ip /* - * Make an extension header from option data. hp is the source, and - * mp is the destination. + * Make an extension header from option data. hp is the source, + * mp is the destination, and _ol is the optlen. */ -#define MAKE_EXTHDR(hp, mp) \ +#define MAKE_EXTHDR(hp, mp, _ol) \ do { \ if (hp) { \ struct ip6_ext *eh = (struct ip6_ext *)(hp); \ @@ -165,6 +165,7 @@ static int copypktopts(struct ip6_pktopts *, struct ip ((eh)->ip6e_len + 1) << 3); \ if (error) \ goto freehdrs; \ + (_ol) += (*(mp))->m_len; \ } \ } while (/*CONSTCOND*/ 0) @@ -305,22 +306,23 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct ip6_hdr *ip6; struct ifnet *ifp, *origifp; struct mbuf *m = m0; - struct mbuf *mprev = NULL; + struct mbuf *mprev; int hlen, tlen, len; struct route_in6 ip6route; struct rtentry *rt = NULL; struct sockaddr_in6 *dst, src_sa, dst_sa; struct in6_addr odst; + u_char *nexthdrp; int error = 0; struct in6_ifaddr *ia = NULL; u_long mtu; int alwaysfrag, dontfrag; - u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; + u_int32_t optlen, plen = 0, unfragpartlen; struct ip6_exthdrs exthdrs; struct in6_addr src0, dst0; u_int32_t zone; struct route_in6 *ro_pmtu = NULL; - int hdrsplit = 0; + bool hdrsplit; int sw_csum, tso; int needfiblookup; uint32_t fibnum; @@ -352,13 +354,50 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, } #endif /* IPSEC */ + /* Source address validation. */ + ip6 = mtod(m, struct ip6_hdr *); + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && + (flags & IPV6_UNSPECSRC) == 0) { + error = EOPNOTSUPP; + IP6STAT_INC(ip6s_badscope); + goto bad; + } + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { + error = EOPNOTSUPP; + IP6STAT_INC(ip6s_badscope); + goto bad; + } + + /* + * If we are given packet options to add extension headers prepare them. + * Calculate the total length of the extension header chain. + * Keep the length of the unfragmentable part for fragmentation. + */ bzero(&exthdrs, sizeof(exthdrs)); + optlen = 0; + unfragpartlen = sizeof(struct ip6_hdr); if (opt) { /* Hop-by-Hop options header. */ - MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); + MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh, optlen); + /* Destination options header (1st part). */ if (opt->ip6po_rthdr) { +#ifndef RTHDR_SUPPORT_IMPLEMENTED /* + * If there is a routing header, discard the packet + * right away here. RH0/1 are obsolete and we do not + * currently support RH2/3/4. + * People trying to use RH253/254 may want to disable + * this check. + * The moment we do support any routing header (again) + * this block should check the routing type more + * selectively. + */ + error = EINVAL; + goto bad; +#endif + + /* * Destination options header (1st part). * This only makes sense with a routing header. * See Section 9.2 of RFC 3542. @@ -368,46 +407,38 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, * options, which might automatically be inserted in * the kernel. */ - MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); + MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1, + optlen); } /* Routing header. */ - MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr); + MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr, optlen); + + unfragpartlen += optlen; + + /* + * NOTE: we don't add AH/ESP length here (done in + * ip6_ipsec_output()). + */ + /* Destination options header (2nd part). */ - MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); + MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2, optlen); } /* - * Calculate the total length of the extension header chain. - * Keep the length of the unfragmentable part for fragmentation. - */ - optlen = 0; - if (exthdrs.ip6e_hbh) - optlen += exthdrs.ip6e_hbh->m_len; - if (exthdrs.ip6e_dest1) - optlen += exthdrs.ip6e_dest1->m_len; - if (exthdrs.ip6e_rthdr) - optlen += exthdrs.ip6e_rthdr->m_len; - unfragpartlen = optlen + sizeof(struct ip6_hdr); - - /* NOTE: we don't add AH/ESP length here (done in ip6_ipsec_output). */ - if (exthdrs.ip6e_dest2) - optlen += exthdrs.ip6e_dest2->m_len; - - /* * If there is at least one extension header, * separate IP6 header from the payload. */ - if (optlen && !hdrsplit) { + hdrsplit = false; + if (optlen) { if ((error = ip6_splithdr(m, &exthdrs)) != 0) { m = NULL; goto freehdrs; } m = exthdrs.ip6e_ip6; - hdrsplit++; + ip6 = mtod(m, struct ip6_hdr *); + hdrsplit = true; } - ip6 = mtod(m, struct ip6_hdr *); - /* Adjust mbuf packet header length. */ m->m_pkthdr.len += optlen; plen = m->m_pkthdr.len - sizeof(*ip6); @@ -420,77 +451,59 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, goto freehdrs; } m = exthdrs.ip6e_ip6; - hdrsplit++; + ip6 = mtod(m, struct ip6_hdr *); + hdrsplit = true; } - /* Adjust pointer. */ - ip6 = mtod(m, struct ip6_hdr *); if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0) goto freehdrs; ip6->ip6_plen = 0; } else ip6->ip6_plen = htons(plen); + nexthdrp = &ip6->ip6_nxt; - /* - * Concatenate headers and fill in next header fields. - * Here we have, on "m" - * IPv6 payload - * and we insert headers accordingly. - * Finally, we should be getting: - * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]. - * - * During the header composing process "m" points to IPv6 - * header. "mprev" points to an extension header prior to esp. - */ - u_char *nexthdrp = &ip6->ip6_nxt; - mprev = m; + if (optlen) { + /* + * Concatenate headers and fill in next header fields. + * Here we have, on "m" + * IPv6 payload + * and we insert headers accordingly. + * Finally, we should be getting: + * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]. + * + * During the header composing process "m" points to IPv6 + * header. "mprev" points to an extension header prior to esp. + */ + mprev = m; - /* - * We treat dest2 specially. This makes IPsec processing - * much easier. The goal here is to make mprev point the - * mbuf prior to dest2. - * - * Result: IPv6 dest2 payload. - * m and mprev will point to IPv6 header. - */ - if (exthdrs.ip6e_dest2) { - if (!hdrsplit) - panic("%s:%d: assumption failed: " - "hdr not split: hdrsplit %d exthdrs %p", - __func__, __LINE__, hdrsplit, &exthdrs); - exthdrs.ip6e_dest2->m_next = m->m_next; - m->m_next = exthdrs.ip6e_dest2; - *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt; - ip6->ip6_nxt = IPPROTO_DSTOPTS; - } + /* + * We treat dest2 specially. This makes IPsec processing + * much easier. The goal here is to make mprev point the + * mbuf prior to dest2. + * + * Result: IPv6 dest2 payload. + * m and mprev will point to IPv6 header. + */ + if (exthdrs.ip6e_dest2) { + if (!hdrsplit) + panic("%s:%d: assumption failed: " + "hdr not split: hdrsplit %d exthdrs %p", + __func__, __LINE__, hdrsplit, &exthdrs); + exthdrs.ip6e_dest2->m_next = m->m_next; + m->m_next = exthdrs.ip6e_dest2; + *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt; + ip6->ip6_nxt = IPPROTO_DSTOPTS; + } - /* - * Result: IPv6 hbh dest1 rthdr dest2 payload. - * m will point to IPv6 header. mprev will point to the - * extension header prior to dest2 (rthdr in the above case). - */ - MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS); - MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp, - IPPROTO_DSTOPTS); - MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp, - IPPROTO_ROUTING); - - /* If there is a routing header, discard the packet. */ - if (exthdrs.ip6e_rthdr) { - error = EINVAL; - goto bad; - } - - /* Source address validation. */ - if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && - (flags & IPV6_UNSPECSRC) == 0) { - error = EOPNOTSUPP; - IP6STAT_INC(ip6s_badscope); - goto bad; - } - if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { - error = EOPNOTSUPP; - IP6STAT_INC(ip6s_badscope); - goto bad; + /* + * Result: IPv6 hbh dest1 rthdr dest2 payload. + * m will point to IPv6 header. mprev will point to the + * extension header prior to dest2 (rthdr in the above case). + */ + MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS); + MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp, + IPPROTO_DSTOPTS); + MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp, + IPPROTO_ROUTING); } IP6STAT_INC(ip6s_localout);