]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/ip6_forward.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / bsd / netinet6 / ip6_forward.c
index 42156858ef82deff0a40b3cdfa33c546fccaac1e..02cac6958803845f9b81826854edada780b472ec 100644 (file)
@@ -1,8 +1,8 @@
 /*
 /*
- * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
 
 #if IPSEC
 #include <netinet6/ipsec.h>
 
 #if IPSEC
 #include <netinet6/ipsec.h>
-#if INET6
 #include <netinet6/ipsec6.h>
 #include <netinet6/ipsec6.h>
-#endif
 #include <netkey/key.h>
 extern int ipsec_bypass;
 #endif /* IPSEC */
 
 #include <netkey/key.h>
 extern int ipsec_bypass;
 #endif /* IPSEC */
 
-#include <netinet6/ip6_fw.h>
-
 #include <net/net_osdep.h>
 
 #include <net/net_osdep.h>
 
+#if DUMMYNET
+#include <netinet/ip_dummynet.h>
+#endif /* DUMMYNET */
+
 #if PF
 #include <net/pfvar.h>
 #endif /* PF */
 #if PF
 #include <net/pfvar.h>
 #endif /* PF */
@@ -128,24 +128,59 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        int error, type = 0, code = 0;
        boolean_t proxy = FALSE;
        struct mbuf *mcopy = NULL;
        int error, type = 0, code = 0;
        boolean_t proxy = FALSE;
        struct mbuf *mcopy = NULL;
-       struct ifnet *ifp, *origifp;    /* maybe unnecessary */
-       u_int32_t inzone, outzone;
+       struct ifnet *ifp, *rcvifp, *origifp;   /* maybe unnecessary */
+       u_int32_t inzone, outzone, len = 0, pktcnt = 0;
        struct in6_addr src_in6, dst_in6;
        struct in6_addr src_in6, dst_in6;
+       uint64_t curtime = net_uptime();
 #if IPSEC
        struct secpolicy *sp = NULL;
 #endif
 #if IPSEC
        struct secpolicy *sp = NULL;
 #endif
-       struct timeval timenow;
        unsigned int ifscope = IFSCOPE_NONE;
 #if PF
        struct pf_mtag *pf_mtag;
        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 */
 #endif /* PF */
+       uint32_t mpktlen = 0;
+
+       /*
+        * In the prefix proxying case, the route to the proxied node normally
+        * gets created by nd6_prproxy_ns_output(), as part of forwarding a
+        * NS (NUD/AR) packet to the proxied node.  In the event that such
+        * packet did not arrive in time before the correct route gets created,
+        * ip6_input() would have performed a rtalloc() which most likely will
+        * create the wrong cloned route; this route points back to the same
+        * interface as the inbound interface, since the parent non-scoped
+        * prefix route points there.  Therefore we check if that is the case
+        * and perform the necessary fixup to get the correct route installed.
+        */
+       if (!srcrt && nd6_prproxy &&
+           (rt = ip6forward_rt->ro_rt) != NULL && (rt->rt_flags & RTF_PROXY)) {
+               nd6_proxy_find_fwdroute(m->m_pkthdr.rcvif, ip6forward_rt);
+               if ((rt = ip6forward_rt->ro_rt) != NULL) {
+                       ifscope = rt->rt_ifp->if_index;
+               }
+       }
 
 
-       getmicrotime(&timenow);
 #if PF
        pf_mtag = pf_find_mtag(m);
 #if PF
        pf_mtag = pf_find_mtag(m);
