]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/ip6_forward.c
xnu-792.24.17.tar.gz
[apple/xnu.git] / bsd / netinet6 / ip6_forward.c
index 95838ed3c46e6adf46b3ebc20e8fddf77032662e..4cb3871ad9f7fb7e5c558dff2b6e822a44237f15 100644 (file)
@@ -1,4 +1,5 @@
-/*     $KAME: ip6_forward.c,v 1.29 2000/02/26 18:08:38 itojun Exp $    */
+/*     $FreeBSD: src/sys/netinet6/ip6_forward.c,v 1.16 2002/10/16 02:25:05 sam Exp $   */
+/*     $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 itojun Exp $    */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * SUCH DAMAGE.
  */
 
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
-#include "opt_ip6fw.h"
-#include "opt_inet.h"
-#endif
 
 #include <sys/param.h>
 #include <sys/systm.h>
 
 #include <netinet/in.h>
 #include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
 #include <netinet/ip_var.h>
+#include <netinet6/in6_var.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet/icmp6.h>
 #include <netinet6/nd6.h>
 
-#if IPSEC_IPV6FWD
+#include <netinet/in_pcb.h>
+
+#if IPSEC
 #include <netinet6/ipsec.h>
+#if INET6
+#include <netinet6/ipsec6.h>
+#endif
 #include <netkey/key.h>
-#include <netkey/key_debug.h>
-#endif /* IPSEC_IPV6FWD */
+extern int ipsec_bypass;
+extern lck_mtx_t *sadb_mutex;
+extern lck_mtx_t *ip6_mutex;
+#endif /* IPSEC */
 
-#if IPV6FIREWALL
 #include <netinet6/ip6_fw.h>
-#endif
-
-#if MIP6
-#include <netinet6/mip6.h>
-#endif
 
 #include <net/net_osdep.h>
 
@@ -89,23 +90,26 @@ struct      route_in6 ip6_forward_rt;
  */
 
 void
-ip6_forward(m, srcrt)
+ip6_forward(m, srcrt, locked)
        struct mbuf *m;
        int srcrt;
+       int locked;
 {
        struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
-       register struct sockaddr_in6 *dst;
-       register struct rtentry *rt;
+       struct sockaddr_in6 *dst;
+       struct rtentry *rt;
        int error, type = 0, code = 0;
        struct mbuf *mcopy = NULL;
-#if IPSEC_IPV6FWD
+       struct ifnet *origifp;  /* maybe unnecessary */
+#if IPSEC
        struct secpolicy *sp = NULL;
 #endif
-#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__)
-       long time_second = time.tv_sec;
-#endif
+       struct timeval timenow;
 
-#if IPSEC_IPV6FWD
+       getmicrotime(&timenow);
+
+
+#if IPSEC
        /*
         * Check AH/ESP integrity.
         */
@@ -113,19 +117,31 @@ ip6_forward(m, srcrt)
         * Don't increment ip6s_cantforward because this is the check
         * before forwarding packet actually.
         */
-       if (ipsec6_in_reject(m, NULL)) {
-               ipsec6stat.in_polvio++;
-               m_freem(m);
-               return;
+       if (ipsec_bypass == 0) {
+               lck_mtx_lock(sadb_mutex);
+               if (ipsec6_in_reject(m, NULL)) {
+                       ipsec6stat.in_polvio++;
+                       lck_mtx_unlock(sadb_mutex);
+                       m_freem(m);
+                       return;
+               }
+               lck_mtx_unlock(sadb_mutex);
        }
-#endif /*IPSEC_IPV6FWD*/
+#endif /*IPSEC*/
 
+       /*
+        * Do not forward packets to multicast destination (should be handled
+        * by ip6_mforward().
+        * 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 ||
-           IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+           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 < time_second) {
-                       ip6_log_time = time_second;
+               if (ip6_log_time + ip6_log_interval < timenow.tv_sec) {
+                       ip6_log_time = timenow.tv_sec;
                        log(LOG_DEBUG,
                            "cannot forward "
                            "from %s to %s nxt %d received on %s\n",
@@ -140,8 +156,12 @@ ip6_forward(m, srcrt)
 
        if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
                /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
+               if (locked)
+                       lck_mtx_unlock(ip6_mutex);
                icmp6_error(m, ICMP6_TIME_EXCEEDED,
                                ICMP6_TIME_EXCEED_TRANSIT, 0);
+               if (locked)
+                       lck_mtx_lock(ip6_mutex);
                return;
        }
        ip6->ip6_hlim -= IPV6_HLIMDEC;
@@ -157,10 +177,13 @@ ip6_forward(m, srcrt)
         */
        mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
 
-
-#if IPSEC_IPV6FWD
+#if IPSEC
+       if (ipsec_bypass != 0)
+               goto skip_ipsec;
+       lck_mtx_lock(sadb_mutex);
        /* get a security policy for this packet */
-       sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
+       sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
+           &error);
        if (sp == NULL) {
                ipsec6stat.out_inval++;
                ip6stat.ip6s_cantforward++;
@@ -171,6 +194,7 @@ ip6_forward(m, srcrt)
                        m_freem(mcopy);
 #endif
                }
+               lck_mtx_unlock(sadb_mutex);
                m_freem(m);
                return;
        }
