Index: sys/netinet/dccp.h =================================================================== --- /dev/null +++ sys/netinet/dccp.h @@ -0,0 +1,64 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Tom Jones + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ +#ifndef _NETINET_DCCP_H_ +#define _NETINET_DCCP_H_ + +__FBSDID("$FreeBSD$"); + +/* DCCP protocol header as per RFC4340 */ +struct dccphdr { + uint16_t d_sport; + uint16_t d_dport; + uint8_t d_doff; +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t d_cscov:4, + d_ccval:4; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint8_t d_ccval:4, + d_cscov:4; +#endif + uint16_t d_cksum; +#if BYTE_ORDER == LITTLE_ENDIAN + uint8_t d_res:3, + d_type:4, + d_x:1; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint8_t d_x:1, + d_type:4, + d_res:3; +#endif + uint8_t d_seq[6]; +}; + +#define DCCP_SHORTHDR 12 +#define DCCP_LONGHDR 16 +#define DCCP_EXTHDR 0x80 + +#endif /* _NETINET_DCCP_H */ Index: sys/netinet/icmp6.h =================================================================== --- sys/netinet/icmp6.h +++ sys/netinet/icmp6.h @@ -156,6 +156,7 @@ #define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */ #define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */ #define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */ +#define ICMP6_PARAMPROB_FRAGHDRCHAIN 3 /* first fragment has incomplete header chain */ #define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */ @@ -587,6 +588,7 @@ uint64_t icp6errs_paramprob_header; uint64_t icp6errs_paramprob_nextheader; uint64_t icp6errs_paramprob_option; + uint64_t icp6errs_paramprob_fraghdrchain; uint64_t icp6errs_redirect; /* we regard redirect as an error here */ uint64_t icp6errs_unknown; }; @@ -626,6 +628,8 @@ #define icp6s_oparamprob_nextheader \ icp6s_outerrhist.icp6errs_paramprob_nextheader #define icp6s_oparamprob_option icp6s_outerrhist.icp6errs_paramprob_option +#define icp6s_oparamprob_fraghdrchain \ + icp6s_outerrhist.icp6errs_paramprob_fraghdrchain #define icp6s_oredirect icp6s_outerrhist.icp6errs_redirect #define icp6s_ounknown icp6s_outerrhist.icp6errs_unknown uint64_t icp6s_pmtuchg; /* path MTU changes */ Index: sys/netinet/in.h =================================================================== --- sys/netinet/in.h +++ sys/netinet/in.h @@ -169,7 +169,7 @@ #define IPPROTO_BLT 30 /* Bulk Data Transfer */ #define IPPROTO_NSP 31 /* Network Services */ #define IPPROTO_INP 32 /* Merit Internodal */ -#define IPPROTO_SEP 33 /* Sequential Exchange */ +#define IPPROTO_DCCP 33 /* Datagram Congestion Control Protocol */ #define IPPROTO_3PC 34 /* Third Party Connect */ #define IPPROTO_IDPR 35 /* InterDomain Policy Routing */ #define IPPROTO_XTP 36 /* XTP */ Index: sys/netinet6/frag6.c =================================================================== --- sys/netinet6/frag6.c +++ sys/netinet6/frag6.c @@ -67,6 +67,13 @@ #include /* for ECN definitions */ #include /* for ECN definitions */ +#include +#include +#include +#include +#include +#include + #include /* @@ -331,6 +338,29 @@ ) break; + /* + * If first fragment (frag offset is 0) doesn't have a valid upper + * layer header drop it per RFC7112. + */ + fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); + if (fragoff == 0 && + (ip6_upperlayerhdr(ip6f->ip6f_nxt) || + V_ip6_allow_unknown_upperlayerhdr)) { + if(!ip6_validhdrchain(&m, offset, ip6f->ip6f_nxt, frgpartlen)) { + /* remove any fragments that have arrived */ + if (q6 != head) { + IP6STAT_ADD(ip6s_fragdropped, q6->ip6q_nfrag); + frag6_freef(q6, hash); + } + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_FRAGHDRCHAIN, offset); + in6_ifstat_inc(dstifp, ifs6_reass_fail); + IP6STAT_INC(ip6s_fragdropped); + IP6Q_UNLOCK(hash); + return (IPPROTO_DONE); + } + } + if (q6 == head) { /* * the first fragment to arrive, create a reassembly queue. @@ -385,7 +415,6 @@ * If it's the 1st fragment, record the length of the * unfragmentable part and the next header of the fragment header. */ - fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); if (fragoff == 0) { q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) - sizeof(struct ip6_frag); @@ -965,3 +994,94 @@ m->m_flags |= M_FRAGMENTED; return (0); } + +/* + * Ensure that proto is an upper layer header and there is enough space for the + * upper layer header. + */ +int +ip6_validhdrchain(struct mbuf **mp, int offset, uint8_t proto, uint16_t length) +{ + struct mbuf *m; + struct ipv6_hdr *ip6; + struct ip *ip4; + struct tcphdr *tp; + struct dccphdr *dccp; + uint16_t hdrlen; + + m = *mp; + ip6 = mtod(m, struct ipv6_hdr *); + + switch (proto) { + case IPPROTO_ICMPV6: + if (length < sizeof(struct icmp6_hdr)) + return 0; + return 1; + case IPPROTO_IPV6: + if (length < sizeof(struct ip6_hdr)) + return 0; + return 1; + case IPPROTO_ESP: + if (length < sizeof(uint32_t)) + return 0; + return 1; + case IPPROTO_SCTP: + if (length < sizeof(struct sctphdr)) + return 0; + return 1; + case IPPROTO_TCP: + if (length < sizeof(struct tcphdr)) + return 0; + /* Get the tcp header in the first mbuf. */ + if (m->m_len < offset + sizeof(struct tcphdr)) + if ((m = m_pullup(m, offset + sizeof(struct tcphdr))) == NULL) { + *mp = NULL; + return 0; + } + tp = (struct tcphdr *)((caddr_t)ip6 + offset); + hdrlen = tp->th_off << 2; + if (length < hdrlen) + return 0; + return 1; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (length < sizeof(struct udphdr)) + return 0; + return 1; + case IPPROTO_DCCP: + if (length < DCCP_SHORTHDR) + return 0; + /* Get the dccp short header in the first mbuf. */ + if (m->m_len < offset + DCCP_SHORTHDR) + if ((m = m_pullup(m, offset + DCCP_SHORTHDR)) == NULL) { + *mp = NULL; + return 0; + } + dccp = (struct dccphdr *)((caddr_t)ip6 + offset); + hdrlen = dccp->d_x; + if (hdrlen & DCCP_EXTHDR && length < DCCP_LONGHDR) + return 0; + return 1; + case IPPROTO_IP: + if (length < sizeof(struct ip)) + return 0; + /* Get the ip header in the first mbuf. */ + if (m->m_len < offset + sizeof(struct ip)) + if ((m = m_pullup(m, offset + sizeof(struct ip))) == NULL) { + *mp = NULL; + return 0; + } + ip4 = (struct ip *)((caddr_t)ip6 + offset); + hdrlen = ip4->ip_hl << 2; + if (length < hdrlen) + return 0; + return 1; + case IPPROTO_GRE: + if (length < sizeof(struct grehdr)) + return 0; + return 1; + default: + /* not a known upper layer protocol */ + return 0; + } +} Index: sys/netinet6/icmp6.c =================================================================== --- sys/netinet6/icmp6.c +++ sys/netinet6/icmp6.c @@ -210,6 +210,9 @@ case ICMP6_PARAMPROB_OPTION: ICMP6STAT_INC(icp6s_oparamprob_option); return; + case ICMP6_PARAMPROB_FRAGHDRCHAIN: + ICMP6STAT_INC(icp6s_oparamprob_fraghdrchain); + return; } break; case ND_REDIRECT: @@ -535,6 +538,7 @@ break; case ICMP6_PARAMPROB_HEADER: case ICMP6_PARAMPROB_OPTION: + case ICMP6_PARAMPROB_FRAGHDRCHAIN: code = PRC_PARAMPROB; break; default: Index: sys/netinet6/in6.h =================================================================== --- sys/netinet6/in6.h +++ sys/netinet6/in6.h @@ -644,7 +644,8 @@ * queue */ #define IPV6CTL_MAXFRAGSPERPACKET 53 /* Max fragments per packet */ #define IPV6CTL_MAXFRAGBUCKETSIZE 54 /* Max reassembly queues per bucket */ -#define IPV6CTL_MAXID 55 +#define IPV6CTL_UNKNOWN_UPPERLAYERHDR 55 /* Allow unknown upper layer headers */ +#define IPV6CTL_MAXID 56 #endif /* __BSD_VISIBLE */ /* Index: sys/netinet6/in6_proto.c =================================================================== --- sys/netinet6/in6_proto.c +++ sys/netinet6/in6_proto.c @@ -399,6 +399,7 @@ * walk list every 5 sec. */ VNET_DEFINE(int, ip6_mcast_pmtu) = 0; /* enable pMTU discovery for multicast? */ VNET_DEFINE(int, ip6_v6only) = 1; +VNET_DEFINE(int, ip6_allow_unknown_upperlayerhdr) = 1; VNET_DEFINE(time_t, ip6_log_time) = (time_t)0L; #ifdef IPSTEALTH @@ -562,6 +563,9 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_V6ONLY, v6only, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_v6only), 0, "Restrict AF_INET6 sockets to IPv6 addresses only"); +SYSCTL_INT(_net_inet6_ip6, IPV6CTL_UNKNOWN_UPPERLAYERHDR, allow_unknown_upperlayerhdr, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_allow_unknown_upperlayerhdr), 0, + "Allow unknown upper layer protocols to skip header chain validation"); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_LINKLOCAL, auto_linklocal, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_auto_linklocal), 0, "Default value of per-interface flag for automatically adding an IPv6 " Index: sys/netinet6/ip6_input.c =================================================================== --- sys/netinet6/ip6_input.c +++ sys/netinet6/ip6_input.c @@ -544,6 +544,9 @@ struct in6_ifaddr *ia; struct ifnet *rcvif; u_int32_t plen; +#ifdef INVARIANTS + u_int16_t length; +#endif u_int32_t rtalert = ~0; int off = sizeof(struct ip6_hdr), nest; int nxt, ours = 0; @@ -957,6 +960,12 @@ return; } #endif /* IPSEC */ +#ifdef INVARIANTS + length = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - off; + if (ip6_upperlayerhdr(nxt)) + if (!ip6_validhdrchain(&m, off, nxt, length)) + goto bad; +#endif nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); } @@ -1847,6 +1856,29 @@ } } +/* + * Check if proto is a supported upper layer protocol. + */ +int +ip6_upperlayerhdr(uint8_t proto) +{ + switch (proto) { + case IPPROTO_ICMPV6: + case IPPROTO_IPV6: + case IPPROTO_ESP: + case IPPROTO_SCTP: + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + case IPPROTO_DCCP: + case IPPROTO_IP: + case IPPROTO_GRE: + return 1; + default: + return 0; + } +} + /* * System control for IP6 */ Index: sys/netinet6/ip6_var.h =================================================================== --- sys/netinet6/ip6_var.h +++ sys/netinet6/ip6_var.h @@ -290,6 +290,7 @@ * walk list every 5 sec. */ VNET_DECLARE(int, ip6_mcast_pmtu); /* enable pMTU discovery for multicast? */ VNET_DECLARE(int, ip6_v6only); +VNET_DECLARE(int, ip6_allow_unknown_upperlayerhdr); #define V_ip6_defhlim VNET(ip6_defhlim) #define V_ip6_defmcasthlim VNET(ip6_defmcasthlim) #define V_ip6_forwarding VNET(ip6_forwarding) @@ -297,6 +298,7 @@ #define V_ip6_rr_prune VNET(ip6_rr_prune) #define V_ip6_mcast_pmtu VNET(ip6_mcast_pmtu) #define V_ip6_v6only VNET(ip6_v6only) +#define V_ip6_allow_unknown_upperlayerhdr VNET(ip6_allow_unknown_upperlayerhdr) VNET_DECLARE(struct socket *, ip6_mrouter); /* multicast routing daemon */ VNET_DECLARE(int, ip6_sendredirects); /* send IP redirects when forwarding? */ @@ -410,8 +412,10 @@ struct ip6_pktopts *ip6_copypktopts(struct ip6_pktopts *, int); int ip6_optlen(struct inpcb *); int ip6_deletefraghdr(struct mbuf *, int, int); +int ip6_validhdrchain(struct mbuf **, int, uint8_t, uint16_t); int ip6_fragment(struct ifnet *, struct mbuf *, int, u_char, int, uint32_t); +int ip6_upperlayerhdr(uint8_t); int route6_input(struct mbuf **, int *, int); Index: usr.bin/netstat/inet6.c =================================================================== --- usr.bin/netstat/inet6.c +++ usr.bin/netstat/inet6.c @@ -1035,6 +1035,8 @@ "{N:/unrecognized next header}\n"); p_5(icp6s_oparamprob_option, "\t\t{:bad-option/%ju} " "{N:/unrecognized option}\n"); + p_5(icp6s_oparamprob_fraghdrchain, "\t\t{:bad-header-chain/%ju} " + "{N:/incomplete upper layer first fragment}\n"); p_5(icp6s_oredirect, "\t\t{:redirects/%ju} " "{N:/redirect}\n"); p_5(icp6s_ounknown, "\t\t{:unknown/%ju} {N:unknown}\n");