-       if (pf_mtag != NULL && pf_mtag->pftag_rtableid != IFSCOPE_NONE)
+       /*
+        * 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;
                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
        /*
         * If the caller provides a route which is on a different interface
         * than the one specified for scoped forwarding, discard the route
@@ -155,8 +190,8 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                RT_LOCK(rt);
                if (rt->rt_ifp->if_index != ifscope) {
                        RT_UNLOCK(rt);
                RT_LOCK(rt);
                if (rt->rt_ifp->if_index != ifscope) {
                        RT_UNLOCK(rt);
-                       rtfree(rt);
-                       rt = ip6forward_rt->ro_rt = NULL;
+                       ROUTE_RELEASE(ip6forward_rt);
+                       rt = NULL;
                } else {
                        RT_UNLOCK(rt);
                }
                } else {
                        RT_UNLOCK(rt);
                }
@@ -175,24 +210,23 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                if (ipsec6_in_reject(m, NULL)) {
                        IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio);
                        m_freem(m);
                if (ipsec6_in_reject(m, NULL)) {
                        IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio);
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
        }
 #endif /*IPSEC*/
 
        /*
                }
        }
 #endif /*IPSEC*/
 
        /*
-        * Do not forward packets to multicast destination (should be handled
-        * by ip6_mforward().
+        * Do not forward packets to multicast destination.
         * Do not forward packets with unspecified source.  It was discussed
         * in July 2000, on ipngwg mailing list.
         */
         * Do not forward packets with unspecified source.  It was discussed
         * in July 2000, on ipngwg mailing list.
         */
-       if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 ||
+       if ((m->m_flags & (M_BCAST | M_MCAST)) != 0 ||
            IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
            IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
                ip6stat.ip6s_cantforward++;
                /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
            IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
            IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
                ip6stat.ip6s_cantforward++;
                /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
-               if (ip6_log_time + ip6_log_interval < timenow.tv_sec) {
-                       ip6_log_time = timenow.tv_sec;
+               if (ip6_log_time + ip6_log_interval < curtime) {
+                       ip6_log_time = curtime;
                        log(LOG_DEBUG,
                            "cannot forward "
                            "from %s to %s nxt %d received on %s\n",
                        log(LOG_DEBUG,
                            "cannot forward "
                            "from %s to %s nxt %d received on %s\n",
@@ -202,14 +236,14 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                            if_name(m->m_pkthdr.rcvif));
                }
                m_freem(m);
                            if_name(m->m_pkthdr.rcvif));
                }
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
 
        if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
                /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
        }
 
        if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
                /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
-               icmp6_error(m, ICMP6_TIME_EXCEEDED,
-                               ICMP6_TIME_EXCEED_TRANSIT, 0);
-               return (NULL);
+               icmp6_error_flag(m, ICMP6_TIME_EXCEEDED,
+                   ICMP6_TIME_EXCEED_TRANSIT, 0, 0);
+               return NULL;
        }
 
        /*
        }
 
        /*
@@ -226,8 +260,9 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                 * Also skip IPsec forwarding path processing as this
                 * packet is not to be forwarded.
                 */
                 * Also skip IPsec forwarding path processing as this
                 * packet is not to be forwarded.
                 */
-               if (proxy)
+               if (proxy) {
                        goto skip_ipsec;
                        goto skip_ipsec;
+               }
        }
 
        ip6->ip6_hlim -= IPV6_HLIMDEC;
        }
 
        ip6->ip6_hlim -= IPV6_HLIMDEC;
@@ -244,8 +279,9 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
 
 #if IPSEC
        mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
 
 #if IPSEC
-       if (ipsec_bypass != 0)
+       if (ipsec_bypass != 0) {
                goto skip_ipsec;
                goto skip_ipsec;
+       }
        /* get a security policy for this packet */
        sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
            &error);
        /* get a security policy for this packet */
        sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
            &error);
@@ -260,7 +296,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 #endif
                }
                m_freem(m);
 #endif
                }
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
 
        error = 0;
        }
 
        error = 0;