@@ -193,6 +217,7 @@ ip6_forward(m, srcrt)
                        m_freem(mcopy);
 #endif
                }
+               lck_mtx_unlock(sadb_mutex);
                m_freem(m);
                return;
 
@@ -200,8 +225,9 @@ ip6_forward(m, srcrt)
        case IPSEC_POLICY_NONE:
                /* no need to do IPsec. */
                key_freesp(sp);
+               lck_mtx_unlock(sadb_mutex);
                goto skip_ipsec;
-       
+
        case IPSEC_POLICY_IPSEC:
                if (sp->req == NULL) {
                        /* XXX should be panic ? */
@@ -215,6 +241,7 @@ ip6_forward(m, srcrt)
                                m_freem(mcopy);
 #endif
                        }
+                       lck_mtx_unlock(sadb_mutex);
                        m_freem(m);
                        return;
                }
@@ -226,6 +253,7 @@ ip6_forward(m, srcrt)
                /* should be panic ?? */
                printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
                key_freesp(sp);
+               lck_mtx_unlock(sadb_mutex);
                goto skip_ipsec;
        }
 
@@ -245,13 +273,16 @@ ip6_forward(m, srcrt)
        state.ro = NULL;        /* update at ipsec6_output_tunnel() */
        state.dst = NULL;       /* update at ipsec6_output_tunnel() */
 
+       if (locked)
+                       lck_mtx_unlock(ip6_mutex);
        error = ipsec6_output_tunnel(&state, sp, 0);
+       if (locked) {
+                       lck_mtx_unlock(sadb_mutex);
+                       lck_mtx_lock(ip6_mutex);
+                       lck_mtx_lock(sadb_mutex);
+       }
 
        m = state.m;
-#if 0  /* XXX allocate a route (ro, dst) again later */
-       ro = (struct route_in6 *)state.ro;
-       dst = (struct sockaddr_in6 *)state.dst;
-#endif
        key_freesp(sp);
 
        if (error) {
@@ -265,7 +296,7 @@ ip6_forward(m, srcrt)
                        break;
                default:
                        printf("ip6_output (ipsec): error code %d\n", error);
-                       /*fall through*/
+                       /* fall through */
                case ENOENT:
                        /* don't show these error codes to the user */
                        break;
@@ -278,32 +309,16 @@ ip6_forward(m, srcrt)
                        m_freem(mcopy);
 #endif
                }
+               lck_mtx_unlock(sadb_mutex);
                m_freem(m);
                return;
        }
     }
+       lck_mtx_unlock(sadb_mutex);
     skip_ipsec:
-#endif /* IPSEC_IPV6FWD */
-       
-#if MIP6
-       {
-               struct   mip6_bc   *bc;
-
-               bc = mip6_bc_find(&ip6->ip6_dst);
-               if ((bc != NULL) && (bc->hr_flag)) {
-                       if (mip6_tunnel_output(&m, bc) != 0) {
-                               ip6stat.ip6s_cantforward++;
-                               if (mcopy)
-                                       m_freem(mcopy);
-                               m_freem(m);
-                               return;
-                       }
-               }
-               ip6 = mtod(m, struct ip6_hdr *);        /* m has changed */
-       }
-#endif
-       
-       dst = &ip6_forward_rt.ro_dst;
+#endif /* IPSEC */
+
+       dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst;
        if (!srcrt) {
                /*
                 * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
@@ -311,24 +326,24 @@ ip6_forward(m, srcrt)
                if (ip6_forward_rt.ro_rt == 0 ||
                    (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) {
                        if (ip6_forward_rt.ro_rt) {
-                               RTFREE(ip6_forward_rt.ro_rt);
+                               rtfree(ip6_forward_rt.ro_rt);
                                ip6_forward_rt.ro_rt = 0;
                        }
                        /* this probably fails but give it a try again */
-#if __FreeBSD__ || defined(__APPLE__)
                        rtalloc_ign((struct route *)&ip6_forward_rt,
                                    RTF_PRCLONING);
-#else
-                       rtalloc((struct route *)&ip6_forward_rt);
-#endif
                }
