Index: sys/netinet/dccp.h =================================================================== --- /dev/null +++ sys/netinet/dccp.h @@ -0,0 +1,50 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 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; + uint8_t d_ccval; + uint16_t d_cksum; + uint8_t d_extseq; + uint8_t d_seq[6]; +}; + +#define IPPROTO_DCCP 33 +#define DCCP_SHORTHDR 12 +#define DCCP_LONGHDR 16 +#define DCCP_EXTHDR 0x80 + +#endif /* _NETINET_DCCP_H */ Index: sys/netinet6/frag6.c =================================================================== --- sys/netinet6/frag6.c +++ sys/netinet6/frag6.c @@ -66,6 +66,13 @@ #include /* for ECN definitions */ #include /* for ECN definitions */ +#include +#include +#include +#include +#include +#include + #include /* @@ -328,6 +335,20 @@ ) break; + /* + * If first fragment (frag offset is 0) doesn't have contain an 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) + frag6_freef(q6, hash); + goto dropfrag; + } + if (q6 == head) { /* * the first fragment to arrive, create a reassembly queue. @@ -382,7 +403,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); @@ -959,3 +979,76 @@ 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 *m, int offset, uint8_t proto, uint16_t length) +{ + struct ipv6_hdr *ip6; + struct ip *ip4; + struct tcphdr *tp; + struct dccphdr *dccp; + + uint16_t hdrlen = 0; + + 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; + 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; + dccp = (struct dccphdr *)((caddr_t)ip6 + offset); + hdrlen = dccp->d_extseq; + if (hdrlen & DCCP_EXTHDR && length < DCCP_LONGHDR) + return 0; + return 1; + case IPPROTO_IP: + if (length < sizeof(struct ip)) + 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; + } + return 0; +} Index: sys/netinet6/ip6_var.h =================================================================== --- sys/netinet6/ip6_var.h +++ sys/netinet6/ip6_var.h @@ -402,6 +402,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);