@@ -268,7 +304,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        /* check policy */
        switch (sp->policy) {
        case IPSEC_POLICY_DISCARD:
        /* check policy */
        switch (sp->policy) {
        case IPSEC_POLICY_DISCARD:
-        case IPSEC_POLICY_GENERATE:
+       case IPSEC_POLICY_GENERATE:
                /*
                 * This packet is just discarded.
                 */
                /*
                 * This packet is just discarded.
                 */
@@ -283,7 +319,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 #endif
                }
                m_freem(m);
 #endif
                }
                m_freem(m);
-               return (NULL);
+               return NULL;
 
        case IPSEC_POLICY_BYPASS:
        case IPSEC_POLICY_NONE:
 
        case IPSEC_POLICY_BYPASS:
        case IPSEC_POLICY_NONE:
@@ -305,7 +341,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 #endif
                        }
                        m_freem(m);
 #endif
                        }
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                /* do IPsec */
                break;
                }
                /* do IPsec */
                break;
@@ -318,61 +354,61 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                goto skip_ipsec;
        }
 
                goto skip_ipsec;
        }
 
-    {
-       struct ipsec_output_state state;
+       {
+               struct ipsec_output_state state;
 
 
-       /*
-        * All the extension headers will become inaccessible
-        * (since they can be encrypted).
-        * Don't panic, we need no more updates to extension headers
-        * on inner IPv6 packet (since they are now encapsulated).
-        *
-        * IPv6 [ESP|AH] IPv6 [extension headers] payload
-        */
-       bzero(&state, sizeof(state));
-       state.m = m;
-       state.dst = NULL;       /* update at ipsec6_output_tunnel() */
-
-       error = ipsec6_output_tunnel(&state, sp, 0);
-       key_freesp(sp, KEY_SADB_UNLOCKED);
-       if (state.tunneled == 4)
-               return (NULL);  /* packet is gone - sent over IPv4 */
-               
-       m = state.m;
-       if (state.ro.ro_rt) {
-               rtfree(state.ro.ro_rt);
-               state.ro.ro_rt = NULL;
-       }
-       if (error) {
-               /* mbuf is already reclaimed in ipsec6_output_tunnel. */
-               switch (error) {
-               case EHOSTUNREACH:
-               case ENETUNREACH:
-               case EMSGSIZE:
-               case ENOBUFS:
-               case ENOMEM:
-                       break;
-               default:
-                       printf("ip6_output (ipsec): error code %d\n", error);
-                       /* fall through */
-               case ENOENT:
-                       /* don't show these error codes to the user */
-                       break;
+               /*
+                * All the extension headers will become inaccessible
+                * (since they can be encrypted).
+                * Don't panic, we need no more updates to extension headers
+                * on inner IPv6 packet (since they are now encapsulated).
+                *
+                * IPv6 [ESP|AH] IPv6 [extension headers] payload
+                */
+               bzero(&state, sizeof(state));
+               state.m = m;
+               state.dst = NULL; /* update at ipsec6_output_tunnel() */
+
+               error = ipsec6_output_tunnel(&state, sp, 0);
+               key_freesp(sp, KEY_SADB_UNLOCKED);
+               if (state.tunneled == 4) {
+                       ROUTE_RELEASE(&state.ro);
+                       return NULL; /* packet is gone - sent over IPv4 */
                }
                }
-               ip6stat.ip6s_cantforward++;
-               if (mcopy) {
+
+               m = state.m;
+               ROUTE_RELEASE(&state.ro);
+
+               if (error) {
+                       /* mbuf is already reclaimed in ipsec6_output_tunnel. */
+                       switch (error) {
+                       case EHOSTUNREACH:
+                       case ENETUNREACH:
+                       case EMSGSIZE:
+                       case ENOBUFS:
+                       case ENOMEM:
+                               break;
+                       default:
+                               printf("ip6_output (ipsec): error code %d\n", error);
+                               OS_FALLTHROUGH;
+                       case ENOENT:
+                               /* don't show these error codes to the user */
+                               break;
+                       }
+                       ip6stat.ip6s_cantforward++;
+                       if (mcopy) {
 #if 0
 #if 0
-                       /* XXX: what icmp ? */
+                               /* XXX: what icmp ? */
 #else
 #else
-                       m_freem(mcopy);
+                               m_freem(mcopy);
 #endif
 #endif
+                       }
+                       m_freem(m);
+                       return NULL;
                }
                }
-               m_freem(m);
-               return (NULL);
        }
        }
-    }
-    skip_ipsec:
 #endif /* IPSEC */
 #endif /* IPSEC */
