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,27 @@ ) 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_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 +413,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 +992,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/ip6_var.h =================================================================== --- sys/netinet6/ip6_var.h +++ sys/netinet6/ip6_var.h @@ -410,6 +410,7 @@ 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); 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");