+/*
+ * 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 $ */
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;
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
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);
default:
goto badcode;
}
+
goto deliver;
break;
}
return(0);
- freeit:
+freeit:
m_freem(m);
return(-1);
}
if (!validated)
return;
-
/*
* In case the suggested mtu is less than IPV6_MMTU, we
* only need to remember that it was for above mentioned
if (mtu < IPV6_MMTU)
mtu = IPV6_MMTU - 8;
-
bzero(&sin6, sizeof(sin6));
sin6.sin6_family = PF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
}
/* 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);
+ }
}
/*
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;
}
* 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);
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);
*/
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: "
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);
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;
}
"%s\n",
ip6_sprintf(gw6),
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
+ RT_UNLOCK(rt);
rtfree(rt);
goto bad;
}
icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
goto bad;
}
+ RT_UNLOCK(rt);
rtfree(rt);
rt = NULL;
}
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;
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? */
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);
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) &&
bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
p += len;
}
+ RT_REMREF_LOCKED(rt_router);
+ RT_UNLOCK(rt_router);
}
nolladdropt:;
return;
fail:
+ if (rt != NULL)
+ RT_UNLOCK(rt);
if (m)
m_freem(m);
if (m0)
case IPV6_LEAVE_GROUP:
case IPV6_PORTRANGE:
case IPV6_IPSEC_POLICY:
+ case IPV6_RECVTCLASS:
+ case IPV6_TCLASS:
return ip6_ctloutput(so, sopt);
default:
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;
}