+skip_ipsec:
 
        dst = (struct sockaddr_in6 *)&ip6forward_rt->ro_dst;
        if ((rt = ip6forward_rt->ro_rt) != NULL) {
 
        dst = (struct sockaddr_in6 *)&ip6forward_rt->ro_dst;
        if ((rt = ip6forward_rt->ro_rt) != NULL) {
@@ -381,19 +417,19 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                RT_ADDREF_LOCKED(rt);
        }
 
                RT_ADDREF_LOCKED(rt);
        }
 
+       VERIFY(rt == NULL || rt == ip6forward_rt->ro_rt);
        if (!srcrt) {
                /*
                 * ip6forward_rt->ro_dst.sin6_addr is equal to ip6->ip6_dst
                 */
        if (!srcrt) {
                /*
                 * ip6forward_rt->ro_dst.sin6_addr is equal to ip6->ip6_dst
                 */
-               if (rt == NULL || !(rt->rt_flags & RTF_UP) ||
-                   rt->generation_id != route_generation) {
+               if (ROUTE_UNUSABLE(ip6forward_rt)) {
                        if (rt != NULL) {
                                /* Release extra ref */
                                RT_REMREF_LOCKED(rt);
                                RT_UNLOCK(rt);
                        if (rt != NULL) {
                                /* Release extra ref */
                                RT_REMREF_LOCKED(rt);
                                RT_UNLOCK(rt);
-                               rtfree(rt);
-                               ip6forward_rt->ro_rt = NULL;
                        }
                        }
+                       ROUTE_RELEASE(ip6forward_rt);
+
                        /* this probably fails but give it a try again */
                        rtalloc_scoped_ign((struct route *)ip6forward_rt,
                            RTF_PRCLONING, ifscope);
                        /* this probably fails but give it a try again */
                        rtalloc_scoped_ign((struct route *)ip6forward_rt,
                            RTF_PRCLONING, ifscope);
@@ -407,23 +443,23 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                if (rt == NULL) {
                        ip6stat.ip6s_noroute++;
                        in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
                if (rt == NULL) {
                        ip6stat.ip6s_noroute++;
                        in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
-                       if (mcopy)
+                       if (mcopy) {
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
-                                           ICMP6_DST_UNREACH_NOROUTE, 0);
+                                   ICMP6_DST_UNREACH_NOROUTE, 0);
+                       }
                        m_freem(m);
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                RT_LOCK_ASSERT_HELD(rt);
                }
                RT_LOCK_ASSERT_HELD(rt);
-       } else if (rt == NULL || !(rt->rt_flags & RTF_UP) ||
-           !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) ||
-           rt->generation_id != route_generation) {
+       } else if (ROUTE_UNUSABLE(ip6forward_rt) ||
+           !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
                if (rt != NULL) {
                        /* Release extra ref */
                        RT_REMREF_LOCKED(rt);
                        RT_UNLOCK(rt);
                if (rt != NULL) {
                        /* Release extra ref */
                        RT_REMREF_LOCKED(rt);
                        RT_UNLOCK(rt);
-                       rtfree(rt);
-                       ip6forward_rt->ro_rt = NULL;
                }
                }
+               ROUTE_RELEASE(ip6forward_rt);
+
                bzero(dst, sizeof(*dst));
                dst->sin6_len = sizeof(struct sockaddr_in6);
                dst->sin6_family = AF_INET6;
                bzero(dst, sizeof(*dst));
                dst->sin6_len = sizeof(struct sockaddr_in6);
                dst->sin6_family = AF_INET6;
@@ -434,11 +470,12 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                if ((rt = ip6forward_rt->ro_rt) == NULL) {
                        ip6stat.ip6s_noroute++;
                        in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
                if ((rt = ip6forward_rt->ro_rt) == NULL) {
                        ip6stat.ip6s_noroute++;
                        in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
-                       if (mcopy)
+                       if (mcopy) {
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
                                    ICMP6_DST_UNREACH_NOROUTE, 0);
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
                                    ICMP6_DST_UNREACH_NOROUTE, 0);
+                       }
                        m_freem(m);
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                RT_LOCK(rt);
                /* Take an extra ref for ourselves */
                }
                RT_LOCK(rt);
                /* Take an extra ref for ourselves */
@@ -457,17 +494,21 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
         */
        src_in6 = ip6->ip6_src;
        if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) {
         */
        src_in6 = ip6->ip6_src;
        if (in6_setscope(&src_in6, rt->rt_ifp, &outzone)) {
+               RT_REMREF_LOCKED(rt);
+               RT_UNLOCK(rt);
                /* XXX: this should not happen */
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
                /* XXX: this should not happen */
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
        if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) {
        }
        if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) {
+               RT_REMREF_LOCKED(rt);
+               RT_UNLOCK(rt);
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
 
        if (inzone != outzone && !proxy) {
        }
 
        if (inzone != outzone && !proxy) {
@@ -475,8 +516,8 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                ip6stat.ip6s_badscope++;
                in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
 
                ip6stat.ip6s_badscope++;
                in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
 
-               if (ip6_log_time + ip6_log_interval < timenow.tv_sec) {
-                       ip6_log_time = timenow.tv_sec;
+               if (ip6_log_time + ip6_log_interval < curtime) {
+                       ip6_log_time = curtime;
                        log(LOG_DEBUG,
                            "cannot forward "
                            "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
                        log(LOG_DEBUG,
                            "cannot forward "
                            "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
@@ -490,10 +531,10 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                RT_UNLOCK(rt);
                if (mcopy) {
                        icmp6_error(mcopy, ICMP6_DST_UNREACH,
                RT_UNLOCK(rt);
                if (mcopy) {
                        icmp6_error(mcopy, ICMP6_DST_UNREACH,
-                                   ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
+                           ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
                }
                m_freem(m);
                }
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
 
        /*
        }
 
        /*
@@ -507,13 +548,19 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 ||
            in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 ||
            inzone != outzone) {
        if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 ||
            in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 ||
            inzone != outzone) {
+               RT_REMREF_LOCKED(rt);
+               RT_UNLOCK(rt);
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
-               return (NULL);
+               return NULL;
+       }
+
+       if (mpktlen == 0) {
+               mpktlen = m->m_pkthdr.len;
        }
 
        }
 
-       if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) {
+       if (mpktlen > rt->rt_ifp->if_mtu) {
                in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
                if (mcopy) {
                        uint32_t mtu;
                in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
                if (mcopy) {
                        uint32_t mtu;
@@ -533,20 +580,22 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                         * encapsulated packet as "rt->rt_ifp".
                         */
                        sp2 = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND,
                         * encapsulated packet as "rt->rt_ifp".
                         */
                        sp2 = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND,
-                               IP_FORWARDING, &ipsecerror);
+                           IP_FORWARDING, &ipsecerror);
                        if (sp2) {
                                ipsechdrsiz = ipsec6_hdrsiz(mcopy,
                        if (sp2) {
                                ipsechdrsiz = ipsec6_hdrsiz(mcopy,
-                                       IPSEC_DIR_OUTBOUND, NULL);
-                               if (ipsechdrsiz < mtu)
+                                   IPSEC_DIR_OUTBOUND, NULL);
+                               if (ipsechdrsiz < mtu) {
                                        mtu -= ipsechdrsiz;
                                        mtu -= ipsechdrsiz;
+                               }
                                key_freesp(sp2, KEY_SADB_UNLOCKED);
                        }
                        /*
                         * if mtu becomes less than minimum MTU,
                         * tell minimum MTU (and I'll need to fragment it).
                         */
                                key_freesp(sp2, KEY_SADB_UNLOCKED);
                        }
                        /*
                         * if mtu becomes less than minimum MTU,
                         * tell minimum MTU (and I'll need to fragment it).
                         */
-                       if (mtu < IPV6_MMTU)
+                       if (mtu < IPV6_MMTU) {
                                mtu = IPV6_MMTU;
                                mtu = IPV6_MMTU;
+                       }
 #endif
                        /* Release extra ref */
                        RT_REMREF_LOCKED(rt);
 #endif
                        /* Release extra ref */
                        RT_REMREF_LOCKED(rt);
@@ -558,11 +607,12 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                        RT_UNLOCK(rt);
                }
                m_freem(m);
                        RT_UNLOCK(rt);
                }
                m_freem(m);
-               return (NULL);
-       }
+               return NULL;
+       }
 
 
-       if (rt->rt_flags & RTF_GATEWAY)
+       if (rt->rt_flags & RTF_GATEWAY) {
                dst = (struct sockaddr_in6 *)(void *)rt->rt_gateway;
                dst = (struct sockaddr_in6 *)(void *)rt->rt_gateway;
+       }
 
        /*
         * If we are to forward the packet using the same interface
 
        /*
         * If we are to forward the packet using the same interface
@@ -575,7 +625,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
         */
        if (!proxy &&
            ip6_sendredirects && rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
         */
        if (!proxy &&
            ip6_sendredirects && rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
-           (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) {
+           (rt->rt_flags & (RTF_DYNAMIC | RTF_MODIFIED)) == 0) {
                if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) {
                        /*
                         * If the incoming interface is equal to the outgoing
                if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) != 0) {
                        /*
                         * If the incoming interface is equal to the outgoing
@@ -587,38 +637,15 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                         * type/code is based on suggestion by Rich Draves.
                         * not sure if it is the best pick.
                         */
                         * type/code is based on suggestion by Rich Draves.
                         * not sure if it is the best pick.
                         */
-                       RT_REMREF_LOCKED(rt);   /* Release extra ref */
+                       RT_REMREF_LOCKED(rt);   /* Release extra ref */
                        RT_UNLOCK(rt);
                        icmp6_error(mcopy, ICMP6_DST_UNREACH,
                        RT_UNLOCK(rt);
                        icmp6_error(mcopy, ICMP6_DST_UNREACH,
-                                   ICMP6_DST_UNREACH_ADDR, 0);
+                           ICMP6_DST_UNREACH_ADDR, 0);
                        m_freem(m);
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                type = ND_REDIRECT;
        }
                }
                type = ND_REDIRECT;
        }
-
-#if IPFW2
-       /*
-        * Check with the firewall...
-        */
-       if (ip6_fw_enable && ip6_fw_chk_ptr) {
-               u_short port = 0;
-               ifp = rt->rt_ifp;
-               /* Drop the lock but retain the extra ref */
-               RT_UNLOCK(rt);
-               /* If ipfw says divert, we have to just drop packet */
-               if (ip6_fw_chk_ptr(&ip6, ifp, &port, &m)) {
-                       m_freem(m);
-                       goto freecopy;
-               }
-               if (!m) {
-                       goto freecopy;
-               }
-               /* We still have the extra ref on rt */
-               RT_LOCK(rt);
-       }
-#endif
-
        /*
         * Fake scoped addresses. Note that even link-local source or
         * destinaion can appear, if the originating node just sends the
        /*
         * Fake scoped addresses. Note that even link-local source or
         * destinaion can appear, if the originating node just sends the
@@ -638,24 +665,33 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                 *      rthdr. (itojun)
                 */
 #if 1
                 *      rthdr. (itojun)
                 */
 #if 1
-               if (0)
+               if ((0))
 #else
 #else
-               if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0)
+               if ((rt->rt_flags & (RTF_BLACKHOLE | RTF_REJECT)) == 0)
 #endif
                {
                        printf("ip6_forward: outgoing interface is loopback. "
 #endif
                {
                        printf("ip6_forward: outgoing interface is loopback. "
-                               "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
-                               ip6_sprintf(&ip6->ip6_src),
-                               ip6_sprintf(&ip6->ip6_dst),
-                               ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif),
-                               if_name(rt->rt_ifp));
+                           "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
+                           ip6_sprintf(&ip6->ip6_src),
+                           ip6_sprintf(&ip6->ip6_dst),
+                           ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif),
+                           if_name(rt->rt_ifp));
                }
 
                /* we can just use rcvif in forwarding. */
                }
 
                /* we can just use rcvif in forwarding. */
