X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/13fec9890cf095cc781fdf7b8917cb03bf32dd4c..060df5ea7c632b1ac8cc8aac1fb59758165c2084:/bsd/netinet6/ip6_forward.c diff --git a/bsd/netinet6/ip6_forward.c b/bsd/netinet6/ip6_forward.c index 4cb3871ad..202d8ccb1 100644 --- a/bsd/netinet6/ip6_forward.c +++ b/bsd/netinet6/ip6_forward.c @@ -1,3 +1,31 @@ +/* + * Copyright (c) 2008 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 + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * 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, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * 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@ + */ + /* $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 $ */ @@ -66,15 +94,16 @@ #endif #include extern int ipsec_bypass; -extern lck_mtx_t *sadb_mutex; -extern lck_mtx_t *ip6_mutex; #endif /* IPSEC */ +extern lck_mtx_t *ip6_mutex; #include #include -struct route_in6 ip6_forward_rt; +#if PF +#include +#endif /* PF */ /* * Forward a packet. If some error occurs return the sender @@ -90,21 +119,20 @@ struct route_in6 ip6_forward_rt; */ void -ip6_forward(m, srcrt, locked) - struct mbuf *m; - int srcrt; - int locked; +ip6_forward(struct mbuf *m, struct route_in6 *ip6forward_rt, + int srcrt, int locked) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst; struct rtentry *rt; int error, type = 0, code = 0; struct mbuf *mcopy = NULL; - struct ifnet *origifp; /* maybe unnecessary */ + struct ifnet *ifp, *origifp; /* maybe unnecessary */ #if IPSEC struct secpolicy *sp = NULL; #endif struct timeval timenow; + int tunneledv4 = 0; getmicrotime(&timenow); @@ -118,14 +146,11 @@ ip6_forward(m, srcrt, locked) * before forwarding packet actually. */ if (ipsec_bypass == 0) { - lck_mtx_lock(sadb_mutex); if (ipsec6_in_reject(m, NULL)) { - ipsec6stat.in_polvio++; - lck_mtx_unlock(sadb_mutex); + IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio); m_freem(m); return; } - lck_mtx_unlock(sadb_mutex); } #endif /*IPSEC*/ @@ -180,12 +205,11 @@ ip6_forward(m, srcrt, locked) #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, IP_FORWARDING, &error); if (sp == NULL) { - ipsec6stat.out_inval++; + IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); ip6stat.ip6s_cantforward++; if (mcopy) { #if 0 @@ -194,7 +218,6 @@ ip6_forward(m, srcrt, locked) m_freem(mcopy); #endif } - lck_mtx_unlock(sadb_mutex); m_freem(m); return; } @@ -204,12 +227,13 @@ ip6_forward(m, srcrt, locked) /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: + case IPSEC_POLICY_GENERATE: /* * This packet is just discarded. */ - ipsec6stat.out_polvio++; + IPSEC_STAT_INCREMENT(ipsec6stat.out_polvio); ip6stat.ip6s_cantforward++; - key_freesp(sp); + key_freesp(sp, KEY_SADB_UNLOCKED); if (mcopy) { #if 0 /* XXX: what icmp ? */ @@ -217,15 +241,13 @@ ip6_forward(m, srcrt, locked) m_freem(mcopy); #endif } - lck_mtx_unlock(sadb_mutex); m_freem(m); return; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ - key_freesp(sp); - lck_mtx_unlock(sadb_mutex); + key_freesp(sp, KEY_SADB_UNLOCKED); goto skip_ipsec; case IPSEC_POLICY_IPSEC: @@ -233,7 +255,7 @@ ip6_forward(m, srcrt, locked) /* XXX should be panic ? */ printf("ip6_forward: No IPsec request specified.\n"); ip6stat.ip6s_cantforward++; - key_freesp(sp); + key_freesp(sp, KEY_SADB_UNLOCKED); if (mcopy) { #if 0 /* XXX: what icmp ? */ @@ -241,7 +263,6 @@ ip6_forward(m, srcrt, locked) m_freem(mcopy); #endif } - lck_mtx_unlock(sadb_mutex); m_freem(m); return; } @@ -252,8 +273,7 @@ ip6_forward(m, srcrt, locked) default: /* should be panic ?? */ printf("ip6_forward: Invalid policy found. %d\n", sp->policy); - key_freesp(sp); - lck_mtx_unlock(sadb_mutex); + key_freesp(sp, KEY_SADB_UNLOCKED); goto skip_ipsec; } @@ -274,17 +294,15 @@ ip6_forward(m, srcrt, locked) 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); - } - + lck_mtx_unlock(ip6_mutex); + error = ipsec6_output_tunnel(&state, sp, 0, &tunneledv4); + if (locked) + lck_mtx_lock(ip6_mutex); + key_freesp(sp, KEY_SADB_UNLOCKED); + if (tunneledv4) + return; /* packet is gone - sent over IPv4 */ + m = state.m; - key_freesp(sp); - if (error) { /* mbuf is already reclaimed in ipsec6_output_tunnel. */ switch (error) { @@ -309,32 +327,53 @@ ip6_forward(m, srcrt, locked) m_freem(mcopy); #endif } - lck_mtx_unlock(sadb_mutex); m_freem(m); return; } } - lck_mtx_unlock(sadb_mutex); skip_ipsec: #endif /* IPSEC */ - dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; + /* + * If "locked", ip6forward_rt points to the globally defined + * struct route cache which requires ip6_mutex, e.g. when this + * is called from ip6_input(). Else the caller is responsible + * for the struct route and its serialization (if needed), e.g. + * when this is called from ip6_rthdr0(). + */ + if (locked) + lck_mtx_assert(ip6_mutex, LCK_MTX_ASSERT_OWNED); + dst = (struct sockaddr_in6 *)&ip6forward_rt->ro_dst; + if ((rt = ip6forward_rt->ro_rt) != NULL) { + RT_LOCK(rt); + /* Take an extra ref for ourselves */ + RT_ADDREF_LOCKED(rt); + } + if (!srcrt) { /* - * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst + * ip6forward_rt->ro_dst.sin6_addr is equal to ip6->ip6_dst */ - 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); - ip6_forward_rt.ro_rt = 0; + if (rt == NULL || !(rt->rt_flags & RTF_UP) || + rt->generation_id != route_generation) { + if (rt != NULL) { + /* Release extra ref */ + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); + rtfree(rt); + ip6forward_rt->ro_rt = NULL; } /* this probably fails but give it a try again */ - rtalloc_ign((struct route *)&ip6_forward_rt, - RTF_PRCLONING); + rtalloc_ign((struct route *)ip6forward_rt, + RTF_PRCLONING); + if ((rt = ip6forward_rt->ro_rt) != NULL) { + RT_LOCK(rt); + /* Take an extra ref for ourselves */ + RT_ADDREF_LOCKED(rt); + } } - if (ip6_forward_rt.ro_rt == 0) { + if (rt == NULL) { ip6stat.ip6s_noroute++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute); if (mcopy) { @@ -348,34 +387,41 @@ ip6_forward(m, srcrt, locked) m_freem(m); return; } - } 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); - ip6_forward_rt.ro_rt = 0; + 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) { + if (rt != NULL) { + /* Release extra ref */ + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); + rtfree(rt); + ip6forward_rt->ro_rt = NULL; } bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; - rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); - if (ip6_forward_rt.ro_rt == 0) { + rtalloc_ign((struct route *)ip6forward_rt, RTF_PRCLONING); + if ((rt = ip6forward_rt->ro_rt) == NULL) { ip6stat.ip6s_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); + ICMP6_DST_UNREACH_NOROUTE, 0); if (locked) lck_mtx_lock(ip6_mutex); } m_freem(m); return; } + RT_LOCK(rt); + /* Take an extra ref for ourselves */ + RT_ADDREF_LOCKED(rt); } - rt = ip6_forward_rt.ro_rt; /* * Scope check: if a packet can't be delivered to its destination @@ -400,6 +446,9 @@ ip6_forward(m, srcrt, locked) ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } + /* Release extra ref */ + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); if (mcopy) { if (locked) lck_mtx_unlock(ip6_mutex); @@ -415,9 +464,9 @@ ip6_forward(m, srcrt, locked) if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { - u_long mtu; + uint32_t mtu; #if IPSEC - struct secpolicy *sp; + struct secpolicy *sp2; int ipsecerror; size_t ipsechdrsiz; #endif @@ -431,16 +480,15 @@ ip6_forward(m, srcrt, locked) * 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, + sp2 = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &ipsecerror); - if (sp) { + if (sp2) { ipsechdrsiz = ipsec6_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND, NULL); if (ipsechdrsiz < mtu) mtu -= ipsechdrsiz; + key_freesp(sp2, KEY_SADB_UNLOCKED); } - lck_mtx_unlock(sadb_mutex); /* * if mtu becomes less than minimum MTU, * tell minimum MTU (and I'll need to fragment it). @@ -448,11 +496,18 @@ ip6_forward(m, srcrt, locked) if (mtu < IPV6_MMTU) mtu = IPV6_MMTU; #endif + /* Release extra ref */ + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); if (locked) lck_mtx_unlock(ip6_mutex); icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); if (locked) lck_mtx_lock(ip6_mutex); + } else { + /* Release extra ref */ + RT_REMREF_LOCKED(rt); + RT_UNLOCK(rt); } m_freem(m); return; @@ -483,6 +538,8 @@ ip6_forward(m, srcrt, locked) * 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_UNLOCK(rt); if (locked) lck_mtx_unlock(ip6_mutex); icmp6_error(mcopy, ICMP6_DST_UNREACH, @@ -495,19 +552,27 @@ ip6_forward(m, srcrt, locked) 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, rt->rt_ifp, &port, &m)) { + if (ip6_fw_chk_ptr(&ip6, ifp, &port, &m)) { m_freem(m); goto freecopy; } - if (!m) + 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 @@ -555,28 +620,61 @@ ip6_forward(m, srcrt, locked) in6_clearscope(&ip6->ip6_dst); #endif - error = nd6_output(rt->rt_ifp, origifp, m, dst, rt, locked); + ifp = rt->rt_ifp; + /* Drop the lock but retain the extra ref */ + RT_UNLOCK(rt); + +#if PF + if (locked) + lck_mtx_unlock(ip6_mutex); + + /* Invoke outbound packet filter */ + error = pf_af_hook(ifp, NULL, &m, AF_INET6, FALSE); + + if (locked) + lck_mtx_lock(ip6_mutex); + + if (error) { + if (m != NULL) { + panic("%s: unexpected packet %p\n", __func__, m); + /* NOTREACHED */ + } + /* Already freed by callee */ + goto senderr; + } + ip6 = mtod(m, struct ip6_hdr *); +#endif /* PF */ + + error = nd6_output(ifp, origifp, m, dst, rt, locked); if (error) { - in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); + in6_ifstat_inc(ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; } else { ip6stat.ip6s_forward++; - in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); + in6_ifstat_inc(ifp, ifs6_out_forward); if (type) ip6stat.ip6s_redirectsent++; else { - if (mcopy) + if (mcopy) { goto freecopy; + } } } - if (mcopy == NULL) +#if PF +senderr: +#endif /* PF */ + if (mcopy == NULL) { + /* Release extra ref */ + RT_REMREF(rt); return; - + } switch (error) { case 0: #if 1 if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); + /* Release extra ref */ + RT_REMREF(rt); return; } #endif @@ -604,9 +702,13 @@ ip6_forward(m, srcrt, locked) icmp6_error(mcopy, type, code, 0); if (locked) lck_mtx_lock(ip6_mutex); + /* Release extra ref */ + RT_REMREF(rt); return; freecopy: m_freem(mcopy); + /* Release extra ref */ + RT_REMREF(rt); return; }