+/*
+ * 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 int ipsec_bypass;
#endif
-#include "faith.h"
-#if defined(NFAITH) && 0 < NFAITH
-#include <net/if_faith.h>
-#endif
-
#include <net/net_osdep.h>
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;
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);
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;
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
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) {
if (n == NULL) {
/* Give up remote */
m_freem(n0);
+ goto rate_limit_checked;
break;
}
M_COPY_PKTHDR(n, n0);
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;
icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++;
icmp6_reflect(n, noff);
}
+ goto rate_limit_checked;
break;
case ICMP6_ECHO_REPLY:
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);
}
mld6_input(n, off);
/* m stays. */
+ goto rate_limit_checked;
break;
case MLD6_LISTENER_DONE:
/* 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)
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);
}
nd6_rs_input(n, off, icmp6len);
/* m stays. */
+ goto rate_limit_checked;
break;
case ND_ROUTER_ADVERT:
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);
}
nd6_ra_input(n, off, icmp6len);
/* m stays. */
+ goto rate_limit_checked;
break;
case ND_NEIGHBOR_SOLICIT:
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);
}
nd6_ns_input(n, off, icmp6len);
/* m stays. */
+ goto rate_limit_checked;
break;
case ND_NEIGHBOR_ADVERT:
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);
}
nd6_na_input(n, off, icmp6len);
/* m stays. */
+ goto rate_limit_checked;
break;
case ND_REDIRECT:
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);
}
icmp6_redirect_input(n, off);
/* m stays. */
+ goto rate_limit_checked;
break;
case ICMP6_ROUTER_RENUMBERING:
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),
/* 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);
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);
}
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
+ * "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;
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);
+ }
}
/*
}
/* 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);
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);
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;
}
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 */
{
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
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? */
#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)
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)
#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) \
int maxpps; /* maximum pps allowed */
{
struct timeval tv, delta;
- int s, rv;
+ int rv;
- s = splclock();
microtime(&tv);
- splx(s);
timersub(&tv, lasttime, &delta);
#if 1 /* DIAGNOSTIC? */
/* be careful about wrap-around */
- if (*curpps + 1 > *curpps)
+ if (*curpps + 1 > 0)
*curpps = *curpps + 1;
#else
/*
* 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;
return ret;
}
+