-               origifp = m->m_pkthdr.rcvif;
-       }
-       else
+               origifp = rcvifp = m->m_pkthdr.rcvif;
+       } else if (nd6_prproxy) {
+               /*
+                * In the prefix proxying case, we need to inform nd6_output()
+                * about the inbound interface, so that any subsequent NS
+                * packets generated by nd6_prproxy_ns_output() will not be
+                * sent back to that same interface.
+                */
+               origifp = rcvifp = m->m_pkthdr.rcvif;
+       } else {
+               rcvifp = m->m_pkthdr.rcvif;
                origifp = rt->rt_ifp;
                origifp = rt->rt_ifp;
+       }
        /*
         * clear embedded scope identifiers if necessary.
         * in6_clearscope will touch the addresses only when necessary.
        /*
         * clear embedded scope identifiers if necessary.
         * in6_clearscope will touch the addresses only when necessary.
@@ -671,39 +707,109 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
         * If this is to be processed locally, let ip6_input have it.
         */
        if (proxy) {
         * If this is to be processed locally, let ip6_input have it.
         */
        if (proxy) {
-               VERIFY(m->m_pkthdr.aux_flags & MAUXF_PROXY_DST);
+               VERIFY(m->m_pkthdr.pkt_flags & PKTF_PROXY_DST);
                /* Release extra ref */
                RT_REMREF(rt);
                /* Release extra ref */
                RT_REMREF(rt);
-               if (mcopy != NULL)
+               if (mcopy != NULL) {
                        m_freem(mcopy);
                        m_freem(mcopy);
-               return (m);
+               }
+               return m;
        }
 
        }
 