-               
+
                if (ip6_forward_rt.ro_rt == 0) {
                        ip6stat.ip6s_noroute++;
-                       /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
+                       in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
                        if (mcopy) {
+                               if (locked)
+                                       lck_mtx_unlock(ip6_mutex);
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
                                            ICMP6_DST_UNREACH_NOROUTE, 0);
+                               if (locked)
+                                       lck_mtx_lock(ip6_mutex);
                        }
                        m_freem(m);
                        return;
@@ -336,7 +351,7 @@ ip6_forward(m, srcrt)
        } else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
                 !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
                if (ip6_forward_rt.ro_rt) {
-                       RTFREE(ip6_forward_rt.ro_rt);
+                       rtfree(ip6_forward_rt.ro_rt);
                        ip6_forward_rt.ro_rt = 0;
                }
                bzero(dst, sizeof(*dst));
@@ -344,17 +359,17 @@ ip6_forward(m, srcrt)
                dst->sin6_family = AF_INET6;
                dst->sin6_addr = ip6->ip6_dst;
 
-#if __FreeBSD__ || defined(__APPLE__)
                rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
-#else
-               rtalloc((struct route *)&ip6_forward_rt);
-#endif
                if (ip6_forward_rt.ro_rt == 0) {
                        ip6stat.ip6s_noroute++;
-                       /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */
+                       in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
                        if (mcopy) {
+                               if (locked)
+                                       lck_mtx_unlock(ip6_mutex);
                                icmp6_error(mcopy, ICMP6_DST_UNREACH,
                                            ICMP6_DST_UNREACH_NOROUTE, 0);
+                               if (locked)
+                                       lck_mtx_lock(ip6_mutex);
                        }
                        m_freem(m);
                        return;
@@ -367,7 +382,7 @@ ip6_forward(m, srcrt)
         * for the reason that the destination is beyond the scope of the
         * source address, discard the packet and return an icmp6 destination
         * unreachable error with Code 2 (beyond scope of source address).
-        * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1]
+        * [draft-ietf-ipngwg-icmp-v3-02.txt, Section 3.1]
         */
        if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) !=
            in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) {
@@ -375,8 +390,8 @@ ip6_forward(m, srcrt)
                ip6stat.ip6s_badscope++;
                in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
 
-               if (ip6_log_time + ip6_log_interval < time_second) {
-                       ip6_log_time = time_second;
+               if (ip6_log_time + ip6_log_interval < timenow.tv_sec) {
+                       ip6_log_time = timenow.tv_sec;
                        log(LOG_DEBUG,
                            "cannot forward "
                            "src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
@@ -385,9 +400,14 @@ ip6_forward(m, srcrt)
                            ip6->ip6_nxt,
                            if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp));
                }
-               if (mcopy)
+               if (mcopy) {
+                       if (locked)
+                               lck_mtx_unlock(ip6_mutex);
                        icmp6_error(mcopy, ICMP6_DST_UNREACH,
                                    ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
+                       if (locked)
+                               lck_mtx_lock(ip6_mutex);
+               }
                m_freem(m);
                return;
        }
@@ -396,14 +416,14 @@ ip6_forward(m, srcrt)
                in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
                if (mcopy) {
                        u_long mtu;
-#if IPSEC_IPV6FWD
+#if IPSEC
                        struct secpolicy *sp;
                        int ipsecerror;
                        size_t ipsechdrsiz;
 #endif
 
                        mtu = rt->rt_ifp->if_mtu;
-#if IPSEC_IPV6FWD
+#if IPSEC
                        /*
                         * When we do IPsec tunnel ingress, we need to play
                         * with if_mtu value (decrement IPsec header size
@@ -411,6 +431,7 @@ ip6_forward(m, srcrt)
                         * case, as we have the outgoing interface for
                         * encapsulated packet as "rt->rt_ifp".
                         */
+                       lck_mtx_lock(sadb_mutex);
                        sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND,
                                IP_FORWARDING, &ipsecerror);
                        if (sp) {
@@ -419,7 +440,7 @@ ip6_forward(m, srcrt)
                                if (ipsechdrsiz < mtu)
                                        mtu -= ipsechdrsiz;
                        }
-
+                       lck_mtx_unlock(sadb_mutex);
                        /*
                         * if mtu becomes less than minimum MTU,
                         * tell minimum MTU (and I'll need to fragment it).
@@ -427,7 +448,11 @@ ip6_forward(m, srcrt)
                        if (mtu < IPV6_MMTU)
                                mtu = IPV6_MMTU;
 #endif
+                       if (locked)
+                               lck_mtx_unlock(ip6_mutex);
                        icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
+                       if (locked)
+                               lck_mtx_lock(ip6_mutex);
                }
                m_freem(m);
                return;
@@ -446,32 +471,91 @@ ip6_forward(m, srcrt)
         * modified by a redirect.
         */
        if (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
+                        * one, and the link attached to the interface is
+                        * point-to-point, then it will be highly probable
+                        * that a routing loop occurs. Thus, we immediately
+                        * drop the packet and send an ICMPv6 error message.
+                        *
+                        * type/code is based on suggestion by Rich Draves.
+                        * not sure if it is the best pick.
+                        */
+                       if (locked)
+                               lck_mtx_unlock(ip6_mutex);
+                       icmp6_error(mcopy, ICMP6_DST_UNREACH,
+                                   ICMP6_DST_UNREACH_ADDR, 0);
+                       if (locked)
+                               lck_mtx_lock(ip6_mutex);
+                       m_freem(m);
+                       return;
+               }
                type = ND_REDIRECT;
+       }
 
