]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/frag6.c
xnu-4570.51.1.tar.gz
[apple/xnu.git] / bsd / netinet6 / frag6.c
index 6a92d73808983ea2932e9d5de7777b1d3fe4e7a9..5bdb1adf374491de0f1ad5a95debef3f41804452 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -79,6 +79,7 @@
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #include <netinet/ip.h>
+#include <netinet/ip_var.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet/icmp6.h>
@@ -204,7 +205,7 @@ frag6_restore_context(struct mbuf *m)
 static void
 frag6_icmp6_paramprob_error(struct fq6_head *diq6)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_NOTOWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_NOTOWNED);
 
        if (!MBUFQ_EMPTY(diq6)) {
                struct mbuf *merr, *merr_tmp;
@@ -227,15 +228,15 @@ frag6_icmp6_paramprob_error(struct fq6_head *diq6)
 static void
 frag6_icmp6_timeex_error(struct fq6_head *diq6)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_NOTOWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_NOTOWNED);
 
        if (!MBUFQ_EMPTY(diq6)) {
                struct mbuf *m, *m_tmp;
                MBUFQ_FOREACH_SAFE(m, diq6, m_tmp) {
                        MBUFQ_REMOVE(diq6, m);
                        MBUFQ_NEXT(m) = NULL;
-                       icmp6_error(m, ICMP6_TIME_EXCEEDED,
-                           ICMP6_TIME_EXCEED_REASSEMBLY, 0);
+                       icmp6_error_flag(m, ICMP6_TIME_EXCEEDED,
+                           ICMP6_TIME_EXCEED_REASSEMBLY, 0, 0);
                }
        }
 }
@@ -298,14 +299,8 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
        MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
 
        ip6 = mtod(m, struct ip6_hdr *);
-#ifndef PULLDOWN_TEST
        IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), goto done);
        ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset);
-#else
-       IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f));
-       if (ip6f == NULL)
-               goto done;
-#endif
 
 #ifdef IN6_IFSTAT_STRICT
        /* find the destination interface of the packet. */
@@ -362,25 +357,55 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
        /* offset now points to data portion */
        offset += sizeof(struct ip6_frag);
 