+       /* Mark this packet as being forwarded from another interface */
+       m->m_pkthdr.pkt_flags |= PKTF_FORWARDED;
+
 #if PF
 #if PF
-       /* Invoke outbound packet filter */
-       error = pf_af_hook(ifp, NULL, &m, AF_INET6, FALSE, NULL);
+       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));
+
+               args.fwa_m = m;
+               args.fwa_oif = ifp;
+               args.fwa_oflags = 0;
+               args.fwa_ro6 = ip6forward_rt;
+               args.fwa_ro6_pmtu = ip6forward_rt;
+               args.fwa_mtu = rt->rt_ifp->if_mtu;
+               args.fwa_dst6 = dst;
+               args.fwa_origifp = origifp;
+               /* Invoke outbound packet filter */
+               error = pf_af_hook(ifp, NULL, &m, AF_INET6, FALSE, &args);
+#else /* !DUMMYNET */
+               error = pf_af_hook(ifp, NULL, &m, AF_INET6, FALSE, NULL);
+#endif /* !DUMMYNET */
+               if (error != 0 || m == NULL) {
+                       if (m != NULL) {
+                               panic("%s: unexpected packet %p\n", __func__, m);
+                               /* NOTREACHED */
+                       }
+                       /* 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;
+                       }
 
 
-       if (error != 0 || m == NULL) {
-               if (m != NULL) {
-                       panic("%s: unexpected packet %p\n", __func__, m);
-                       /* NOTREACHED */
+                       /*
+                        * nd6_output() frees packetchain in both success and
+                        * failure cases.
+                        */
+                       error = nd6_output(ifp, origifp, m, dst, rt, NULL);
+                       m = NULL;
+                       goto sent;
                }
                }