-#if IPV6FIREWALL
        /*
         * Check with the firewall...
         */
-       if (ip6_fw_chk_ptr) {
+       if (ip6_fw_enable && ip6_fw_chk_ptr) {
                u_short port = 0;
                /* If ipfw says divert, we have to just drop packet */
-               if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) {
+               if (ip6_fw_chk_ptr(&ip6, rt->rt_ifp, &port, &m)) {
                        m_freem(m);
                        goto freecopy;
                }
                if (!m)
                        goto freecopy;
        }
-#endif
 
-#if OLDIP6OUTPUT
-       error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m,
-                                        (struct sockaddr *)dst,
-                                        ip6_forward_rt.ro_rt);
+       /*
+        * Fake scoped addresses. Note that even link-local source or
+        * destinaion can appear, if the originating node just sends the
+        * packet to us (without address resolution for the destination).
+        * Since both icmp6_error and icmp6_redirect_output fill the embedded
+        * link identifiers, we can do this stuff after making a copy for
+        * returning an error.
+        */
+       if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
+               /*
+                * See corresponding comments in ip6_output.
+                * XXX: but is it possible that ip6_forward() sends a packet
+                *      to a loopback interface? I don't think so, and thus
+                *      I bark here. (jinmei@kame.net)
+                * XXX: it is common to route invalid packets to loopback.
+                *      also, the codepath will be visited on use of ::1 in
+                *      rthdr. (itojun)
+                */
+#if 1
+               if (0)
 #else
-       error = nd6_output(rt->rt_ifp, m, dst, rt);
-#endif 
+               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));
+               }
+
+               /* we can just use rcvif in forwarding. */
+               origifp = m->m_pkthdr.rcvif;
+       }
+       else
+               origifp = rt->rt_ifp;
+#ifndef SCOPEDROUTING
+       /*
+        * clear embedded scope identifiers if necessary.
+        * in6_clearscope will touch the addresses only when necessary.
+        */
+       in6_clearscope(&ip6->ip6_src);
+       in6_clearscope(&ip6->ip6_dst);
+#endif
+
+       error = nd6_output(rt->rt_ifp, origifp, m, dst, rt, locked);
        if (error) {
                in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
                ip6stat.ip6s_cantforward++;
@@ -515,7 +599,11 @@ ip6_forward(m, srcrt)
                code = ICMP6_DST_UNREACH_ADDR;
                break;
        }
+       if (locked)
+               lck_mtx_unlock(ip6_mutex);
        icmp6_error(mcopy, type, code, 0);
+       if (locked)
+               lck_mtx_lock(ip6_mutex);
        return;
 
  freecopy: