]> 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 ee5a7045369e7f99e4ad65b3dcc4b5ea926ac305..02cac6958803845f9b81826854edada780b472ec 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2009-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2020 Apple Inc. All rights reserved.
  *
  * @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
  * 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.
- * 
+ *
  * 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.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
 
 #if IPSEC
 #include <netinet6/ipsec.h>
-#if INET6
 #include <netinet6/ipsec6.h>
-#endif
 #include <netkey/key.h>
 extern int ipsec_bypass;
 #endif /* IPSEC */
 
-#include <netinet6/ip6_fw.h>
-
 #include <net/net_osdep.h>
 
+#if DUMMYNET
+#include <netinet/ip_dummynet.h>
+#endif /* DUMMYNET */
+
 #if PF
 #include <net/pfvar.h>
 #endif /* PF */
@@ -128,8 +128,8 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        int error, type = 0, code = 0;
        boolean_t proxy = FALSE;
        struct mbuf *mcopy = NULL;
-       struct ifnet *ifp, *rcvifp, *origifp;   /* maybe unnecessary */
-       u_int32_t inzone, outzone, len;
+       struct ifnet *ifp, *rcvifp, *origifp;   /* maybe unnecessary */
+       u_int32_t inzone, outzone, len = 0, pktcnt = 0;
        struct in6_addr src_in6, dst_in6;
        uint64_t curtime = net_uptime();
 #if IPSEC
@@ -138,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
@@ -154,15 +157,30 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        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)
+               if ((rt = ip6forward_rt->ro_rt) != NULL) {
                        ifscope = rt->rt_ifp->if_index;
+               }
        }
 
 #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;
-
+       }
+       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
@@ -192,7 +210,7 @@ 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);
-                       return (NULL);
+                       return NULL;
                }
        }
 #endif /*IPSEC*/
@@ -202,7 +220,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
         * 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++;
@@ -218,14 +236,14 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                            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) */
                icmp6_error_flag(m, ICMP6_TIME_EXCEEDED,
-                               ICMP6_TIME_EXCEED_TRANSIT, 0, 0);
-               return (NULL);
+                   ICMP6_TIME_EXCEED_TRANSIT, 0, 0);
+               return NULL;
        }
 
        /*
@@ -242,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.
                 */
-               if (proxy)
+               if (proxy) {
                        goto skip_ipsec;
+               }
        }
 
        ip6->ip6_hlim -= IPV6_HLIMDEC;
@@ -260,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
-       if (ipsec_bypass != 0)
+       if (ipsec_bypass != 0) {
                goto skip_ipsec;
+       }
        /* get a security policy for this packet */
        sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
            &error);
@@ -276,7 +296,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 #endif
                }
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
 
        error = 0;
@@ -284,7 +304,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
        /* check policy */
        switch (sp->policy) {
        case IPSEC_POLICY_DISCARD:
-        case IPSEC_POLICY_GENERATE:
+       case IPSEC_POLICY_GENERATE:
                /*
                 * This packet is just discarded.
                 */
@@ -299,7 +319,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 #endif
                }
                m_freem(m);
-               return (NULL);
+               return NULL;
 
        case IPSEC_POLICY_BYPASS:
        case IPSEC_POLICY_NONE:
@@ -321,7 +341,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 #endif
                        }
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                /* do IPsec */
                break;
@@ -334,61 +354,61 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                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() */
+               /*
+                * 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 */
-       }
+               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 */
+               }
 
-       m = state.m;
-       ROUTE_RELEASE(&state.ro);
+               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);
-                       /* fall through */
-               case ENOENT:
-                       /* don't show these error codes to the user */
-                       break;
-               }
-               ip6stat.ip6s_cantforward++;
-               if (mcopy) {
+               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
-                       /* XXX: what icmp ? */
+                               /* XXX: what icmp ? */
 #else
-                       m_freem(mcopy);
+                               m_freem(mcopy);
 #endif
+                       }
+                       m_freem(m);
+                       return NULL;
                }
-               m_freem(m);
-               return (NULL);
        }
-    }
 #endif /* IPSEC */
