Index: sys/netinet6/frag6.c =================================================================== --- sys/netinet6/frag6.c +++ sys/netinet6/frag6.c @@ -66,6 +66,11 @@ #include /* for ECN definitions */ #include /* for ECN definitions */ +#include +#include +#include +#include + #include /* @@ -83,6 +88,8 @@ static void frag6_remque(struct ip6q *, uint32_t bucket); static void frag6_freef(struct ip6q *, uint32_t bucket); +static int ip6_validhdrchain(uint8_t, uint16_t); + struct ip6qbucket { struct ip6q ip6q; struct mtx lock; @@ -328,6 +335,20 @@ ) break; + /* + * If first fragment (frag offset is 0) doesn't have carry an upper + * layer header drop it per RFC7112 + */ + fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); + if (fragoff == 0 && + !ip6_validhdrchain(ip6f->ip6f_nxt, frgpartlen)) { + + /* remove any fragments that have arrived in this chain */ + 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,43 @@ 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. + */ +static int +ip6_validhdrchain(uint8_t proto, uint16_t length) +{ + 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 < TCP_MAXHLEN) + return 0; + return 1; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (length < sizeof(struct udphdr)) + return 0; + return 1; + default: + /* not a known upper layer protocol */ + return 0; + } + return 0; +}