+       /*
+        * RFC 6946: Handle "atomic" fragments (offset and m bit set to 0)
+        * upfront, unrelated to any reassembly.  Just skip the fragment header.
+        */
+       if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) {
+               /*
+                * In ICMPv6 processing, we drop certain
+                * NDP messages that are not expected to
+                * have fragment header based on recommendations
+                * against security vulnerability as described in
+                * RFC 6980.
+                * We set PKTF_REASSEMBLED flag to let ICMPv6 NDP
+                * drop such packets.
+                * However there are already devices running software
+                * that are creating interface with MTU < IPv6 Min
+                * MTU. We should not have allowed that but they are
+                * out, and sending atomic NDP fragments.
+                * For that reason, we do not set the same flag here
+                * and relax the check.
+                */
+               ip6stat.ip6s_atmfrag_rcvd++;
+               in6_ifstat_inc(dstifp, ifs6_atmfrag_rcvd);
+               *offp = offset;
+               return (ip6f->ip6f_nxt);
+       }
+
        /*
         * Leverage partial checksum offload for simple UDP/IP fragments,
         * as that is the most common case.
         *
         * Perform 1's complement adjustment of octets that got included/
-        * excluded in the hardware-calculated checksum value.
+        * excluded in the hardware-calculated checksum value.  Also take
+        * care of any trailing bytes and subtract out their partial sum.
         */
        if (ip6f->ip6f_nxt == IPPROTO_UDP &&
            offset == (sizeof (*ip6) + sizeof (*ip6f)) &&
            (m->m_pkthdr.csum_flags &
            (CSUM_DATA_VALID | CSUM_PARTIAL | CSUM_PSEUDO_HDR)) ==
            (CSUM_DATA_VALID | CSUM_PARTIAL)) {
-               uint32_t start;
+               uint32_t start = m->m_pkthdr.csum_rx_start;
+               uint32_t ip_len = (sizeof (*ip6) + ntohs(ip6->ip6_plen));
+               int32_t trailer = (m_pktlen(m) - ip_len);
+               uint32_t swbytes = (uint32_t)trailer;
 
-               start = m->m_pkthdr.csum_rx_start;
                csum = m->m_pkthdr.csum_rx_val;
 
-               if (start != offset) {
-                       uint16_t s, d;
+               ASSERT(trailer >= 0);
+               if (start != offset || trailer != 0) {
+                       uint16_t s = 0, d = 0;
 
                        if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) {
                                s = ip6->ip6_src.s6_addr16[1];
@@ -392,7 +417,12 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
                        }
 
                        /* callee folds in sum */
-                       csum = m_adj_sum16(m, start, offset, csum);
+                       csum = m_adj_sum16(m, start, offset,
+                           (ip_len - offset), csum);
+                       if (offset > start)
+                               swbytes += (offset - start);
+                       else
+                               swbytes += (start - offset);
 
                        if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
                                ip6->ip6_src.s6_addr16[1] = s;
@@ -401,6 +431,11 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
 
                }
                csum_flags = m->m_pkthdr.csum_flags;
+
+               if (swbytes != 0)
+                       udp_in6_cksum_stats(swbytes);
+               if (trailer != 0)
+                       m_adj(m, -trailer);
        } else {
                csum = 0;
                csum_flags = 0;
@@ -787,9 +822,18 @@ insert:
        frag6_nfrags -= q6->ip6q_nfrag;
        ip6q_free(q6);
 
-       if (m->m_flags & M_PKTHDR)      /* Isn't it always true? */
+       if (m->m_flags & M_PKTHDR) {    /* Isn't it always true? */
                m_fixhdr(m);
-
+               /*
+                * Mark packet as reassembled
+                * In ICMPv6 processing, we drop certain
+                * NDP messages that are not expected to
+                * have fragment header based on recommendations
+                * against security vulnerability as described in
+                * RFC 6980.
+                */
+               m->m_pkthdr.pkt_flags |= PKTF_REASSEMBLED;
+       }
        ip6stat.ip6s_reassembled++;
 
        /*
@@ -844,7 +888,7 @@ frag6_freef(struct ip6q *q6, struct fq6_head *dfq6, struct fq6_head *diq6)
 {
        struct ip6asfrag *af6, *down6;
 
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_OWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
 
        for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6;
             af6 = down6) {
@@ -887,7 +931,7 @@ frag6_freef(struct ip6q *q6, struct fq6_head *dfq6, struct fq6_head *diq6)
 void
 frag6_enq(struct ip6asfrag *af6, struct ip6asfrag *up6)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_OWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
 
        af6->ip6af_up = up6;
        af6->ip6af_down = up6->ip6af_down;
@@ -901,7 +945,7 @@ frag6_enq(struct ip6asfrag *af6, struct ip6asfrag *up6)
 void
 frag6_deq(struct ip6asfrag *af6)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_OWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
 
        af6->ip6af_up->ip6af_down = af6->ip6af_down;
        af6->ip6af_down->ip6af_up = af6->ip6af_up;
@@ -910,7 +954,7 @@ frag6_deq(struct ip6asfrag *af6)
 void
 frag6_insque(struct ip6q *new, struct ip6q *old)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_OWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
 
        new->ip6q_prev = old;
        new->ip6q_next = old->ip6q_next;
@@ -921,7 +965,7 @@ frag6_insque(struct ip6q *new, struct ip6q *old)
 void
 frag6_remque(struct ip6q *p6)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_OWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
 
        p6->ip6q_prev->ip6q_next = p6->ip6q_next;
        p6->ip6q_next->ip6q_prev = p6->ip6q_prev;
@@ -992,7 +1036,7 @@ frag6_timeout(void *arg)
 static void
 frag6_sched_timeout(void)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_OWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
 
        if (!frag6_timeout_run && frag6_nfragpackets > 0) {
                frag6_timeout_run = 1;
@@ -1096,7 +1140,7 @@ ip6af_free(struct ip6asfrag *af6)
 static void
 ip6q_updateparams(void)
 {
-       lck_mtx_assert(&ip6qlock, LCK_MTX_ASSERT_OWNED);
+       LCK_MTX_ASSERT(&ip6qlock, LCK_MTX_ASSERT_OWNED);
        /*
         * -1 for unlimited allocation.
         */