/*
- * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/sysctl.h>
#include <sys/kauth.h>
#include <sys/priv.h>
-#include <kern/lock.h>
+#include <kern/locks.h>
#include <net/if.h>
#include <net/if_types.h>
if (inp != NULL) {
mopts = inp->in6p_moptions;
- if (inp->inp_flags & INP_NO_IFT_CELLULAR)
+ if (INP_NO_CELLULAR(inp))
ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR;
+ if (INP_NO_EXPENSIVE(inp))
+ ip6oa.ip6oa_flags |= IP6OAF_NO_EXPENSIVE;
+ if (INP_AWDL_UNRESTRICTED(inp))
+ ip6oa.ip6oa_flags |= IP6OAF_AWDL_UNRESTRICTED;
+
} else {
mopts = NULL;
}
}
IFA_LOCK_SPIN(&ia6->ia_ifa);
if ((ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY)) ||
- ((ip6oa.ip6oa_flags & IP6OAF_NO_CELLULAR) &&
- IFNET_IS_CELLULAR(ia6->ia_ifa.ifa_ifp))) {
+ (inp && inp_restricted_send(inp, ia6->ia_ifa.ifa_ifp))) {
IFA_UNLOCK(&ia6->ia_ifa);
IFA_REMREF(&ia6->ia_ifa);
*errorp = EHOSTUNREACH;
/* Rule 1: Prefer same address */
if (IN6_ARE_ADDR_EQUAL(&dst, &ia->ia_addr.sin6_addr))
- BREAK(1); /* there should be no better candidate */
+ BREAK(IP6S_SRCRULE_1); /* there should be no better candidate */
if (ia_best == NULL)
- REPLACE(0);
+ REPLACE(IP6S_SRCRULE_0);
/* Rule 2: Prefer appropriate scope */
if (dst_scope < 0)
new_scope = in6_addrscope(&ia->ia_addr.sin6_addr);
if (IN6_ARE_SCOPE_CMP(best_scope, new_scope) < 0) {
if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0)
- REPLACE(2);
- NEXTSRC(2);
+ REPLACE(IP6S_SRCRULE_2);
+ NEXTSRC(IP6S_SRCRULE_2);
} else if (IN6_ARE_SCOPE_CMP(new_scope, best_scope) < 0) {
if (IN6_ARE_SCOPE_CMP(new_scope, dst_scope) < 0)
- NEXTSRC(2);
- REPLACE(2);
+ NEXTSRC(IP6S_SRCRULE_2);
+ REPLACE(IP6S_SRCRULE_2);
}
/*
*/
if (!IFA6_IS_DEPRECATED(ia_best, secs) &&
IFA6_IS_DEPRECATED(ia, secs))
- NEXTSRC(3);
+ NEXTSRC(IP6S_SRCRULE_3);
if (IFA6_IS_DEPRECATED(ia_best, secs) &&
!IFA6_IS_DEPRECATED(ia, secs))
- REPLACE(3);
+ REPLACE(IP6S_SRCRULE_3);
/*
* RFC 4429 says that optimistic addresses are equivalent to
*/
if ((ia_best->ia6_flags & IN6_IFF_OPTIMISTIC) == 0 &&
(ia->ia6_flags & IN6_IFF_OPTIMISTIC) != 0)
- NEXTSRC(3);
+ NEXTSRC(IP6S_SRCRULE_3);
if ((ia_best->ia6_flags & IN6_IFF_OPTIMISTIC) != 0 &&
(ia->ia6_flags & IN6_IFF_OPTIMISTIC) == 0)
- REPLACE(3);
+ REPLACE(IP6S_SRCRULE_3);
/* Rule 4: Prefer home addresses */
/*
/* Rule 5: Prefer outgoing interface */
if (ia_best->ia_ifp == ifp && ia->ia_ifp != ifp)
- NEXTSRC(5);
+ NEXTSRC(IP6S_SRCRULE_5);
if (ia_best->ia_ifp != ifp && ia->ia_ifp == ifp)
- REPLACE(5);
+ REPLACE(IP6S_SRCRULE_5);
+
+ /* Rule 5.5: Prefer addresses in a prefix advertised by the next hop. */
+ if (ro != NULL && ro->ro_rt != NULL && ia_best->ia6_ndpr != NULL &&
+ ia->ia6_ndpr != NULL) {
+ struct rtentry *rta, *rtb;
+ int op;
+
+ NDPR_LOCK(ia_best->ia6_ndpr);
+ rta = ia_best->ia6_ndpr->ndpr_rt;
+ if (rta != NULL)
+ RT_ADDREF(rta);
+ NDPR_UNLOCK(ia_best->ia6_ndpr);
+
+ NDPR_LOCK(ia->ia6_ndpr);
+ rtb = ia->ia6_ndpr->ndpr_rt;
+ if (rtb != NULL)
+ RT_ADDREF(rtb);
+ NDPR_UNLOCK(ia->ia6_ndpr);
+
+ if (rta == NULL || rtb == NULL)
+ op = 0;
+ else if (rta == ro->ro_rt && rtb != ro->ro_rt)
+ op = 1;
+ else if (rta != ro->ro_rt && rtb == ro->ro_rt)
+ op = 2;
+ else
+ op = 0;
+
+ if (rta != NULL)
+ RT_REMREF(rta);
+ if (rtb != NULL)
+ RT_REMREF(rtb);
+
+ switch (op) {
+ case 1:
+ NEXTSRC(IP6S_SRCRULE_5_5);
+ break;
+ case 2:
+ REPLACE(IP6S_SRCRULE_5_5);
+ break;
+ default:
+ break;
+ }
+ }
/*
* Rule 6: Prefer matching label
new_policy = in6_addrsel_lookup_policy(&ia->ia_addr);
if (dst_policy->label == best_policy->label &&
dst_policy->label != new_policy->label)
- NEXTSRC(6);
+ NEXTSRC(IP6S_SRCRULE_6);
if (dst_policy->label != best_policy->label &&
dst_policy->label == new_policy->label)
- REPLACE(6);
+ REPLACE(IP6S_SRCRULE_6);
}
/*
- * Rule 7: Prefer public addresses.
+ * Rule 7: Prefer temporary addresses.
* We allow users to reverse the logic by configuring
- * a sysctl variable, so that privacy conscious users can
- * always prefer temporary addresses.
+ * a sysctl variable, so that transparency conscious users can
+ * always prefer stable addresses.
* Don't use temporary addresses for local destinations or
* for multicast addresses unless we were passed in an option.
*/
if (!(ia_best->ia6_flags & IN6_IFF_TEMPORARY) &&
(ia->ia6_flags & IN6_IFF_TEMPORARY)) {
if (prefer_tempaddr)
- REPLACE(7);
+ REPLACE(IP6S_SRCRULE_7);
else
- NEXTSRC(7);
+ NEXTSRC(IP6S_SRCRULE_7);
}
if ((ia_best->ia6_flags & IN6_IFF_TEMPORARY) &&
!(ia->ia6_flags & IN6_IFF_TEMPORARY)) {
if (prefer_tempaddr)
- NEXTSRC(7);
+ NEXTSRC(IP6S_SRCRULE_7);
else
- REPLACE(7);
+ REPLACE(IP6S_SRCRULE_7);
}
/*
- * Rule 8: prefer addresses on alive interfaces.
+ * Rule 7x: prefer addresses on alive interfaces.
* This is a KAME specific rule.
*/
if ((ia_best->ia_ifp->if_flags & IFF_UP) &&
!(ia->ia_ifp->if_flags & IFF_UP))
- NEXTSRC(8);
+ NEXTSRC(IP6S_SRCRULE_7x);
if (!(ia_best->ia_ifp->if_flags & IFF_UP) &&
(ia->ia_ifp->if_flags & IFF_UP))
- REPLACE(8);
+ REPLACE(IP6S_SRCRULE_7x);
/*
- * Rule 14: Use longest matching prefix.
- * Note: in the address selection draft, this rule is
- * documented as "Rule 8". However, since it is also
- * documented that this rule can be overridden, we assign
- * a large number so that it is easy to assign smaller numbers
- * to more preferred rules.
+ * Rule 8: Use longest matching prefix.
*/
new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, &dst);
if (best_matchlen < new_matchlen)
- REPLACE(14);
+ REPLACE(IP6S_SRCRULE_8);
if (new_matchlen < best_matchlen)
- NEXTSRC(14);
-
- /* Rule 15 is reserved. */
+ NEXTSRC(IP6S_SRCRULE_8);
/*
* Last resort: just keep the current candidate.
lck_rw_done(&in6_ifaddr_rwlock);
- if (ia_best != NULL &&
- (ip6oa.ip6oa_flags & IP6OAF_NO_CELLULAR) &&
- IFNET_IS_CELLULAR(ia_best->ia_ifa.ifa_ifp)) {
+ if (ia_best != NULL && inp &&
+ inp_restricted_send(inp, ia_best->ia_ifa.ifa_ifp)) {
IFA_REMREF(&ia_best->ia_ifa);
ia_best = NULL;
*errorp = EHOSTUNREACH;
}
done:
- if (error == 0) {
- if (ip6oa != NULL &&
- (ip6oa->ip6oa_flags & IP6OAF_NO_CELLULAR) &&
- ((ifp != NULL && IFNET_IS_CELLULAR(ifp)) ||
- (route != NULL && route->ro_rt != NULL &&
- IFNET_IS_CELLULAR(route->ro_rt->rt_ifp)))) {
- if (route != NULL && route->ro_rt != NULL) {
- ROUTE_RELEASE(route);
- route = NULL;
- }
- ifp = NULL; /* ditch ifp; keep ifp0 */
- error = EHOSTUNREACH;
- ip6oa->ip6oa_retflags |= IP6OARF_IFDENIED;
+ /*
+ * Check for interface restrictions.
+ */
+#define CHECK_RESTRICTIONS(_ip6oa, _ifp) \
+ ((((_ip6oa)->ip6oa_flags & IP6OAF_NO_CELLULAR) && \
+ IFNET_IS_CELLULAR(_ifp)) || \
+ (((_ip6oa)->ip6oa_flags & IP6OAF_NO_EXPENSIVE) && \
+ IFNET_IS_EXPENSIVE(_ifp)) || \
+ (!((_ip6oa)->ip6oa_flags & IP6OAF_AWDL_UNRESTRICTED) && \
+ IFNET_IS_AWDL_RESTRICTED(_ifp)))
+
+ if (error == 0 && ip6oa != NULL &&
+ ((ifp && CHECK_RESTRICTIONS(ip6oa, ifp)) ||
+ (route && route->ro_rt &&
+ CHECK_RESTRICTIONS(ip6oa, route->ro_rt->rt_ifp)))) {
+ if (route != NULL && route->ro_rt != NULL) {
+ ROUTE_RELEASE(route);
+ route = NULL;
}
+ ifp = NULL; /* ditch ifp; keep ifp0 */
+ error = EHOSTUNREACH;
+ ip6oa->ip6oa_retflags |= IP6OARF_IFDENIED;
}
+#undef CHECK_RESTRICTIONS
/*
* If the interface is disabled for IPv6, then ENETDOWN error.
lck_rw_lock_exclusive(pcbinfo->ipi_lock);
socket_lock(inp->inp_socket, 0);
}
+
+ /*
+ * Check if a local port was assigned to the inp while
+ * this thread was waiting for the pcbinfo lock
+ */
+ if (inp->inp_lport != 0) {
+ VERIFY(inp->inp_flags2 & INP2_INHASHLIST);
+ lck_rw_done(pcbinfo->ipi_lock);
+
+ /*
+ * It is not an error if another thread allocated
+ * a port
+ */
+ return (0);
+ }
}
/* XXX: this is redundant when called from in6_pcbbind */
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
wild = INPLOOKUP_WILDCARD;
- inp->inp_flags |= INP_ANONPORT;
-
if (inp->inp_flags & INP_HIGHPORT) {
first = ipport_hifirstauto; /* sysctl */
last = ipport_hilastauto;
}
inp->inp_lport = lport;
+ inp->inp_flags |= INP_ANONPORT;
+
if (in_pcbinshash(inp, 1) != 0) {
inp->in6p_laddr = in6addr_any;
- inp->inp_lport = 0;
inp->in6p_last_outifp = NULL;
+
+ inp->inp_lport = 0;
+ inp->inp_flags &= ~INP_ANONPORT;
if (!locked)
lck_rw_done(pcbinfo->ipi_lock);
return (EAGAIN);
addrsel_policy_init(void)
{
/*
- * Default address selection policy based on RFC 3484 and
- * draft-arifumi-6man-rfc3484-revise-03.
+ * Default address selection policy based on RFC 6724.
*/
static const struct in6_addrpolicy defaddrsel[] = {
- /* localhost */
+ /* Loopback -- prefix=::1/128, precedence=50, label=0 */
{
.addr = {
.sin6_family = AF_INET6,
.sin6_addr = IN6MASK128,
.sin6_len = sizeof (struct sockaddr_in6)
},
- .preced = 60,
+ .preced = 50,
.label = 0
},
- /* ULA */
+ /* Unspecified -- prefix=::/0, precedence=40, label=1 */
{
.addr = {
.sin6_family = AF_INET6,
- .sin6_addr = {{{ 0xfc }}},
+ .sin6_addr = IN6ADDR_ANY_INIT,
.sin6_len = sizeof (struct sockaddr_in6)
},
.addrmask = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6MASK7,
+ .sin6_addr = IN6MASK0,
.sin6_len = sizeof (struct sockaddr_in6)
},
- .preced = 50,
+ .preced = 40,
.label = 1
},
- /* any IPv6 src */
+ /* IPv4 Mapped -- prefix=::ffff:0:0/96, precedence=35, label=4 */
{
.addr = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ANY_INIT,
+ .sin6_addr = IN6ADDR_V4MAPPED_INIT,
.sin6_len = sizeof (struct sockaddr_in6)
},
.addrmask = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6MASK0,
+ .sin6_addr = IN6MASK96,
.sin6_len = sizeof (struct sockaddr_in6)
},
- .preced = 40,
- .label = 2 },
+ .preced = 35,
+ .label = 4
+ },
- /* any IPv4 src */
+ /* 6to4 -- prefix=2002::/16, precedence=30, label=2 */
{
.addr = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_V4MAPPED_INIT,
+ .sin6_addr = {{{ 0x20, 0x02 }}},
.sin6_len = sizeof (struct sockaddr_in6)
},
.addrmask = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6MASK96,
+ .sin6_addr = IN6MASK16,
.sin6_len = sizeof (struct sockaddr_in6)
},
.preced = 30,
- .label = 3
+ .label = 2
},
- /* 6to4 */
+ /* Teredo -- prefix=2001::/32, precedence=5, label=5 */
{
.addr = {
.sin6_family = AF_INET6,
- .sin6_addr = {{{ 0x20, 0x02 }}},
+ .sin6_addr = {{{ 0x20, 0x01 }}},
.sin6_len = sizeof (struct sockaddr_in6)
},
.addrmask = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6MASK16,
+ .sin6_addr = IN6MASK32,
.sin6_len = sizeof (struct sockaddr_in6)
},
- .preced = 20,
- .label = 4
+ .preced = 5,
+ .label = 5
},
- /* Teredo */
+ /* Unique Local (ULA) -- prefix=fc00::/7, precedence=3, label=13 */
{
.addr = {
.sin6_family = AF_INET6,
- .sin6_addr = {{{ 0x20, 0x01 }}},
+ .sin6_addr = {{{ 0xfc }}},
.sin6_len = sizeof (struct sockaddr_in6)
},
.addrmask = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6MASK32,
+ .sin6_addr = IN6MASK7,
.sin6_len = sizeof (struct sockaddr_in6)
},
- .preced = 10,
- .label = 5
+ .preced = 3,
+ .label = 13
},
- /* v4 compat addresses */
+ /* IPv4 Compatible -- prefix=::/96, precedence=1, label=3 */
{
.addr = {
.sin6_family = AF_INET6,
.sin6_len = sizeof (struct sockaddr_in6)
},
.preced = 1,
- .label = 10
+ .label = 3
},
- /* site-local (deprecated) */
+ /* Site-local (deprecated) -- prefix=fec0::/10, precedence=1, label=11 */
{
.addr = {
.sin6_family = AF_INET6,
.label = 11
},
- /* 6bone (deprecated) */
+ /* 6bone (deprecated) -- prefix=3ffe::/16, precedence=1, label=12 */
{
.addr = {
.sin6_family = AF_INET6,
scopeid = scope6_addr2default(in6);
#endif
- if (IN6_IS_SCOPE_LINKLOCAL(in6)) {
+ if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
struct in6_pktinfo *pi;
struct ifnet *im6o_multicast_ifp = NULL;
*/
sin6->sin6_scope_id = 0;
- if (IN6_IS_SCOPE_LINKLOCAL(in6)) {
+ if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
/*
* KAME assumption: link id == interface id
*/