-               /* Already freed by callee */
-               goto senderr;
+               /*
+                * We do not use ip6 header again in the code below,
+                * however still adding the bit here so that any new
+                * code in future doesn't end up working with the
+                * wrong pointer
+                */
+               ip6 = mtod(m, struct ip6_hdr *);
        }
        }
-       ip6 = mtod(m, struct ip6_hdr *);
 #endif /* PF */
 
 #endif /* PF */
 
+       len = m_pktlen(m);
+       pktcnt = 1;
        error = nd6_output(ifp, origifp, m, dst, rt, NULL);
        error = nd6_output(ifp, origifp, m, dst, rt, NULL);
+sent:
        if (error) {
        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 {
        } else {
-               ip6stat.ip6s_forward++;
-               in6_ifstat_inc(ifp, ifs6_out_forward);
-               if (type)
+               /*
+                * 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 += pktcnt;
+               rcvifp->if_fbytes += len;
+
+               ip6stat.ip6s_forward += pktcnt;
+               in6_ifstat_add(ifp, ifs6_out_forward, pktcnt);
+               if (type) {
                        ip6stat.ip6s_redirectsent++;
                        ip6stat.ip6s_redirectsent++;
-               else {
+               else {
                        if (mcopy) {
                                goto freecopy;
                        }
                        if (mcopy) {
                                goto freecopy;
                        }
@@ -715,7 +821,7 @@ senderr:
        if (mcopy == NULL) {
                /* Release extra ref */
                RT_REMREF(rt);
        if (mcopy == NULL) {
                /* Release extra ref */
                RT_REMREF(rt);
-               return (NULL);
+               return NULL;
        }
        switch (error) {
        case 0:
        }
        switch (error) {
        case 0:
@@ -724,7 +830,7 @@ senderr:
                        icmp6_redirect_output(mcopy, rt);
                        /* Release extra ref */
                        RT_REMREF(rt);
                        icmp6_redirect_output(mcopy, rt);
                        /* Release extra ref */
                        RT_REMREF(rt);
-                       return (NULL);
+                       return NULL;
                }
 #endif
                goto freecopy;
                }
 #endif
                goto freecopy;
@@ -737,7 +843,7 @@ senderr:
                /* Tell source to slow down like source quench in IP? */
                goto freecopy;
 
                /* Tell source to slow down like source quench in IP? */
                goto freecopy;
 
-       case ENETUNREACH:       /* shouldn't happen, checked above */
+       case ENETUNREACH:       /* shouldn't happen, checked above */
        case EHOSTUNREACH:
        case ENETDOWN:
        case EHOSTDOWN:
        case EHOSTUNREACH:
        case ENETDOWN:
        case EHOSTDOWN:
@@ -749,11 +855,11 @@ senderr:
        icmp6_error(mcopy, type, code, 0);
        /* Release extra ref */
        RT_REMREF(rt);
        icmp6_error(mcopy, type, code, 0);
        /* Release extra ref */
        RT_REMREF(rt);
-       return (NULL);
+       return NULL;
 
 
- freecopy:
+freecopy:
        m_freem(mcopy);
        /* Release extra ref */
        RT_REMREF(rt);
        m_freem(mcopy);
        /* Release extra ref */
        RT_REMREF(rt);
-       return (NULL);
+       return NULL;
 }
 }