/*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 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
* 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,
* 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@
*/
#if IPSEC
#include <netinet6/ipsec.h>
#include <netkey/key.h>
-
-extern int ipsec_bypass;
#endif
#include <net/net_osdep.h>
+#if NECP
+#include <net/necp.h>
+#endif
+
extern struct ip6protosw *ip6_protox[];
extern uint32_t rip_sendspace;
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;
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);
}
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:
* 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;
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) {
* 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 */
#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++;
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
switch (code) {
case ICMP6_DST_UNREACH_NOROUTE:
+ case ICMP6_DST_UNREACH_ADDR: /* PRC_HOSTDEAD is a DOS */
code = PRC_UNREACH_NET;
break;
case ICMP6_DST_UNREACH_ADMIN:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
break;
- case ICMP6_DST_UNREACH_ADDR:
- code = PRC_HOSTDEAD;
- break;
case ICMP6_DST_UNREACH_BEYONDSCOPE:
/* I mean "source address was incorrect." */
code = PRC_PARAMPROB;
goto badcode;
}
goto deliver;
- break;
case ICMP6_PACKET_TOO_BIG:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
* intermediate extension headers.
*/
goto deliver;
- break;
case ICMP6_TIME_EXCEEDED:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
goto badcode;
}
goto deliver;
- break;
case ICMP6_PARAM_PROB:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
goto badcode;
}
goto deliver;
- break;
case ICMP6_ECHO_REQUEST:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
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)) {
/* Give up remote */
m_freem(n0);
goto rate_limit_checked;
- break;
}
MGETHDR(n, M_DONTWAIT, n0->m_type); /* MAC-OK */
if (n && maxlen >= MHLEN) {
/* Give up remote */
m_freem(n0);
goto rate_limit_checked;
- break;
}
M_COPY_PKTHDR(n, n0);
/*
icmp6_reflect(n, noff);
}
goto rate_limit_checked;
- break;
case ICMP6_ECHO_REPLY:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
m_freem(n);
/* m stays. */
goto rate_limit_checked;
- break;
case MLD_LISTENER_DONE:
icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
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;
icmp6_reflect(n, noff);
}
goto rate_limit_checked;
- break;
case ICMP6_WRUREPLY:
if (code != 0)
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);
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);
if (icmp6len < sizeof(struct nd_neighbor_solicit))
goto badlen;
- 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;
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);
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);
icmp6_redirect_input(n, off);
/* m stays. */
goto rate_limit_checked;
- break;
case ICMP6_ROUTER_RENUMBERING:
if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
} else {
/* ICMPv6 informational: MUST not deliver */
goto rate_limit_checked;
- break;
}
deliver:
if (icmp6_notify_error(m, off, icmp6len, code)) {
}
rate_limit_checked:
- /* deliver the packet to appropriate sockets (unless proxying) */
- if (!proxy) {
- icmp6_rip6_input(&m, *offp);
- return IPPROTO_DONE;
- }
+ icmp6_rip6_input(&m, *offp);
+ return IPPROTO_DONE;
freeit:
m_freem(m);
}
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;
/* Detect the upper level protocol */
{
- void (*ctlfunc)(int, struct sockaddr *, void *);
+ void (*ctlfunc)(int, struct sockaddr *, void *, struct ifnet *);
u_int8_t nxt = eip6->ip6_nxt;
int eoff = off + sizeof(struct icmp6_hdr) +
sizeof(struct ip6_hdr);
icmp6_mtudisc_update(&ip6cp, 1); /*XXX*/
}
- ctlfunc = (void (*)(int, struct sockaddr *, void *))
- (ip6_protox[nxt]->pr_ctlinput);
+ ctlfunc = ip6_protox[nxt]->pr_ctlinput;
if (ctlfunc) {
+ LCK_MTX_ASSERT(inet6_domain_mutex, LCK_MTX_ASSERT_OWNED);
+
+ lck_mtx_unlock(inet6_domain_mutex);
+
(void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst,
- &ip6cp);
+ &ip6cp, m->m_pkthdr.rcvif);
+
+ lck_mtx_lock(inet6_domain_mutex);
}
}
return(0);
}
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;
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) {
*/
#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;
!(icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK)) {
nd6log((LOG_DEBUG, "ni6_input: ignore node info to "
"a temporary address in %s:%d",
- __FILE__, __LINE__));
+ __func__, __LINE__));
goto 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;
* 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;
* 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;
}
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;
* 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 *);
rip6src.sin6_family = AF_INET6;
rip6src.sin6_len = sizeof(struct sockaddr_in6);
rip6src.sin6_addr = ip6->ip6_src;
- if (sa6_recoverscope(&rip6src, TRUE))
+ if (sa6_recoverscope(&rip6src, TRUE))
return (IPPROTO_DONE);
lck_rw_lock_shared(ripcbinfo.ipi_lock);
in6p->in6p_icmp6filt))
continue;
- if (inp_restricted(in6p, ifp))
- continue;
-
- if (ifp != NULL && IFNET_IS_CELLULAR(ifp) &&
- (in6p->in6p_flags & INP_NO_IFT_CELLULAR))
+ if (inp_restricted_recv(in6p, ifp))
continue;
if (last) {
* 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;
int type, code;
struct ifnet *outif = NULL;
struct sockaddr_in6 sa6_src, sa6_dst;
- struct nd_ifinfo *ndi;
+ struct nd_ifinfo *ndi = NULL;
u_int32_t oflow;
struct ip6_out_args ip6oa = { IFSCOPE_NONE, { 0 },
- IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR, 0 };
+ IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR |
+ IP6OAF_INTCOPROC_ALLOWED | IP6OAF_AWDL_UNRESTRICTED, 0,
+ SO_TC_UNSPEC, _NET_SERVICE_TYPE_UNSPEC };
if (!(m->m_pkthdr.pkt_flags & PKTF_LOOP) && m->m_pkthdr.rcvif != NULL) {
ip6oa.ip6oa_boundif = m->m_pkthdr.rcvif->if_index;
nd6log((LOG_DEBUG,
"sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
(u_int32_t)off, (u_int32_t)sizeof(struct ip6_hdr),
- __FILE__, __LINE__));
+ __func__, __LINE__));
goto bad;
}
ip6->ip6_flow |= (oflow & htonl(0x0ff00000));
}
ip6->ip6_nxt = IPPROTO_ICMPV6;
- lck_rw_lock_shared(nd_if_rwlock);
if (outif != NULL && (ndi = ND_IFINFO(outif)) != NULL &&
ndi->initialized) {
lck_mtx_lock(&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.rcvif = NULL;
+
m->m_pkthdr.csum_data = 0;
m->m_pkthdr.csum_flags = 0;
ip6_output(m, NULL, NULL, IPV6_OUTARGS, NULL, &outif, &ip6oa);
}
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)",
}
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 *);
}
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;
struct ifnet *outif = NULL;
struct sockaddr_in6 src_sa;
struct ip6_out_args ip6oa = { IFSCOPE_NONE, { 0 },
- IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR, 0 };
+ IP6OAF_SELECT_SRCIF | IP6OAF_BOUND_SRCADDR, 0,
+ SO_TC_UNSPEC, _NET_SERVICE_TYPE_UNSPEC };
icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0);
* and truncates if not.
*/
if (m0->m_next || m0->m_pkthdr.len != m0->m_len)
- panic("assumption failed in %s:%d\n", __FILE__, __LINE__);
+ panic("assumption failed in %s:%d\n", __func__, __LINE__);
if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
/* not enough room, truncate */
= 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;
* 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;
}
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.
*/
struct sockaddr_in6 *dst = (struct sockaddr_in6 *)(void *)nam;
struct icmp6_hdr *icmp6;
- if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) {
+ if (inp == NULL
+#if NECP
+ || (necp_socket_should_use_flow_divert(inp))
+#endif /* NECP */
+ ) {
error = (inp == NULL ? EINVAL : EPROTOTYPE);
goto bad;
}
* 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++;