]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/ip6_forward.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / netinet6 / ip6_forward.c
index 8498211c71ee3168bec6da27b85e2282e3984cc9..02cac6958803845f9b81826854edada780b472ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -90,9 +90,7 @@
 
 #if IPSEC
 #include <netinet6/ipsec.h>
-#if INET6
 #include <netinet6/ipsec6.h>
-#endif
 #include <netkey/key.h>
 extern int ipsec_bypass;
 #endif /* IPSEC */
@@ -100,7 +98,6 @@ extern int ipsec_bypass;
 #include <net/net_osdep.h>
 
 #if DUMMYNET
-#include <netinet/ip_fw.h>
 #include <netinet/ip_dummynet.h>
 #endif /* DUMMYNET */
 
@@ -132,7 +129,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        boolean_t proxy = FALSE;
        struct mbuf *mcopy = NULL;
        struct ifnet *ifp, *rcvifp, *origifp;   /* maybe unnecessary */
-       u_int32_t inzone, outzone, len;
+       u_int32_t inzone, outzone, len = 0, pktcnt = 0;
        struct in6_addr src_in6, dst_in6;
        uint64_t curtime = net_uptime();
 #if IPSEC
@@ -141,7 +138,10 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        unsigned int ifscope = IFSCOPE_NONE;
 #if PF
        struct pf_mtag *pf_mtag;
+       struct pf_fragment_tag *pf_ftagp, pf_ftag;
+       boolean_t pf_ftag_valid = FALSE;
 #endif /* PF */
+       uint32_t mpktlen = 0;
 
        /*
         * In the prefix proxying case, the route to the proxied node normally
@@ -164,10 +164,23 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 
 #if PF
        pf_mtag = pf_find_mtag(m);
+       /*
+        * save the PF fragmentation metadata as m_copy() removes the
+        * mbufs tags from the original mbuf.
+        */
+       pf_ftagp = pf_find_fragment_tag(m);
+       if (pf_ftagp != NULL) {
+               ASSERT(pf_mtag->pftag_flags & PF_TAG_REASSEMBLED);
+               pf_ftag = *pf_ftagp;
+               pf_ftag_valid = TRUE;
+               mpktlen = pf_ftag.ft_maxlen;
+               ASSERT(mpktlen);
+       }
        if (pf_mtag != NULL && pf_mtag->pftag_rtableid != IFSCOPE_NONE) {
                ifscope = pf_mtag->pftag_rtableid;
        }
-
+       pf_mtag = NULL;
+       pf_ftagp = NULL;
        /*
         * If the caller provides a route which is on a different interface
         * than the one specified for scoped forwarding, discard the route
@@ -377,7 +390,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                                break;
                        default:
                                printf("ip6_output (ipsec): error code %d\n", error);
-                       /* fall through */
+                               OS_FALLTHROUGH;
                        case ENOENT:
                                /* don't show these error codes to the user */
                                break;
@@ -543,7 +556,11 @@ skip_ipsec:
                return NULL;
        }
 
-       if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) {
+       if (mpktlen == 0) {
+               mpktlen = m->m_pkthdr.len;
+       }
+
+       if (mpktlen > rt->rt_ifp->if_mtu) {
                in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
                if (mcopy) {
                        uint32_t mtu;
@@ -704,6 +721,14 @@ skip_ipsec:
 
 #if PF
        if (PF_IS_ENABLED) {
+               /*
+                * PF refragments any packet which it reassembled due to scrub
+                * rules, in which case it will set the PF_TAG_REFRAGMENTED
+                * flag in PF mbuf tag.
+                */
+               if (pf_ftag_valid) {
+                       pf_copy_fragment_tag(m, &pf_ftag, M_DONTWAIT);
+               }
 #if DUMMYNET
                struct ip_fw_args args;
                bzero(&args, sizeof(args));
@@ -729,6 +754,31 @@ skip_ipsec:
                        /* Already freed by callee */
                        goto senderr;
                }
+
+               pf_mtag = pf_find_mtag(m);
+               /*
+                * refragmented packets from PF.
+                */
+               if ((pf_mtag->pftag_flags & PF_TAG_REFRAGMENTED) != 0) {
+                       struct mbuf *t;
+
+                       pf_mtag->pftag_flags &= ~PF_TAG_REFRAGMENTED;
+                       /* for statistics */
+                       t = m;
+                       while (t != NULL) {
+                               pktcnt++;
+                               len += m_pktlen(t);
+                               t = t->m_nextpkt;
+                       }
+
+                       /*
+                        * nd6_output() frees packetchain in both success and
+                        * failure cases.
+                        */
+                       error = nd6_output(ifp, origifp, m, dst, rt, NULL);
+                       m = NULL;
+                       goto sent;
+               }
                /*
                 * We do not use ip6 header again in the code below,
                 * however still adding the bit here so that any new
@@ -740,21 +790,23 @@ skip_ipsec:
 #endif /* PF */
 
        len = m_pktlen(m);
+       pktcnt = 1;
        error = nd6_output(ifp, origifp, m, dst, rt, NULL);
+sent:
        if (error) {
-               in6_ifstat_inc(ifp, ifs6_out_discard);
-               ip6stat.ip6s_cantforward++;
+               in6_ifstat_add(ifp, ifs6_out_discard, pktcnt);
+               ip6stat.ip6s_cantforward += pktcnt;
        } else {
                /*
                 * Increment stats on the source interface; the ones
                 * for destination interface has been taken care of
                 * during output above by virtue of PKTF_FORWARDED.
                 */
-               rcvifp->if_fpackets++;
+               rcvifp->if_fpackets += pktcnt;
                rcvifp->if_fbytes += len;
 
-               ip6stat.ip6s_forward++;
-               in6_ifstat_inc(ifp, ifs6_out_forward);
+               ip6stat.ip6s_forward += pktcnt;
+               in6_ifstat_add(ifp, ifs6_out_forward, pktcnt);
                if (type) {
                        ip6stat.ip6s_redirectsent++;
                } else {