X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e2fac8b15b12a7979f72090454d850e612fc5b13..b0d623f7f2ae71ed96e60569f61f9a9a27016e80:/bsd/netinet6/icmp6.c diff --git a/bsd/netinet6/icmp6.c b/bsd/netinet6/icmp6.c index db0662895..02d19734f 100644 --- a/bsd/netinet6/icmp6.c +++ b/bsd/netinet6/icmp6.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/icmp6.c,v 1.6.2.6 2001/07/10 09:44:16 ume Exp $ */ /* $KAME: icmp6.c,v 1.211 2001/04/04 05:56:20 itojun Exp $ */ @@ -109,8 +137,8 @@ extern struct domain inet6domain; extern struct ip6protosw inet6sw[]; extern struct ip6protosw *ip6_protox[]; -extern u_long rip_sendspace; -extern u_long rip_recvspace; +extern uint32_t rip_sendspace; +extern uint32_t rip_recvspace; struct icmp6stat icmp6stat; @@ -142,6 +170,10 @@ static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, static int icmp6_notify_error(struct mbuf *, int, int, int); #ifdef COMPAT_RFC1885 +/* + * XXX: Compiled out for now, but if enabled we must use a lock for accesses, + * or easier, define it locally inside icmp6_reflect() and don't cache. + */ static struct route_in6 icmp6_reflect_rt; #endif @@ -436,6 +468,7 @@ icmp6_input(mp, offp) if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error); + switch (icmp6->icmp6_type) { case ICMP6_DST_UNREACH: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); @@ -466,6 +499,7 @@ icmp6_input(mp, offp) default: goto badcode; } + goto deliver; break; @@ -1095,7 +1129,7 @@ icmp6_notify_error(m, off, icmp6len, code) } return(0); - freeit: +freeit: m_freem(m); return(-1); } @@ -1114,7 +1148,6 @@ icmp6_mtudisc_update(ip6cp, validated) if (!validated) return; - /* * In case the suggested mtu is less than IPV6_MMTU, we * only need to remember that it was for above mentioned @@ -1124,7 +1157,6 @@ icmp6_mtudisc_update(ip6cp, validated) if (mtu < IPV6_MMTU) mtu = IPV6_MMTU - 8; - bzero(&sin6, sizeof(sin6)); sin6.sin6_family = PF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); @@ -1136,20 +1168,22 @@ icmp6_mtudisc_update(ip6cp, validated) } /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */ rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_CLONING | RTF_PRCLONING); - - if (rt && (rt->rt_flags & RTF_HOST) - && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { - if (mtu < IPV6_MMTU) { - /* xxx */ - rt->rt_rmx.rmx_locks |= RTV_MTU; - } else if (mtu < rt->rt_ifp->if_mtu && - rt->rt_rmx.rmx_mtu > mtu) { - icmp6stat.icp6s_pmtuchg++; - rt->rt_rmx.rmx_mtu = mtu; + if (rt != NULL) { + RT_LOCK(rt); + if ((rt->rt_flags & RTF_HOST) && + !(rt->rt_rmx.rmx_locks & RTV_MTU)) { + if (mtu < IPV6_MMTU) { + /* xxx */ + rt->rt_rmx.rmx_locks |= RTV_MTU; + } else if (mtu < rt->rt_ifp->if_mtu && + rt->rt_rmx.rmx_mtu > mtu) { + icmp6stat.icp6s_pmtuchg++; + rt->rt_rmx.rmx_mtu = mtu; + } } - } - if (rt) + RT_UNLOCK(rt); rtfree(rt); + } } /* @@ -2006,12 +2040,13 @@ icmp6_reflect(m, off) int mtu = IPV6_MMTU; struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; #endif + u_int32_t oflow; /* too short to reflect */ if (off < sizeof(struct ip6_hdr)) { nd6log((LOG_DEBUG, "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", - (u_long)off, (u_long)sizeof(struct ip6_hdr), + (u_int32_t)off, (u_int32_t)sizeof(struct ip6_hdr), __FILE__, __LINE__)); goto bad; } @@ -2085,7 +2120,9 @@ icmp6_reflect(m, off) * does not fit in with (return) path MTU, but the description was * removed in the new spec. */ - if (icmp6_reflect_rt.ro_rt == 0 || + if (icmp6_reflect_rt.ro_rt == NULL || + !(icmp6_reflect_rt.ro_rt->rt_flags & RTF_UP) || + icmp6_reflect_rt.ro_rt->generation_id != route_generation || ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) { if (icmp6_reflect_rt.ro_rt) { rtfree(icmp6_reflect_rt.ro_rt); @@ -2103,9 +2140,11 @@ icmp6_reflect(m, off) if (icmp6_reflect_rt.ro_rt == 0) goto bad; + RT_LOCK(icmp6_reflect_rt.ro_rt); if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST) && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu) mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu; + RT_UNLOCK(icmp6_reflect_rt.ro_rt); if (mtu < m->m_pkthdr.len) { plen -= (m->m_pkthdr.len - mtu); @@ -2146,10 +2185,8 @@ icmp6_reflect(m, off) */ bzero(&ro, sizeof(ro)); src = in6_selectsrc(&sa6_src, NULL, NULL, &ro, NULL, &src_storage, &e); - if (ro.ro_rt) { + if (ro.ro_rt) rtfree(ro.ro_rt); /* XXX: we could use this */ - ro.ro_rt = NULL; - } if (src == NULL) { nd6log((LOG_DEBUG, "icmp6_reflect: source can't be determined: " @@ -2161,16 +2198,23 @@ icmp6_reflect(m, off) ip6->ip6_src = *src; + oflow = ip6->ip6_flow; /* Save for later */ ip6->ip6_flow = 0; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; + if (icmp6->icmp6_type == ICMP6_ECHO_REPLY && icmp6->icmp6_code == 0) { + ip6->ip6_flow |= (oflow & htonl(0x0ff00000)); + } ip6->ip6_nxt = IPPROTO_ICMPV6; - if (m->m_pkthdr.rcvif) { + if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_index < nd_ifinfo_indexlim) { /* XXX: This may not be the outgoing interface */ + lck_rw_lock_shared(nd_if_rwlock); ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; - } else + lck_rw_done(nd_if_rwlock); + } else { ip6->ip6_hlim = ip6_defhlim; - + } + /* Use the same traffic class as in the request to match IPv4 */ icmp6->icmp6_cksum = 0; icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), plen); @@ -2291,14 +2335,16 @@ icmp6_redirect_input(m, off) sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6)); - rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + rt = rtalloc1((struct sockaddr *)&sin6, 0, 0); if (rt) { + RT_LOCK(rt); if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) { nd6log((LOG_ERR, "ICMP6 redirect rejected; no route " "with inet6 gateway found for redirect dst: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + RT_UNLOCK(rt); rtfree(rt); goto bad; } @@ -2311,6 +2357,7 @@ icmp6_redirect_input(m, off) "%s\n", ip6_sprintf(gw6), icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); + RT_UNLOCK(rt); rtfree(rt); goto bad; } @@ -2321,6 +2368,7 @@ icmp6_redirect_input(m, off) icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } + RT_UNLOCK(rt); rtfree(rt); rt = NULL; } @@ -2449,6 +2497,9 @@ icmp6_redirect_output(m0, rt) icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0); + if (rt != NULL) + RT_LOCK(rt); + /* sanity check */ if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) goto fail; @@ -2470,8 +2521,13 @@ icmp6_redirect_output(m0, rt) src_sa.sin6_addr = sip6->ip6_src; /* we don't currently use sin6_scope_id, but eventually use it */ src_sa.sin6_scope_id = in6_addr2scopeid(ifp, &sip6->ip6_src); - if (nd6_is_addr_neighbor(&src_sa, ifp, 0) == 0) + RT_UNLOCK(rt); + if (nd6_is_addr_neighbor(&src_sa, ifp, 0) == 0) { + /* already unlocked */ + rt = NULL; goto fail; + } + RT_LOCK(rt); if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) goto fail; /* what should we do here? */ @@ -2558,6 +2614,8 @@ icmp6_redirect_output(m0, rt) bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, sizeof(nd_rd->nd_rd_dst)); } + RT_UNLOCK(rt); + rt = NULL; p = (u_char *)(nd_rd + 1); @@ -2572,14 +2630,19 @@ icmp6_redirect_output(m0, rt) struct nd_opt_hdr *nd_opt; char *lladdr; + /* Callee returns a locked route upon success */ rt_router = nd6_lookup(router_ll6, 0, ifp, 0); if (!rt_router) goto nolladdropt; + RT_LOCK_ASSERT_HELD(rt_router); len = sizeof(*nd_opt) + ifp->if_addrlen; len = (len + 7) & ~7; /* round by 8 */ /* safety check */ - if (len + (p - (u_char *)ip6) > maxlen) + if (len + (p - (u_char *)ip6) > maxlen) { + RT_REMREF_LOCKED(rt_router); + RT_UNLOCK(rt_router); goto nolladdropt; + } if (!(rt_router->rt_flags & RTF_GATEWAY) && (rt_router->rt_flags & RTF_LLINFO) && (rt_router->rt_gateway->sa_family == AF_LINK) && @@ -2592,6 +2655,8 @@ icmp6_redirect_output(m0, rt) bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); p += len; } + RT_REMREF_LOCKED(rt_router); + RT_UNLOCK(rt_router); } nolladdropt:; @@ -2713,6 +2778,8 @@ noredhdropt:; return; fail: + if (rt != NULL) + RT_UNLOCK(rt); if (m) m_freem(m); if (m0) @@ -2842,6 +2909,8 @@ icmp6_dgram_ctloutput(struct socket *so, struct sockopt *sopt) case IPV6_LEAVE_GROUP: case IPV6_PORTRANGE: case IPV6_IPSEC_POLICY: + case IPV6_RECVTCLASS: + case IPV6_TCLASS: return ip6_ctloutput(so, sopt); default: @@ -2898,10 +2967,16 @@ icmp6_dgram_send(struct socket *so, __unused int flags, struct mbuf *m, struct s icmp6 = mtod(m, struct icmp6_hdr *); /* - * Allow only to send echo request type 128 with code 0 + * Allow only to send echo request and node information request * See RFC 2463 for Echo Request Message format */ - if (icmp6->icmp6_type != 128 || icmp6->icmp6_code != 0) { + if ((icmp6->icmp6_type == ICMP6_ECHO_REQUEST && icmp6->icmp6_code == 0) || + (icmp6->icmp6_type == ICMP6_NI_QUERY && + (icmp6->icmp6_code == ICMP6_NI_SUBJ_IPV6 || + icmp6->icmp6_code == ICMP6_NI_SUBJ_FQDN))) { + /* Good */ + ; + } else { error = EPERM; goto bad; }