X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..d190cdc3f5544636abb56dc1874be391d3e1b148:/bsd/netinet6/icmp6.c diff --git a/bsd/netinet6/icmp6.c b/bsd/netinet6/icmp6.c index 43a61a6d2..23a9178d3 100644 --- a/bsd/netinet6/icmp6.c +++ b/bsd/netinet6/icmp6.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2011 Apple Inc. All rights reserved. + * Copyright (c) 2000-2016 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@ */ @@ -98,6 +98,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,7 @@ #include #include #include +#include #include #include @@ -120,6 +122,7 @@ #include #include #include +#include #include #include #include @@ -128,14 +131,14 @@ #if IPSEC #include #include - -extern int ipsec_bypass; #endif #include -extern struct domain inet6domain; -extern struct ip6protosw inet6sw[]; +#if NECP +#include +#endif + extern struct ip6protosw *ip6_protox[]; extern uint32_t rip_sendspace; @@ -145,24 +148,23 @@ struct icmp6stat icmp6stat; extern struct inpcbhead ripcb; extern int icmp6errppslim; +extern int icmp6rappslim; static int icmp6errpps_count = 0; +static int icmp6rapps_count = 0; static struct timeval icmp6errppslim_last; +static struct timeval icmp6rappslim_last; extern int icmp6_nodeinfo; extern struct inpcbinfo ripcbinfo; -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_ratelimit(const struct in6_addr *, const int, const int); static const char *icmp6_redirect_diag(struct in6_addr *, struct in6_addr *, struct in6_addr *); -#ifndef HAVE_PPSRATECHECK -static int ppsratecheck(struct timeval *, int *, int); -#endif static struct mbuf *ni6_input(struct mbuf *, int); static struct mbuf *ni6_nametodns(const char *, int, int); static int ni6_dnsmatch(const char *, int, const char *, int); -static int ni6_addrs(struct icmp6_nodeinfo *, +static int ni6_addrs(struct icmp6_nodeinfo *, struct ifnet **, char *); static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, struct ifnet *, int); @@ -171,15 +173,24 @@ static int icmp6_notify_error(struct mbuf *, int, int, int); void -icmp6_init() +icmp6_init(struct ip6protosw *pp, struct domain *dp) { - mld_init(); +#pragma unused(dp) + static int icmp6_initialized = 0; + + /* Also called from ip6_init() without pp */ + VERIFY(pp == NULL || + (pp->pr_flags & (PR_INITIALIZED|PR_ATTACHED)) == PR_ATTACHED); + + /* This gets called by more than one protocols, so initialize once */ + if (!icmp6_initialized) { + icmp6_initialized = 1; + mld_init(); + } } static void -icmp6_errcount(stat, type, code) - struct icmp6errstat *stat; - int type, code; +icmp6_errcount(struct icmp6errstat *stat, int type, int code) { switch (type) { case ICMP6_DST_UNREACH: @@ -271,7 +282,11 @@ icmp6_error2(struct mbuf *m, int type, int code, int param, * Generate an error packet of type error in response to bad IP6 packet. */ void -icmp6_error(struct mbuf *m, int type, int code, int param) +icmp6_error(struct mbuf *m, int type, int code, int param) { + icmp6_error_flag(m, type, code, param, ICMP6_ERROR_RST_MRCVIF); +} + +void icmp6_error_flag (struct mbuf *m, int type, int code, int param, int flags) { struct ip6_hdr *oip6, *nip6; struct icmp6_hdr *icmp6; @@ -380,7 +395,7 @@ icmp6_error(struct mbuf *m, int type, int code, int param) m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); - M_PREPEND(m, preplen, M_DONTWAIT); + M_PREPEND(m, preplen, M_DONTWAIT, 1); if (m && m->m_len < preplen) m = m_pullup(m, preplen); if (m == NULL) { @@ -407,7 +422,9 @@ icmp6_error(struct mbuf *m, int type, int code, int param) * clear m->m_pkthdr.rcvif for safety, we should have enough scope * information in ip header (nip6). */ - m->m_pkthdr.rcvif = NULL; + if (flags & ICMP6_ERROR_RST_MRCVIF) { + m->m_pkthdr.rcvif = NULL; + } icmp6stat.icp6s_outhist[type]++; icmp6_reflect(m, sizeof(struct ip6_hdr)); /* header order: IPv6 - ICMPv6 */ @@ -434,7 +451,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) struct icmp6_hdr *icmp6, *nicmp6; int off = *offp; int icmp6len = m->m_pkthdr.len - *offp; - int code, sum, noff; + int code, sum, noff, proxy = 0; ifp = m->m_pkthdr.rcvif; @@ -443,17 +460,55 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) /* m might change if M_LOOP. So, call mtod after this */ #endif + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); + /* * Locate icmp6 structure in mbuf, and check * that not corrupted and of at least minimum length */ - ip6 = mtod(m, struct ip6_hdr *); if (icmp6len < sizeof(struct icmp6_hdr)) { icmp6stat.icp6s_tooshort++; goto freeit; } +#ifndef PULLDOWN_TEST + icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + code = icmp6->icmp6_code; + + /* + * Early check for RFC 6980 + * Drop certain NDP packets if they came in fragmented + */ + switch (icmp6->icmp6_type) { + case ND_ROUTER_SOLICIT: + case ND_ROUTER_ADVERT: + case ND_NEIGHBOR_SOLICIT: + case ND_NEIGHBOR_ADVERT: + case ND_REDIRECT: + if (m->m_pkthdr.pkt_flags & PKTF_REASSEMBLED) { + icmp6stat.icp6s_rfc6980_drop++; + goto freeit; + } + break; + default: + break; + } + + /* Apply rate limit before checksum validation. */ + if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { + icmp6stat.icp6s_toofreq++; + goto freeit; + } + /* * Check multicast group membership. * Note: SSM filters are not applied for ICMPv6 traffic. @@ -466,9 +521,16 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) in6_multihead_lock_done(); if (inm == NULL) { - ip6stat.ip6s_notmember++; - in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); - goto freeit; + /* + * Don't discard if this is a Neighbor Solicitation + * that needs to be proxied (see check down below.) + */ + if (!(m->m_pkthdr.pkt_flags & PKTF_PROXY_DST)) { + ip6stat.ip6s_notmember++; + in6_ifstat_inc(m->m_pkthdr.rcvif, + ifs6_in_discard); + goto freeit; + } } else { IN6M_REMREF(inm); } @@ -477,17 +539,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) /* * calculate the checksum */ -#ifndef PULLDOWN_TEST - icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); -#else - IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); - if (icmp6 == NULL) { - icmp6stat.icp6s_tooshort++; - return IPPROTO_DONE; - } -#endif - code = icmp6->icmp6_code; - if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { nd6log((LOG_ERR, "ICMP6 checksum error(%d|%x) %s\n", @@ -496,23 +547,22 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) goto freeit; } -#if defined(NFAITH) && 0 < NFAITH - if (faithprefix(&ip6->ip6_dst)) { + if (m->m_pkthdr.pkt_flags & PKTF_PROXY_DST) { /* - * Deliver very specific ICMP6 type only. - * This is important to deliver TOOBIG. Otherwise PMTUD - * will not work. + * This is the special case of proxying NS (dst is either + * solicited-node multicast or unicast); process it locally + * but don't deliver it to sockets. It practically lets us + * steer the packet to nd6_prproxy_ns_input, where more + * specific tests and actions will be taken. */ switch (icmp6->icmp6_type) { - case ICMP6_DST_UNREACH: - case ICMP6_PACKET_TOO_BIG: - case ICMP6_TIME_EXCEEDED: + case ND_NEIGHBOR_SOLICIT: + proxy = 1; break; default: goto freeit; } } -#endif icmp6stat.icp6s_inhist[icmp6->icmp6_type]++; icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg); @@ -544,7 +594,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) goto badcode; } goto deliver; - break; case ICMP6_PACKET_TOO_BIG: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig); @@ -556,7 +605,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) * intermediate extension headers. */ goto deliver; - break; case ICMP6_TIME_EXCEEDED: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed); @@ -571,7 +619,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) goto badcode; } goto deliver; - break; case ICMP6_PARAM_PROB: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob); @@ -587,22 +634,15 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) goto badcode; } goto deliver; - break; case ICMP6_ECHO_REQUEST: 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 || n->m_len < off + sizeof(struct icmp6_hdr)) { @@ -617,7 +657,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) /* Give up remote */ m_freem(n0); goto rate_limit_checked; - break; } MGETHDR(n, M_DONTWAIT, n0->m_type); /* MAC-OK */ if (n && maxlen >= MHLEN) { @@ -631,7 +670,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) /* Give up remote */ m_freem(n0); goto rate_limit_checked; - break; } M_COPY_PKTHDR(n, n0); /* @@ -658,15 +696,18 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) sizeof(*nicmp6)); noff = off; } - nicmp6->icmp6_type = ICMP6_ECHO_REPLY; - nicmp6->icmp6_code = 0; + if(nicmp6 == NULL) + panic("nicmp6 is NULL in %s, which isn't good!\n", __FUNCTION__); + else { + nicmp6->icmp6_type = ICMP6_ECHO_REPLY; + nicmp6->icmp6_code = 0; + } if (n) { icmp6stat.icp6s_reflect++; 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_echoreply); @@ -684,11 +725,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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 */ if (mld_input(m, off, icmp6len) == IPPROTO_DONE) @@ -699,7 +735,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) m_freem(n); /* m stays. */ goto rate_limit_checked; - break; case MLD_LISTENER_DONE: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone); @@ -717,7 +752,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) if (!icmp6_nodeinfo) break; //### LD 10/20 Check fbsd differences here. Not sure we're more advanced or not. - /* By RFC 4620 refuse to answer queries from global scope addresses */ + /* 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; @@ -728,10 +763,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), return IPPROTO_DONE); #endif - if (icmp6_ratelimit(&ip6->ip6_dst, icmp6->icmp6_type, code)) { - icmp6stat.icp6s_toofreq++; - goto freeit; - } n = m_copy(m, 0, M_COPYALL); if (n) @@ -743,7 +774,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) icmp6_reflect(n, noff); } goto rate_limit_checked; - break; case ICMP6_WRUREPLY: if (code != 0) @@ -757,11 +787,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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); @@ -771,7 +796,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) nd6_rs_input(n, off, icmp6len); /* m stays. */ goto rate_limit_checked; - break; case ND_ROUTER_ADVERT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert); @@ -780,11 +804,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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); @@ -794,7 +813,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) nd6_ra_input(n, off, icmp6len); /* m stays. */ goto rate_limit_checked; - break; case ND_NEIGHBOR_SOLICIT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit); @@ -803,12 +821,8 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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) { + if (proxy || + ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL)) { /* give up local */ nd6_ns_input(m, off, icmp6len); m = NULL; @@ -817,7 +831,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) nd6_ns_input(n, off, icmp6len); /* m stays. */ goto rate_limit_checked; - break; case ND_NEIGHBOR_ADVERT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert); @@ -826,11 +839,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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); @@ -840,7 +848,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) nd6_na_input(n, off, icmp6len); /* m stays. */ goto rate_limit_checked; - break; case ND_REDIRECT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect); @@ -849,11 +856,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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); @@ -863,7 +865,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) icmp6_redirect_input(n, off); /* m stays. */ goto rate_limit_checked; - break; case ICMP6_ROUTER_RENUMBERING: if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && @@ -874,11 +875,6 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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), @@ -891,14 +887,8 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) } 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); @@ -914,25 +904,17 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) 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 IPPROTO_DONE; - freeit: +freeit: m_freem(m); return IPPROTO_DONE; } static int -icmp6_notify_error(m, off, icmp6len, code) - struct mbuf *m; - int off, icmp6len, code; +icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code) { struct icmp6_hdr *icmp6; struct ip6_hdr *eip6; @@ -1051,7 +1033,7 @@ icmp6_notify_error(m, off, icmp6len, code) /* just ignore a bogus header */ if ((rth0->ip6r0_len % 2) == 0 && (hops = rth0->ip6r0_len/2)) - finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); + finaldst = (struct in6_addr *)(void *)(rth0 + 1) + (hops - 1); } eoff += rthlen; nxt = rth->ip6r_nxt; @@ -1166,9 +1148,7 @@ freeit: } void -icmp6_mtudisc_update(ip6cp, validated) - struct ip6ctlparam *ip6cp; - int validated; +icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated) { struct in6_addr *dst = ip6cp->ip6c_finaldst; struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6; @@ -1205,6 +1185,13 @@ 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 */ + /* + * XXX On a side note, for asymmetric data-path + * the lookup on receive interace is probably not + * what we want to do. + * That requires looking at the cached route for the + * protocol control block. + */ rt = rtalloc1_scoped((struct sockaddr *)&sin6, 0, RTF_CLONING | RTF_PRCLONING, m->m_pkthdr.rcvif->if_index); if (rt != NULL) { @@ -1234,9 +1221,7 @@ icmp6_mtudisc_update(ip6cp, validated) */ #define hostnamelen strlen(hostname) static struct mbuf * -ni6_input(m, off) - struct mbuf *m; - int off; +ni6_input(struct mbuf *m, int off) { struct icmp6_nodeinfo *ni6, *nni6; struct mbuf *n = NULL; @@ -1294,23 +1279,18 @@ ni6_input(m, off) goto bad; /* else it's a link-local multicast, fine */ } else { /* unicast or anycast */ - struct in6_ifaddr *ia6; + uint32_t ia6_flags; - if ((ia6 = ip6_getdstifaddr(m)) == NULL) + if (ip6_getdstifaddr_info(m, NULL, &ia6_flags) != 0) goto bad; /* XXX impossible */ - IFA_LOCK(&ia6->ia_ifa); - if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) && + if ((ia6_flags & IN6_IFF_TEMPORARY) && !(icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK)) { nd6log((LOG_DEBUG, "ni6_input: ignore node info to " "a temporary address in %s:%d", __FILE__, __LINE__)); - IFA_UNLOCK(&ia6->ia_ifa); - IFA_REMREF(&ia6->ia_ifa); goto bad; } - IFA_UNLOCK(&ia6->ia_ifa); - IFA_REMREF(&ia6->ia_ifa); } /* validate query Subject field. */ @@ -1580,10 +1560,10 @@ bad: * treated as truncated name (two \0 at the end). this is a wild guess. */ static struct mbuf * -ni6_nametodns(name, namelen, old) - const char *name; - int namelen; - int old; /* return pascal string if non-zero */ +ni6_nametodns( + const char *name, + int namelen, + int old) /* return pascal string if non-zero */ { struct mbuf *m; char *cp, *ep; @@ -1680,11 +1660,7 @@ ni6_nametodns(name, namelen, old) * XXX upper/lowercase match (see RFC2065) */ static int -ni6_dnsmatch(a, alen, b, blen) - const char *a; - int alen; - const char *b; - int blen; +ni6_dnsmatch(const char *a, int alen, const char *b, int blen) { const char *a0, *b0; int l; @@ -1744,10 +1720,7 @@ ni6_dnsmatch(a, alen, b, blen) * calculate the number of addresses to be returned in the node info reply. */ static int -ni6_addrs(ni6, ifpp, subj) - struct icmp6_nodeinfo *ni6; - struct ifnet **ifpp; - char *subj; +ni6_addrs(struct icmp6_nodeinfo *ni6, struct ifnet **ifpp, char *subj) { struct ifnet *ifp; struct in6_ifaddr *ifa6; @@ -1764,7 +1737,7 @@ ni6_addrs(ni6, ifpp, subj) case ICMP6_NI_SUBJ_IPV6: if (subj == NULL) /* must be impossible... */ return(0); - subj_ip6 = (struct sockaddr_in6 *)subj; + subj_ip6 = (struct sockaddr_in6 *)(void *)subj; break; default: /* @@ -1863,10 +1836,8 @@ ni6_addrs(ni6, ifpp, subj) } static int -ni6_store_addrs(ni6, nni6, ifp0, resid) - struct icmp6_nodeinfo *ni6, *nni6; - struct ifnet *ifp0; - int resid; +ni6_store_addrs(struct icmp6_nodeinfo *ni6, struct icmp6_nodeinfo *nni6, + struct ifnet *ifp0, int resid) { struct ifnet *ifp = ifp0; struct in6_ifaddr *ifa6; @@ -1876,9 +1847,7 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) u_char *cp = (u_char *)(nni6 + 1); int niflags = ni6->ni_flags; u_int32_t ltime; - struct timeval timenow; - - getmicrotime(&timenow); + uint64_t now = net_uptime(); if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL)) return (0); /* needless to copy */ @@ -1893,6 +1862,8 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) ifnet_lock_shared(ifp); for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { + struct in6_addrlifetime_i *lt; + IFA_LOCK(ifa); if (ifa->ifa_addr->sa_family != AF_INET6) { IFA_UNLOCK(ifa); @@ -1988,13 +1959,12 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) * address configuration by DHCPv6, so the former * case can't happen. */ - if (ifa6->ia6_lifetime.ia6t_expire == 0 && - (ifa6->ia6_flags & IN6_IFF_TEMPORARY) == 0) + lt = &ifa6->ia6_lifetime; + if (lt->ia6ti_expire == 0) { ltime = ND6_INFINITE_LIFETIME; - else { - if (ifa6->ia6_lifetime.ia6t_expire > - timenow.tv_sec) - ltime = htonl(ifa6->ia6_lifetime.ia6t_expire - timenow.tv_sec); + } else { + if (lt->ia6ti_expire > now) + ltime = htonl(lt->ia6ti_expire - now); else ltime = 0; } @@ -2007,7 +1977,7 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) sizeof(struct in6_addr)); /* XXX: KAME link-local hack; remove ifindex */ if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) - ((struct in6_addr *)cp)->s6_addr16[1] = 0; + ((struct in6_addr *)(void *)cp)->s6_addr16[1] = 0; cp += sizeof(struct in6_addr); resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t)); @@ -2035,9 +2005,7 @@ ni6_store_addrs(ni6, nni6, ifp0, resid) * XXX almost dup'ed code with rip6_input. */ static int -icmp6_rip6_input(mp, off) - struct mbuf **mp; - int off; +icmp6_rip6_input(struct mbuf **mp, int off) { struct mbuf *m = *mp; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); @@ -2047,6 +2015,7 @@ icmp6_rip6_input(mp, off) struct icmp6_hdr *icmp6; struct mbuf *opts = NULL; int ret = 0; + struct ifnet *ifp = m->m_pkthdr.rcvif; #ifndef PULLDOWN_TEST /* this is assumed to be safe. */ @@ -2067,10 +2036,10 @@ icmp6_rip6_input(mp, off) rip6src.sin6_family = AF_INET6; rip6src.sin6_len = sizeof(struct sockaddr_in6); rip6src.sin6_addr = ip6->ip6_src; - if (sa6_recoverscope(&rip6src)) + if (sa6_recoverscope(&rip6src, TRUE)) return (IPPROTO_DONE); - - lck_rw_lock_shared(ripcbinfo.mtx); + + lck_rw_lock_shared(ripcbinfo.ipi_lock); LIST_FOREACH(in6p, &ripcb, inp_list) { if ((in6p->inp_vflag & INP_IPV6) == 0) @@ -2087,10 +2056,14 @@ icmp6_rip6_input(mp, off) && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, in6p->in6p_icmp6filt)) continue; + + if (inp_restricted_recv(in6p, ifp)) + continue; + if (last) { struct mbuf *n; if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { - if ((last->in6p_flags & IN6P_CONTROLOPTS) != 0 || + if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 || (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0) { ret = ip6_savecontrol(last, n, &opts); @@ -2133,16 +2106,15 @@ icmp6_rip6_input(mp, off) } else { goto error; } - lck_rw_done(ripcbinfo.mtx); + lck_rw_done(ripcbinfo.ipi_lock); return IPPROTO_DONE; error: - lck_rw_done(ripcbinfo.mtx); + lck_rw_done(ripcbinfo.ipi_lock); m_freem(m); m_freem(opts); ip6stat.ip6s_delivered--; - return IPPROTO_DONE; - + return IPPROTO_DONE; } /* @@ -2150,10 +2122,9 @@ error: * OFF points to the icmp6 header, counted from the top of the mbuf. */ void -icmp6_reflect(m, off) - struct mbuf *m; - size_t off; +icmp6_reflect(struct mbuf *m, size_t off) { + struct mbuf *m_ip6hdr = m; struct ip6_hdr *ip6; struct icmp6_hdr *icmp6; struct in6_ifaddr *ia; @@ -2162,11 +2133,17 @@ icmp6_reflect(m, off) int type, code; struct ifnet *outif = NULL; struct sockaddr_in6 sa6_src, sa6_dst; + struct nd_ifinfo *ndi = NULL; u_int32_t oflow; - struct ip6_out_args ip6oa = { IFSCOPE_NONE, 0 }; + struct ip6_out_args ip6oa = { IFSCOPE_NONE, { 0 }, + IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR | + IP6OAF_INTCOPROC_ALLOWED | IP6OAF_AWDL_UNRESTRICTED, 0, + SO_TC_UNSPEC, _NET_SERVICE_TYPE_UNSPEC }; - if ((m->m_flags & M_PKTHDR) && m->m_pkthdr.rcvif != NULL) + if (!(m->m_pkthdr.pkt_flags & PKTF_LOOP) && m->m_pkthdr.rcvif != NULL) { ip6oa.ip6oa_boundif = m->m_pkthdr.rcvif->if_index; + ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; + } /* too short to reflect */ if (off < sizeof(struct ip6_hdr)) { @@ -2181,10 +2158,6 @@ icmp6_reflect(m, off) * If there are extra headers between IPv6 and ICMPv6, strip * off that header first. */ -#if DIAGNOSTIC - if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN) - panic("assumption failed in icmp6_reflect"); -#endif if (off > sizeof(struct ip6_hdr)) { size_t l; struct ip6_hdr nip6; @@ -2194,7 +2167,7 @@ icmp6_reflect(m, off) m_adj(m, l); l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); if (m->m_len < l) { - if ((m = m_pullup(m, l)) == NULL) + if ((m_ip6hdr = m_pulldown(m, 0, l, NULL)) == NULL) return; } bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6)); @@ -2202,12 +2175,12 @@ icmp6_reflect(m, off) size_t l; l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); if (m->m_len < l) { - if ((m = m_pullup(m, l)) == NULL) + if ((m_ip6hdr = m_pulldown(m, 0, l, NULL)) == NULL) return; } } plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); - ip6 = mtod(m, struct ip6_hdr *); + ip6 = mtod(m_ip6hdr, struct ip6_hdr *); ip6->ip6_nxt = IPPROTO_ICMPV6; icmp6 = (struct icmp6_hdr *)(ip6 + 1); type = icmp6->icmp6_type; /* keep type for statistics */ @@ -2260,10 +2233,14 @@ icmp6_reflect(m, off) IFA_UNLOCK(&ia->ia_ifa); } lck_rw_done(&in6_ifaddr_rwlock); - if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) { + if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && + ((m->m_flags & M_LOOP) || (m->m_pkthdr.pkt_flags & PKTF_LOOP))) { /* * This is the case if the dst is our link-local address - * and the sender is also ourselves. + * and the sender is also ourselves. Here we test for both + * M_LOOP and PKTF_LOOP, since the former may have been set + * in ip6_output() and that we get here as part of callling + * ip6_process_hopopts(). See comments in */ src = &t; } @@ -2284,10 +2261,14 @@ icmp6_reflect(m, off) sin6.sin6_addr = ip6->ip6_dst; /* zone ID should be embedded */ bzero(&ro, sizeof(ro)); + /* + * in6_selectsrc() might return outif with its reference held + * even in the error case, so we always need to release it + * if non-NULL. + */ src = in6_selectsrc(&sin6, NULL, NULL, &ro, &outif, &src_storage, ip6oa.ip6oa_boundif, &e); - if (ro.ro_rt) - rtfree(ro.ro_rt); /* XXX: we could use this */ + ROUTE_RELEASE(&ro); if (src == NULL) { nd6log((LOG_DEBUG, "icmp6_reflect: source can't be determined: " @@ -2305,36 +2286,39 @@ icmp6_reflect(m, off) ip6->ip6_flow |= (oflow & htonl(0x0ff00000)); } ip6->ip6_nxt = IPPROTO_ICMPV6; - lck_rw_lock_shared(nd_if_rwlock); - if (outif) - ip6->ip6_hlim = ND_IFINFO(outif)->chlim; - if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_index < nd_ifinfo_indexlim) { + if (outif != NULL && (ndi = ND_IFINFO(outif)) != NULL && + ndi->initialized) { + lck_mtx_lock(&ndi->lock); + ip6->ip6_hlim = ndi->chlim; + lck_mtx_unlock(&ndi->lock); + } + if (m->m_pkthdr.rcvif != NULL && + (ndi = ND_IFINFO(m->m_pkthdr.rcvif)) != NULL && + ndi->initialized) { /* XXX: This may not be the outgoing interface */ - ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; + lck_mtx_lock(&ndi->lock); + ip6->ip6_hlim = ndi->chlim; + lck_mtx_unlock(&ndi->lock); } else { ip6->ip6_hlim = ip6_defhlim; } - lck_rw_done(nd_if_rwlock); /* 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); + sizeof(struct ip6_hdr), plen); /* * XXX option handling */ - m->m_flags &= ~(M_BCAST|M_MCAST); -#if IPSEC - /* Don't lookup socket */ - if (ipsec_bypass == 0) - (void)ipsec_setsocket(m, NULL); -#endif /*IPSEC*/ if (outif != NULL) { ifnet_release(outif); outif = NULL; } + + m->m_pkthdr.csum_data = 0; + m->m_pkthdr.csum_flags = 0; ip6_output(m, NULL, NULL, IPV6_OUTARGS, NULL, &outif, &ip6oa); if (outif != NULL) { icmp6_ifoutstat_inc(outif, type, code); @@ -2350,10 +2334,9 @@ bad: } static const char * -icmp6_redirect_diag(src6, dst6, tgt6) - struct in6_addr *src6; - struct in6_addr *dst6; - struct in6_addr *tgt6; +icmp6_redirect_diag(struct in6_addr *src6, + struct in6_addr *dst6, + struct in6_addr *tgt6) { static char buf[1024]; snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)", @@ -2362,9 +2345,7 @@ icmp6_redirect_diag(src6, dst6, tgt6) } void -icmp6_redirect_input(m, off) - struct mbuf *m; - int off; +icmp6_redirect_input(struct mbuf *m, int off) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); @@ -2385,8 +2366,11 @@ icmp6_redirect_input(m, off) if (!m || !ifp) return; - /* XXX if we are router, we don't update route by icmp6 redirect */ - if (ip6_forwarding) + /* + * If we are an advertising router on this interface, + * don't update route by icmp6 redirect. + */ + if (ifp->if_eflags & IFEF_IPV6_ROUTER) goto freeit; if (!icmp6_rediraccept) goto freeit; @@ -2446,7 +2430,8 @@ icmp6_redirect_input(m, off) goto bad; } - gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); + gw6 = &(((struct sockaddr_in6 *)(void *) + rt->rt_gateway)->sin6_addr); if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { nd6log((LOG_ERR, "ICMP6 redirect rejected; " @@ -2551,16 +2536,8 @@ 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 @@ -2576,9 +2553,7 @@ icmp6_redirect_input(m, off) } void -icmp6_redirect_output(m0, rt) - struct mbuf *m0; - struct rtentry *rt; +icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) { struct ifnet *ifp; /* my outgoing interface */ struct in6_addr ifp_ll6; @@ -2591,7 +2566,9 @@ icmp6_redirect_output(m0, rt) u_char *p; struct ifnet *outif = NULL; struct sockaddr_in6 src_sa; - struct ip6_out_args ip6oa = { IFSCOPE_NONE, 0 }; + struct ip6_out_args ip6oa = { IFSCOPE_NONE, { 0 }, + IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR, 0, + SO_TC_UNSPEC, _NET_SERVICE_TYPE_UNSPEC }; icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0); @@ -2602,8 +2579,11 @@ icmp6_redirect_output(m0, rt) if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) goto fail; - /* if we are not router, we don't send icmp6 redirect */ - if (!ip6_forwarding || ip6_accept_rtadv || (ifp->if_eflags & IFEF_ACCEPT_RTADVD)) + /* + * If we are not a router to begin with, or not an advertising + * router on this interface, don't send icmp6 redirect. + */ + if (!ip6_forwarding || !(ifp->if_eflags & IFEF_IPV6_ROUTER)) goto fail; /* @@ -2672,7 +2652,7 @@ icmp6_redirect_output(m0, rt) /* get ip6 linklocal address for the router. */ if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { struct sockaddr_in6 *sin6; - sin6 = (struct sockaddr_in6 *)rt->rt_gateway; + sin6 = (struct sockaddr_in6 *)(void *)rt->rt_gateway; router_ll6 = &sin6->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(router_ll6)) router_ll6 = (struct in6_addr *)NULL; @@ -2747,8 +2727,8 @@ icmp6_redirect_output(m0, rt) if (!(rt_router->rt_flags & RTF_GATEWAY) && (rt_router->rt_flags & RTF_LLINFO) && (rt_router->rt_gateway->sa_family == AF_LINK) && - (sdl = (struct sockaddr_dl *)rt_router->rt_gateway) && - sdl->sdl_alen) { + (sdl = (struct sockaddr_dl *)(void *) + rt_router->rt_gateway) && sdl->sdl_alen) { nd_opt = (struct nd_opt_hdr *)p; nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = len >> 3; @@ -2758,7 +2738,7 @@ icmp6_redirect_output(m0, rt) } RT_REMREF_LOCKED(rt_router); RT_UNLOCK(rt_router); - } + } nolladdropt:; @@ -2856,13 +2836,8 @@ noredhdropt:; = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); /* send the packet to outside... */ -#if IPSEC - /* Don't lookup socket */ - if (ipsec_bypass == 0) - (void)ipsec_setsocket(m, NULL); -#endif /*IPSEC*/ - ip6oa.ip6oa_boundif = ifp->if_index; + ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; ip6_output(m, NULL, NULL, IPV6_OUTARGS, NULL, &outif, &ip6oa); if (outif) { @@ -2887,9 +2862,7 @@ fail: * ICMPv6 socket option processing. */ int -icmp6_ctloutput(so, sopt) - struct socket *so; - struct sockopt *sopt; +icmp6_ctloutput(struct socket *so, struct sockopt *sopt) { int error = 0; int optlen; @@ -2925,7 +2898,7 @@ icmp6_ctloutput(so, sopt) } if (optlen == 0) { - /* According to RFC 3542, an installed filter can be + /* According to RFC 3542, an installed filter can be * cleared by issuing a setsockopt for ICMP6_FILTER * with a zero length. */ @@ -2972,7 +2945,7 @@ icmp6_ctloutput(so, sopt) int icmp6_dgram_ctloutput(struct socket *so, struct sockopt *sopt) { - if (so->so_uid == 0) + if (kauth_cred_issuser(so->so_cred)) return icmp6_ctloutput(so, sopt); if (sopt->sopt_level == IPPROTO_ICMPV6) { @@ -2983,14 +2956,13 @@ icmp6_dgram_ctloutput(struct socket *so, struct sockopt *sopt) return EPERM; } } - + if (sopt->sopt_level != IPPROTO_IPV6) return EINVAL; - + switch (sopt->sopt_name) { case IPV6_UNICAST_HOPS: case IPV6_CHECKSUM: - case IPV6_FAITH: case IPV6_V6ONLY: case IPV6_USE_MIN_MTU: case IPV6_RECVRTHDR: @@ -3020,11 +2992,9 @@ icmp6_dgram_ctloutput(struct socket *so, struct sockopt *sopt) case IPV6_NO_IFT_CELLULAR: return ip6_ctloutput(so, sopt); - + default: return EPERM; - - } } @@ -3036,17 +3006,26 @@ icmp6_dgram_send(struct socket *so, int flags, struct mbuf *m, int error = 0; struct inpcb *inp = sotoinpcb(so); struct sockaddr_in6 tmp; - struct sockaddr_in6 *dst = (struct sockaddr_in6 *)nam; + struct sockaddr_in6 *dst = (struct sockaddr_in6 *)(void *)nam; struct icmp6_hdr *icmp6; - if (so->so_uid == 0) - return rip6_output(m, so, (struct sockaddr_in6 *) nam, control, 0); + if (inp == NULL +#if NECP + || (necp_socket_should_use_flow_divert(inp)) +#endif /* NECP */ + ) { + error = (inp == NULL ? EINVAL : EPROTOTYPE); + goto bad; + } + + if (kauth_cred_issuser(so->so_cred)) + return (rip6_output(m, so, SIN6(nam), control, 0)); /* always copy sockaddr to avoid overwrites */ if (so->so_state & SS_ISCONNECTED) { - if (nam) { - m_freem(m); - return EISCONN; + if (nam != NULL) { + error = EISCONN; + goto bad; } /* XXX */ bzero(&tmp, sizeof(tmp)); @@ -3057,32 +3036,33 @@ icmp6_dgram_send(struct socket *so, int flags, struct mbuf *m, dst = &tmp; } else { if (nam == NULL) { - m_freem(m); - return ENOTCONN; + error = ENOTCONN; + goto bad; } - tmp = *(struct sockaddr_in6 *)nam; + tmp = *(struct sockaddr_in6 *)(void *)nam; dst = &tmp; } /* * For an ICMPv6 packet, we should know its type and code */ - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + if (SOCK_PROTO(so) == 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))) { + 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 { @@ -3093,14 +3073,20 @@ icmp6_dgram_send(struct socket *so, int flags, struct mbuf *m, #if ENABLE_DEFAULT_SCOPE if (dst->sin6_scope_id == 0) { /* not change if specified */ - dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); + dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); } #endif - return rip6_output(m, so, (struct sockaddr_in6 *) nam, control, 0); + return (rip6_output(m, so, dst, control, 0)); bad: - m_freem(m); - return error; + VERIFY(error != 0); + + if (m != NULL) + m_freem(m); + if (control != NULL) + m_freem(control); + + return (error); } /* Like rip6_attach but without root privilege enforcement */ @@ -3113,10 +3099,10 @@ icmp6_dgram_attach(struct socket *so, int proto, struct proc *p) 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; @@ -3137,95 +3123,30 @@ icmp6_dgram_attach(struct socket *so, int proto, struct proc *p) } - -#ifndef HAVE_PPSRATECHECK -#ifndef timersub -#define timersub(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ - (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ - if ((vvp)->tv_usec < 0) { \ - (vvp)->tv_sec--; \ - (vvp)->tv_usec += 1000000; \ - } \ - } while (0) -#endif - -/* - * ppsratecheck(): packets (or events) per second limitation. - */ -static int -ppsratecheck(lasttime, curpps, maxpps) - struct timeval *lasttime; - int *curpps; - int maxpps; /* maximum pps allowed */ -{ - struct timeval tv, delta; - int rv; - - microtime(&tv); - - timersub(&tv, lasttime, &delta); - - /* - * Check for 0,0 so that the message will be seen at least once. - * If more than one second has passed since the last update of - * lasttime, reset the counter. - * - * we do increment *curpps even in *curpps < maxpps case, as some may - * try to use *curpps for stat purposes as well. - */ - if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || - delta.tv_sec >= 1) { - *lasttime = tv; - *curpps = 0; - rv = 1; - } else if (maxpps < 0) - rv = 1; - else if (*curpps < maxpps) - rv = 1; - else - rv = 0; - -#if 1 /* DIAGNOSTIC? */ - /* be careful about wrap-around */ - if (*curpps + 1 > 0) - *curpps = *curpps + 1; -#else - /* - * assume that there's not too many calls to this function. - * not sure if the assumption holds, as it depends on *caller's* - * behavior, not the behavior of this function. - * IMHO it is wrong to make assumption on the caller's behavior, - * so the above #if is #if 1, not #ifdef DIAGNOSTIC. - */ - *curpps = *curpps + 1; -#endif - - return (rv); -} -#endif - /* * Perform rate limit check. * Returns 0 if it is okay to send the icmp6 packet. * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate * limitation. * - * XXX per-destination/type check necessary? + * XXX per-destination check necessary? */ static int 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 */ + const int type, + __unused const int code) { int ret; ret = 0; /* okay to send */ /* PPS limit */ - if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count, + if (type == ND_ROUTER_ADVERT) { + if (!ppsratecheck(&icmp6rappslim_last, &icmp6rapps_count, + icmp6rappslim)) + ret++; + } else if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count, icmp6errppslim)) { /* The packet is subject to rate limit */ ret++;