-    skip_ipsec:
+skip_ipsec:
 
        dst = (struct sockaddr_in6 *)&ip6forward_rt->ro_dst;
        if ((rt = ip6forward_rt->ro_rt) != NULL) {
@@ -423,11 +443,12 @@ 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 (mcopy)
+                       if (mcopy) {
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
-                                           ICMP6_DST_UNREACH_NOROUTE, 0);
+                                   ICMP6_DST_UNREACH_NOROUTE, 0);
+                       }
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                RT_LOCK_ASSERT_HELD(rt);
        } else if (ROUTE_UNUSABLE(ip6forward_rt) ||
@@ -449,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 (mcopy)
+                       if (mcopy) {
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
                                    ICMP6_DST_UNREACH_NOROUTE, 0);
+                       }
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                RT_LOCK(rt);
                /* Take an extra ref for ourselves */
@@ -478,7 +500,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
        if (in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone)) {
                RT_REMREF_LOCKED(rt);
@@ -486,7 +508,7 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                ip6stat.ip6s_cantforward++;
                ip6stat.ip6s_badscope++;
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
 
        if (inzone != outzone && !proxy) {
@@ -509,10 +531,10 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                RT_UNLOCK(rt);
                if (mcopy) {
                        icmp6_error(mcopy, ICMP6_DST_UNREACH,
-                                   ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
+                           ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
                }
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
 
        /*
@@ -531,10 +553,14 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                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;
@@ -554,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,
-                               IP_FORWARDING, &ipsecerror);
+                           IP_FORWARDING, &ipsecerror);
                        if (sp2) {
                                ipsechdrsiz = ipsec6_hdrsiz(mcopy,
-                                       IPSEC_DIR_OUTBOUND, NULL);
-                               if (ipsechdrsiz < mtu)
+                                   IPSEC_DIR_OUTBOUND, NULL);
+                               if (ipsechdrsiz < mtu) {
                                        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).
                         */
-                       if (mtu < IPV6_MMTU)
+                       if (mtu < IPV6_MMTU) {
                                mtu = IPV6_MMTU;
+                       }
 #endif
                        /* Release extra ref */
                        RT_REMREF_LOCKED(rt);
@@ -579,11 +607,12 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                        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;
+       }
 
        /*
         * If we are to forward the packet using the same interface
@@ -596,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 &&
-           (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
@@ -608,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.
                         */
-                       RT_REMREF_LOCKED(rt);   /* Release extra ref */
+                       RT_REMREF_LOCKED(rt);   /* Release extra ref */
                        RT_UNLOCK(rt);
                        icmp6_error(mcopy, ICMP6_DST_UNREACH,
-                                   ICMP6_DST_UNREACH_ADDR, 0);
+                           ICMP6_DST_UNREACH_ADDR, 0);
                        m_freem(m);
-                       return (NULL);
+                       return NULL;
                }
                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
@@ -661,15 +667,15 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
 #if 1
                if ((0))
 #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. "
-                               "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. */
@@ -704,48 +710,106 @@ ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt,
                VERIFY(m->m_pkthdr.pkt_flags & PKTF_PROXY_DST);
                /* Release extra ref */
                RT_REMREF(rt);
-               if (mcopy != NULL)
+               if (mcopy != NULL) {
                        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
-       /* 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;
+               }
 
-       if (error != 0 || m == NULL) {
-               if (m != NULL) {
-                       panic("%s: unexpected packet %p\n", __func__, m);
-                       /* NOTREACHED */
+               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;
                }
-               /* 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 */
 
-       /* Mark this packet as being forwarded from another interface */
-       m->m_pkthdr.pkt_flags |= PKTF_FORWARDED;
        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);
-               if (type)
+               ip6stat.ip6s_forward += pktcnt;
+               in6_ifstat_add(ifp, ifs6_out_forward, pktcnt);
+               if (type) {
                        ip6stat.ip6s_redirectsent++;
-               else {
+               else {
                        if (mcopy) {
                                goto freecopy;
                        }
@@ -757,7 +821,7 @@ senderr:
        if (mcopy == NULL) {
                /* Release extra ref */
                RT_REMREF(rt);
-               return (NULL);
+               return NULL;
        }
        switch (error) {
        case 0:
@@ -766,7 +830,7 @@ senderr:
                        icmp6_redirect_output(mcopy, rt);
                        /* Release extra ref */
                        RT_REMREF(rt);
-                       return (NULL);
+                       return NULL;
                }
 #endif
                goto freecopy;
@@ -779,7 +843,7 @@ senderr:
                /* 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:
@@ -791,11 +855,11 @@ senderr:
        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);
-       return (NULL);
+       return NULL;
 }