X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/490019cf9519204c5fb36b2fba54ceb983bb6b72..HEAD:/bsd/netinet6/ip6_forward.c diff --git a/bsd/netinet6/ip6_forward.c b/bsd/netinet6/ip6_forward.c index ee5a70453..02cac6958 100644 --- a/bsd/netinet6/ip6_forward.c +++ b/bsd/netinet6/ip6_forward.c @@ -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 @@ -11,10 +11,10 @@ * 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@ */ @@ -90,17 +90,17 @@ #if IPSEC #include -#if INET6 #include -#endif #include extern int ipsec_bypass; #endif /* IPSEC */ -#include - #include +#if DUMMYNET +#include +#endif /* DUMMYNET */ + #if PF #include #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; }