X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/91447636331957f3d9b5ca5b508f07c526b0074d..b7266188b87f3620ec3f9f717e57194a7dd989fe:/bsd/netinet6/icmp6.c diff --git a/bsd/netinet6/icmp6.c b/bsd/netinet6/icmp6.c index 042bdd76d..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 $ */ @@ -103,17 +131,15 @@ extern int ipsec_bypass; #endif -#include "faith.h" -#if defined(NFAITH) && 0 < NFAITH -#include -#endif - #include extern struct domain inet6domain; extern struct ip6protosw inet6sw[]; extern struct ip6protosw *ip6_protox[]; +extern uint32_t rip_sendspace; +extern uint32_t rip_recvspace; + struct icmp6stat icmp6stat; extern struct inpcbhead ripcb; @@ -123,7 +149,8 @@ static struct timeval icmp6errppslim_last; extern int icmp6_nodeinfo; extern struct inpcbinfo ripcbinfo; extern lck_mtx_t *ip6_mutex; -extern lck_mtx_t *nd6_mutex; +extern lck_mtx_t *nd6_mutex; +extern lck_mtx_t *inet6_domain_mutex; static void icmp6_errcount(struct icmp6errstat *, int, int); static int icmp6_rip6_input(struct mbuf **, int); @@ -143,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 @@ -437,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); @@ -467,6 +499,7 @@ icmp6_input(mp, offp) default: goto badcode; } + goto deliver; break; @@ -517,8 +550,15 @@ icmp6_input(mp, offp) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo); if (code != 0) goto badcode; + + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if ((n = m_copy(m, 0, M_COPYALL)) == NULL) { /* Give up remote */ + goto rate_limit_checked; break; } if ((n->m_flags & M_EXT) != 0 @@ -533,9 +573,10 @@ icmp6_input(mp, offp) if (maxlen >= MCLBYTES) { /* Give up remote */ m_freem(n0); + goto rate_limit_checked; break; } - MGETHDR(n, M_DONTWAIT, n0->m_type); + MGETHDR(n, M_DONTWAIT, n0->m_type); /* MAC-OK */ if (n && maxlen >= MHLEN) { MCLGET(n, M_DONTWAIT); if ((n->m_flags & M_EXT) == 0) { @@ -546,6 +587,7 @@ icmp6_input(mp, offp) if (n == NULL) { /* Give up remote */ m_freem(n0); + goto rate_limit_checked; break; } M_COPY_PKTHDR(n, n0); @@ -569,7 +611,8 @@ icmp6_input(mp, offp) n0->m_flags &= ~M_PKTHDR; } else { nip6 = mtod(n, struct ip6_hdr *); - nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off); + IP6_EXTHDR_GET(nicmp6, struct icmp6_hdr *, n, off, + sizeof(*nicmp6)); noff = off; } nicmp6->icmp6_type = ICMP6_ECHO_REPLY; @@ -579,6 +622,7 @@ icmp6_input(mp, offp) icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++; icmp6_reflect(n, noff); } + goto rate_limit_checked; break; case ICMP6_ECHO_REPLY: @@ -595,6 +639,12 @@ icmp6_input(mp, offp) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery); else icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport); + + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { /* give up local */ mld6_input(m, off); @@ -603,6 +653,7 @@ icmp6_input(mp, offp) } mld6_input(n, off); /* m stays. */ + goto rate_limit_checked; break; case MLD6_LISTENER_DONE: @@ -617,87 +668,37 @@ icmp6_input(mp, offp) /* XXX: per-interface statistics? */ break; /* just pass it to applications */ - case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ - { - enum { WRU, FQDN } mode; - + case ICMP6_NI_QUERY: if (!icmp6_nodeinfo) break; - if (icmp6len == sizeof(struct icmp6_hdr) + 4) - mode = WRU; - else if (icmp6len >= sizeof(struct icmp6_nodeinfo)) - mode = FQDN; - else + /* By RFC 4620 refuse to answer queries from global scope addresses */ + if ((icmp6_nodeinfo & 8) != 8 && in6_addrscope(&ip6->ip6_src) == IPV6_ADDR_SCOPE_GLOBAL) + break; + + if (icmp6len < sizeof(struct icmp6_nodeinfo)) goto badlen; -#define hostnamelen strlen(hostname) - if (mode == FQDN) { #ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), - return IPPROTO_DONE); + IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), + return IPPROTO_DONE); #endif - n = m_copy(m, 0, M_COPYALL); - if (n) - n = ni6_input(n, off); - /* XXX meaningless if n == NULL */ - noff = sizeof(struct ip6_hdr); - } else { - u_char *p; - int maxlen, maxhlen; - - if ((icmp6_nodeinfo & 5) != 5) - break; - - if (code != 0) - goto badcode; - maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4; - if (maxlen >= MCLBYTES) { - /* Give up remote */ - break; - } - MGETHDR(n, M_DONTWAIT, m->m_type); - if (n && maxlen > MHLEN) { - MCLGET(n, M_DONTWAIT); - if ((n->m_flags & M_EXT) == 0) { - m_free(n); - n = NULL; - } - } - if (n == NULL) { - /* Give up remote */ - break; - } - n->m_pkthdr.rcvif = NULL; - n->m_len = 0; - maxhlen = M_TRAILINGSPACE(n) - maxlen; - if (maxhlen > hostnamelen) - maxhlen = hostnamelen; - /* - * Copy IPv6 and ICMPv6 only. - */ - nip6 = mtod(n, struct ip6_hdr *); - bcopy(ip6, nip6, sizeof(struct ip6_hdr)); - nicmp6 = (struct icmp6_hdr *)(nip6 + 1); - bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); - p = (u_char *)(nicmp6 + 1); - bzero(p, 4); - bcopy(hostname, p + 4, maxhlen); /* meaningless TTL */ - noff = sizeof(struct ip6_hdr); - M_COPY_PKTHDR(n, m); /* just for rcvif */ - n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + - sizeof(struct icmp6_hdr) + 4 + maxhlen; - nicmp6->icmp6_type = ICMP6_WRUREPLY; - nicmp6->icmp6_code = 0; + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; } -#undef hostnamelen + + n = m_copy(m, 0, M_COPYALL); + if (n) + n = ni6_input(n, off); if (n) { + noff = sizeof(struct ip6_hdr); icmp6stat.icp6s_reflect++; icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++; icmp6_reflect(n, noff); } + goto rate_limit_checked; break; - } case ICMP6_WRUREPLY: if (code != 0) @@ -710,6 +711,12 @@ icmp6_input(mp, offp) goto badcode; if (icmp6len < sizeof(struct nd_router_solicit)) goto badlen; + + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { /* give up local */ nd6_rs_input(m, off, icmp6len); @@ -718,6 +725,7 @@ icmp6_input(mp, offp) } nd6_rs_input(n, off, icmp6len); /* m stays. */ + goto rate_limit_checked; break; case ND_ROUTER_ADVERT: @@ -726,6 +734,12 @@ icmp6_input(mp, offp) goto badcode; if (icmp6len < sizeof(struct nd_router_advert)) goto badlen; + + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { /* give up local */ nd6_ra_input(m, off, icmp6len); @@ -734,6 +748,7 @@ icmp6_input(mp, offp) } nd6_ra_input(n, off, icmp6len); /* m stays. */ + goto rate_limit_checked; break; case ND_NEIGHBOR_SOLICIT: @@ -742,6 +757,12 @@ icmp6_input(mp, offp) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_solicit)) goto badlen; + + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { /* give up local */ nd6_ns_input(m, off, icmp6len); @@ -750,6 +771,7 @@ icmp6_input(mp, offp) } nd6_ns_input(n, off, icmp6len); /* m stays. */ + goto rate_limit_checked; break; case ND_NEIGHBOR_ADVERT: @@ -758,6 +780,12 @@ icmp6_input(mp, offp) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_advert)) goto badlen; + + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { /* give up local */ nd6_na_input(m, off, icmp6len); @@ -766,6 +794,7 @@ icmp6_input(mp, offp) } nd6_na_input(n, off, icmp6len); /* m stays. */ + goto rate_limit_checked; break; case ND_REDIRECT: @@ -774,6 +803,12 @@ icmp6_input(mp, offp) goto badcode; if (icmp6len < sizeof(struct nd_redirect)) goto badlen; + + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { /* give up local */ icmp6_redirect_input(m, off); @@ -782,6 +817,7 @@ icmp6_input(mp, offp) } icmp6_redirect_input(n, off); /* m stays. */ + goto rate_limit_checked; break; case ICMP6_ROUTER_RENUMBERING: @@ -793,6 +829,11 @@ icmp6_input(mp, offp) break; default: + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + nd6log((LOG_DEBUG, "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), @@ -804,9 +845,15 @@ icmp6_input(mp, offp) /* deliver */ } else { /* ICMPv6 informational: MUST not deliver */ + goto rate_limit_checked; break; } deliver: + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + if (icmp6_notify_error(m, off, icmp6len, code)) { /* In this case, m should've been freed. */ return(IPPROTO_DONE); @@ -822,6 +869,11 @@ icmp6_input(mp, offp) break; } + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } +rate_limit_checked: /* deliver the packet to appropriate sockets */ icmp6_rip6_input(&m, *offp); @@ -1077,7 +1129,7 @@ icmp6_notify_error(m, off, icmp6len, code) } return(0); - freeit: +freeit: m_freem(m); return(-1); } @@ -1096,6 +1148,14 @@ 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 + * "alwaysfrag" case. + * Try to be as close to the spec as possible. + */ + if (mtu < IPV6_MMTU) + mtu = IPV6_MMTU - 8; bzero(&sin6, sizeof(sin6)); sin6.sin6_family = PF_INET6; @@ -1107,22 +1167,23 @@ icmp6_mtudisc_update(ip6cp, validated) htons(m->m_pkthdr.rcvif->if_index); } /* 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; + rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_CLONING | RTF_PRCLONING); + 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); + } } /* @@ -1359,7 +1420,7 @@ ni6_input(m, off) } /* allocate an mbuf to reply. */ - MGETHDR(n, M_DONTWAIT, m->m_type); + MGETHDR(n, M_DONTWAIT, m->m_type); /* MAC-OK */ if (n == NULL) { m_freem(m); return(NULL); @@ -1979,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; } @@ -2058,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); @@ -2076,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); @@ -2132,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); @@ -2262,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; } @@ -2282,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; } @@ -2292,6 +2368,7 @@ icmp6_redirect_input(m, off) icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } + RT_UNLOCK(rt); rtfree(rt); rt = NULL; } @@ -2365,10 +2442,9 @@ icmp6_redirect_input(m, off) bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); - rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw, - (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST, - (struct sockaddr *)&ssrc, - (struct rtentry **)NULL); + rtredirect(ifp, (struct sockaddr *)&sdst, + (struct sockaddr *)&sgw, NULL, RTF_GATEWAY | RTF_HOST, + (struct sockaddr *)&ssrc, NULL); } /* finally update cached route in each socket via pfctlinput */ { @@ -2378,7 +2454,16 @@ icmp6_redirect_input(m, off) sdst.sin6_family = AF_INET6; sdst.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); + + /* + * Radar 6843900 + * Release the IPv6 domain lock because we are going to take domain_proto_mtx + * and could otherwise cause a deadlock with other threads taking these locks + * in the reverse order -- e.g. frag6_slowtimo() from pfslowtimo() + */ + lck_mtx_unlock(inet6_domain_mutex); pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst); + lck_mtx_lock(inet6_domain_mutex); #if IPSEC key_sa_routechange((struct sockaddr *)&sdst); #endif @@ -2412,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; @@ -2433,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? */ @@ -2450,7 +2543,7 @@ icmp6_redirect_output(m0, rt) #if IPV6_MMTU >= MCLBYTES # error assumption failed about IPV6_MMTU and MCLBYTES #endif - MGETHDR(m, M_DONTWAIT, MT_HEADER); + MGETHDR(m, M_DONTWAIT, MT_HEADER); /* MAC-OK */ if (m && IPV6_MMTU >= MHLEN) MCLGET(m, M_DONTWAIT); if (!m) @@ -2521,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); @@ -2535,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) && @@ -2555,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:; @@ -2676,6 +2778,8 @@ noredhdropt:; return; fail: + if (rt != NULL) + RT_UNLOCK(rt); if (m) m_freem(m); if (m0) @@ -2766,6 +2870,165 @@ icmp6_ctloutput(so, sopt) #undef in6p_icmp6filt #endif +/* + * ICMPv6 socket datagram option processing. + */ +int +icmp6_dgram_ctloutput(struct socket *so, struct sockopt *sopt) +{ + if (so->so_uid == 0) + return icmp6_ctloutput(so, sopt); + + if (sopt->sopt_level == IPPROTO_ICMPV6) { + switch (sopt->sopt_name) { + case ICMP6_FILTER: + return icmp6_ctloutput(so, sopt); + default: + return EPERM; + } + } + + if (sopt->sopt_level != IPPROTO_IPV6) + return EINVAL; + + switch (sopt->sopt_name) { + case IPV6_PKTOPTIONS: + case IPV6_UNICAST_HOPS: + case IPV6_CHECKSUM: + case IPV6_FAITH: + case IPV6_V6ONLY: + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: + case IPV6_HOPOPTS: + case IPV6_DSTOPTS: + case IPV6_RTHDR: + case IPV6_MULTICAST_IF: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: + case IPV6_JOIN_GROUP: + case IPV6_LEAVE_GROUP: + case IPV6_PORTRANGE: + case IPV6_IPSEC_POLICY: + case IPV6_RECVTCLASS: + case IPV6_TCLASS: + return ip6_ctloutput(so, sopt); + + default: + return EPERM; + + + } +} + +__private_extern__ int +icmp6_dgram_send(struct socket *so, __unused int flags, struct mbuf *m, struct sockaddr *nam, + struct mbuf *control, __unused struct proc *p) +{ + int error = 0; + struct inpcb *inp = sotoinpcb(so); + struct sockaddr_in6 tmp; + struct sockaddr_in6 *dst; + struct icmp6_hdr *icmp6; + + if (so->so_uid == 0) + return rip6_output(m, so, (struct sockaddr_in6 *) nam, control); + + /* always copy sockaddr to avoid overwrites */ + if (so->so_state & SS_ISCONNECTED) { + if (nam) { + m_freem(m); + return EISCONN; + } + /* XXX */ + bzero(&tmp, sizeof(tmp)); + tmp.sin6_family = AF_INET6; + tmp.sin6_len = sizeof(struct sockaddr_in6); + bcopy(&inp->in6p_faddr, &tmp.sin6_addr, + sizeof(struct in6_addr)); + dst = &tmp; + } else { + if (nam == NULL) { + m_freem(m); + return ENOTCONN; + } + tmp = *(struct sockaddr_in6 *)nam; + dst = &tmp; + } + + /* + * For an ICMPv6 packet, we should know its type and code + */ + if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + if (m->m_len < sizeof(struct icmp6_hdr) && + (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { + error = ENOBUFS; + goto bad; + } + icmp6 = mtod(m, struct icmp6_hdr *); + + /* + * Allow only to send echo request and node information request + * See RFC 2463 for Echo Request Message format + */ + 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; + } + } + +#if ENABLE_DEFAULT_SCOPE + if (dst->sin6_scope_id == 0) { /* not change if specified */ + dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); + } +#endif + + return rip6_output(m, so, (struct sockaddr_in6 *) nam, control); +bad: + m_freem(m); + return error; +} + +/* Like rip6_attach but without root privilege enforcement */ +__private_extern__ int +icmp6_dgram_attach(struct socket *so, int proto, struct proc *p) +{ + struct inpcb *inp; + int error; + + inp = sotoinpcb(so); + if (inp) + panic("icmp6_dgram_attach"); + + if (proto != IPPROTO_ICMPV6) + return EINVAL; + + error = soreserve(so, rip_sendspace, rip_recvspace); + if (error) + return error; + error = in_pcballoc(so, &ripcbinfo, p); + if (error) + return error; + inp = (struct inpcb *)so->so_pcb; + inp->inp_vflag |= INP_IPV6; + inp->in6p_ip6_nxt = IPPROTO_ICMPV6; + inp->in6p_hops = -1; /* use kernel default */ + inp->in6p_cksum = -1; + MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *, + sizeof(struct icmp6_filter), M_PCB, M_WAITOK); + if (inp->in6p_icmp6filt == NULL) + return (ENOMEM); + ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); + return 0; +} + + + #ifndef HAVE_PPSRATECHECK #ifndef timersub #define timersub(tvp, uvp, vvp) \ @@ -2789,11 +3052,9 @@ ppsratecheck(lasttime, curpps, maxpps) int maxpps; /* maximum pps allowed */ { struct timeval tv, delta; - int s, rv; + int rv; - s = splclock(); microtime(&tv); - splx(s); timersub(&tv, lasttime, &delta); @@ -2819,7 +3080,7 @@ ppsratecheck(lasttime, curpps, maxpps) #if 1 /* DIAGNOSTIC? */ /* be careful about wrap-around */ - if (*curpps + 1 > *curpps) + if (*curpps + 1 > 0) *curpps = *curpps + 1; #else /* @@ -2845,10 +3106,10 @@ ppsratecheck(lasttime, curpps, maxpps) * XXX per-destination/type check necessary? */ static int -icmp6_ratelimit(dst, type, code) - const struct in6_addr *dst; /* not used at this moment */ - const int type; /* not used at this moment */ - const int code; /* not used at this moment */ +icmp6_ratelimit( + __unused const struct in6_addr *dst, /* not used at this moment */ + __unused const int type, /* not used at this moment */ + __unused const int code) /* not used at this moment */ { int ret; @@ -2863,3 +3124,4 @@ icmp6_ratelimit(dst, type, code) return ret; } +