/*
- * Copyright (c) 2003-2018 Apple Inc. All rights reserved.
+ * Copyright (c) 2003-2020 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
const struct in6_addr in6mask128 = IN6MASK128;
const struct sockaddr_in6 sa6_any = {
- sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0
+ .sin6_len = sizeof(sa6_any),
+ .sin6_family = AF_INET6,
+ .sin6_port = 0,
+ .sin6_flowinfo = 0,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ .sin6_scope_id = 0
};
static int in6ctl_associd(struct socket *, u_long, caddr_t);
static void in6_if_up_dad_start(struct ifnet *);
+#define IA6_HASH_INIT(ia) { \
+ (ia)->ia6_hash.tqe_next = (void *)(uintptr_t)-1; \
+ (ia)->ia6_hash.tqe_prev = (void *)(uintptr_t)-1; \
+}
+
+#define IA6_IS_HASHED(ia) \
+ (!((ia)->ia6_hash.tqe_next == (void *)(uintptr_t)-1 || \
+ (ia)->ia6_hash.tqe_prev == (void *)(uintptr_t)-1))
+
+static void in6_iahash_remove(struct in6_ifaddr *);
+static void in6_iahash_insert(struct in6_ifaddr *);
+static void in6_iahash_insert_ptp(struct in6_ifaddr *);
+
extern lck_mtx_t *nd6_mutex;
#define IN6IFA_TRACE_HIST_SIZE 32 /* size of trace history */
* XXX: we should avoid such a configuration in IPv6...
*/
lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- for (ia = in6_ifaddrs; ia; ia = ia->ia_next) {
+ TAILQ_FOREACH(ia, IN6ADDR_HASH(IFA_IN6(ifa)), ia6_hash) {
IFA_LOCK(&ia->ia_ifa);
if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) {
ia_count++;
/* Remove link local addresses from interface */
lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- ia = in6_ifaddrs;
- while (ia != NULL) {
- if (ia->ia_ifa.ifa_ifp != ifp) {
- ia = ia->ia_next;
- continue;
- }
- IFA_LOCK(&ia->ia_ifa);
- if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) {
- IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */
+ boolean_t from_begining = TRUE;
+ while (from_begining) {
+ from_begining = FALSE;
+ TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) {
+ if (ia->ia_ifa.ifa_ifp != ifp) {
+ continue;
+ }
+ IFA_LOCK(&ia->ia_ifa);
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) {
+ IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */
+ IFA_UNLOCK(&ia->ia_ifa);
+ lck_rw_done(&in6_ifaddr_rwlock);
+ in6_purgeaddr(&ia->ia_ifa);
+ IFA_REMREF(&ia->ia_ifa); /* for us */
+ lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+ /*
+ * Purging the address caused in6_ifaddr_rwlock
+ * to be dropped and reacquired;
+ * therefore search again from the beginning
+ * of in6_ifaddrs list.
+ */
+ from_begining = TRUE;
+ break;
+ }
IFA_UNLOCK(&ia->ia_ifa);
- lck_rw_done(&in6_ifaddr_rwlock);
- in6_purgeaddr(&ia->ia_ifa);
- IFA_REMREF(&ia->ia_ifa); /* for us */
- lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- /*
- * Purging the address caused in6_ifaddr_rwlock
- * to be dropped and reacquired;
- * therefore search again from the beginning
- * of in6_ifaddrs list.
- */
- ia = in6_ifaddrs;
- continue;
}
- IFA_UNLOCK(&ia->ia_ifa);
- ia = ia->ia_next;
}
lck_rw_done(&in6_ifaddr_rwlock);
pr0.ndpr_plen = 64;
pr0.ndpr_ifp = ifp;
pr0.ndpr_prefix.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_ULL;
- in6_setscope(&pr0.ndpr_prefix.sin6_addr, ifp, NULL);
+ (void)in6_setscope(&pr0.ndpr_prefix.sin6_addr, ifp, NULL);
pr = nd6_prefix_lookup(&pr0, ND6_PREFIX_EXPIRY_UNSPEC);
if (pr) {
lck_mtx_lock(nd6_mutex);
lt.ia6t_preferred = ia6_lt.ia6t_preferred;
lt.ia6t_vltime = ia6_lt.ia6t_vltime;
lt.ia6t_pltime = ia6_lt.ia6t_pltime;
- bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof(lt));
+ bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof(ifr->ifr_ifru.ifru_lifetime));
} else {
struct in6_addrlifetime_32 lt;
lt.ia6t_preferred = (uint32_t)ia6_lt.ia6t_preferred;
lt.ia6t_vltime = (uint32_t)ia6_lt.ia6t_vltime;
lt.ia6t_pltime = (uint32_t)ia6_lt.ia6t_pltime;
- bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof(lt));
+ bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof(ifr->ifr_ifru.ifru_lifetime));
}
IFA_UNLOCK(&ia->ia_ifa);
break;
if (pr != NULL) {
if ((ia6 = in6_pfx_newpersistaddr(pr, FALSE, &error, TRUE)) == NULL) {
- nd6log0((LOG_ERR, "Could not configure CLAT46 address on interface "
- "%s.\n", ifp->if_xname));
+ nd6log0(error, "Could not configure CLAT46 address on interface "
+ "%s.\n", ifp->if_xname);
} else {
IFA_LOCK(&ia6->ia_ifa);
NDPR_LOCK(pr);
/* Remove autoconfigured address from interface */
lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- ia = in6_ifaddrs;
- while (ia != NULL) {
- if (ia->ia_ifa.ifa_ifp != ifp) {
- ia = ia->ia_next;
- continue;
- }
- IFA_LOCK(&ia->ia_ifa);
- if (ia->ia6_flags & IN6_IFF_AUTOCONF) {
- IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */
+ boolean_t from_begining = TRUE;
+ while (from_begining) {
+ from_begining = FALSE;
+ TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) {
+ if (ia->ia_ifa.ifa_ifp != ifp) {
+ continue;
+ }
+ IFA_LOCK(&ia->ia_ifa);
+ if (ia->ia6_flags & IN6_IFF_AUTOCONF) {
+ IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */
+ IFA_UNLOCK(&ia->ia_ifa);
+ lck_rw_done(&in6_ifaddr_rwlock);
+ in6_purgeaddr(&ia->ia_ifa);
+ IFA_REMREF(&ia->ia_ifa); /* for us */
+ lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+ /*
+ * Purging the address caused in6_ifaddr_rwlock
+ * to be dropped and reacquired;
+ * therefore search again from the beginning
+ * of in6_ifaddrs list.
+ */
+ from_begining = TRUE;
+ break;
+ }
IFA_UNLOCK(&ia->ia_ifa);
- lck_rw_done(&in6_ifaddr_rwlock);
- in6_purgeaddr(&ia->ia_ifa);
- IFA_REMREF(&ia->ia_ifa); /* for us */
- lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- /*
- * Purging the address caused in6_ifaddr_rwlock
- * to be dropped and reacquired;
- * therefore search again from the beginning
- * of in6_ifaddrs list.
- */
- ia = in6_ifaddrs;
- continue;
}
- IFA_UNLOCK(&ia->ia_ifa);
- ia = ia->ia_next;
}
lck_rw_done(&in6_ifaddr_rwlock);
}
ifa = &ia->ia_ifa;
in6m_sol = NULL;
- nd6log2((LOG_DEBUG, "%s - %s ifp %s ia6_flags 0x%x ifaupflags 0x%x\n",
+ nd6log2(debug, "%s - %s ifp %s ia6_flags 0x%x ifaupflags 0x%x\n",
__func__,
ip6_sprintf(&ia->ia_addr.sin6_addr),
if_name(ia->ia_ifp),
ia->ia6_flags,
- ifaupflags));
+ ifaupflags);
/*
* Just to be safe, always clear certain flags when address
}
imm = in6_joingroup(ifp, &llsol, &error, delay);
if (imm == NULL) {
- nd6log((LOG_WARNING,
+ nd6log(info,
"%s: addmulti failed for %s on %s (errno=%d)\n",
__func__, ip6_sprintf(&llsol), if_name(ifp),
- error));
+ error);
VERIFY(error != 0);
goto unwind;
}
imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0);
if (!imm) {
- nd6log((LOG_WARNING,
+ nd6log(info,
"%s: addmulti failed for %s on %s (errno=%d)\n",
__func__, ip6_sprintf(&mltaddr.sin6_addr),
- if_name(ifp), error));
+ if_name(ifp), error);
VERIFY(error != 0);
goto unwind;
}
*/
delay = random() % MAX_RTR_SOLICITATION_DELAY;
}
- if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr)
- == 0) {
+ lck_mtx_lock(&hostname_lock);
+ int n = in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr);
+ lck_mtx_unlock(&hostname_lock);
+ if (n == 0) {
imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error,
delay); /* XXX jinmei */
if (!imm) {
- nd6log((LOG_WARNING,
+ nd6log(info,
"%s: addmulti failed for %s on %s "
"(errno=%d)\n",
__func__, ip6_sprintf(&mltaddr.sin6_addr),
- if_name(ifp), error));
+ if_name(ifp), error);
/* XXX not very fatal, go on... */
error = 0;
} else {
imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0);
if (!imm) {
- nd6log((LOG_WARNING,
+ nd6log(info,
"%s: addmulti failed for %s on %s (errno=%d)\n",
__func__, ip6_sprintf(&mltaddr.sin6_addr),
- if_name(ifp), error));
+ if_name(ifp), error);
VERIFY(error != 0);
goto unwind;
}
++nd6_sched_timeout_want;
/*
- * Perform DAD, if needed.
- * XXX It may be of use, if we can administratively
- * disable DAD.
+ * Perform DAD, if:
+ * * Interface is marked to perform DAD, AND
+ * * Address is not marked to skip DAD, AND
+ * * Address is in a pre-DAD state (Tentative or Optimistic)
*/
IFA_LOCK_SPIN(ifa);
- if (in6if_do_dad(ifp) && ((ifa->ifa_flags & IN6_IFF_NODAD) == 0) &&
- (ia->ia6_flags & IN6_IFF_DADPROGRESS)) {
+ if (in6if_do_dad(ifp) && (ia->ia6_flags & IN6_IFF_NODAD) == 0 &&
+ (ia->ia6_flags & IN6_IFF_DADPROGRESS) != 0) {
int mindelay, maxdelay;
int *delayptr, delayval;
* Please enjoy the dancing sea turtle.
*/
IFA_ADDREF(ifa); /* for this and optionally for caller */
+ IA6_HASH_INIT(ia);
ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
if (ifra->ifra_dstaddr.sin6_family == AF_INET6 ||
(ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) {
ifnet_lock_done(ifp);
lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- if (in6_ifaddrs != NULL) {
- struct in6_ifaddr *iac;
- for (iac = in6_ifaddrs; iac->ia_next != NULL;
- iac = iac->ia_next) {
- continue;
- }
- iac->ia_next = ia;
- } else {
- in6_ifaddrs = ia;
- }
+ TAILQ_INSERT_TAIL(&in6_ifaddrhead, ia, ia6_link);
IFA_ADDREF(ifa); /* hold for in6_ifaddrs link */
lck_rw_done(&in6_ifaddr_rwlock);
} else {
static void
in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
{
- struct in6_ifaddr *oia;
+ struct in6_ifaddr *nia;
struct ifaddr *ifa;
int unlinked;
IFA_UNLOCK(ifa);
ifnet_lock_done(ifp);
- unlinked = 1;
+ unlinked = 0;
lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- oia = ia;
- if (oia == (ia = in6_ifaddrs)) {
- in6_ifaddrs = ia->ia_next;
- } else {
- while (ia->ia_next && (ia->ia_next != oia)) {
- ia = ia->ia_next;
- }
- if (ia->ia_next) {
- ia->ia_next = oia->ia_next;
- } else {
- /* search failed */
- log(LOG_NOTICE, "%s: search failed.\n", __func__);
- unlinked = 0;
+ TAILQ_FOREACH(nia, &in6_ifaddrhead, ia6_link) {
+ if (ia == nia) {
+ TAILQ_REMOVE(&in6_ifaddrhead, ia, ia6_link);
+ IFA_LOCK(ifa);
+ if (IA6_IS_HASHED(ia)) {
+ in6_iahash_remove(ia);
+ }
+ IFA_UNLOCK(ifa);
+ unlinked = 1;
+ break;
}
}
* of other (detached) addresses, call
* pfxlist_onlink_check().
*/
- ifa = &oia->ia_ifa;
IFA_LOCK(ifa);
/*
* Only log the below message for addresses other than
*
* For now quiece down the log message for LLAs.
*/
- if (!IN6_IS_ADDR_LINKLOCAL(&oia->ia_addr.sin6_addr)) {
- if (oia->ia6_ndpr == NULL) {
+ if (!IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) {
+ if (ia->ia6_ndpr == NULL) {
log(LOG_NOTICE, "in6_unlink_ifa: IPv6 address "
"0x%llx has no prefix\n",
- (uint64_t)VM_KERNEL_ADDRPERM(oia));
+ (uint64_t)VM_KERNEL_ADDRPERM(ia));
} else {
- struct nd_prefix *pr = oia->ia6_ndpr;
- oia->ia6_flags &= ~IN6_IFF_AUTOCONF;
- oia->ia6_ndpr = NULL;
+ struct nd_prefix *pr = ia->ia6_ndpr;
+ ia->ia6_flags &= ~IN6_IFF_AUTOCONF;
+ ia->ia6_ndpr = NULL;
NDPR_LOCK(pr);
VERIFY(pr->ndpr_addrcnt != 0);
pr->ndpr_addrcnt--;
- if (oia->ia6_flags & IN6_IFF_CLAT46) {
+ if (ia->ia6_flags & IN6_IFF_CLAT46) {
pr->ndpr_stateflags &= ~NDPRF_CLAT46;
}
NDPR_UNLOCK(pr);
IFA_UNLOCK(ifa);
lck_rw_done(&in6_ifaddr_rwlock);
- if ((oia->ia6_flags & IN6_IFF_AUTOCONF) != 0) {
+ if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0) {
lck_mtx_lock(nd6_mutex);
pfxlist_onlink_check();
lck_mtx_unlock(nd6_mutex);
LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED);
lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- ia = in6_ifaddrs;
- while (ia != NULL) {
- if (ia->ia_ifa.ifa_ifp != ifp) {
- ia = ia->ia_next;
- continue;
+ boolean_t from_begining = TRUE;
+ while (from_begining) {
+ from_begining = FALSE;
+ TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) {
+ if (ia->ia_ifa.ifa_ifp != ifp) {
+ continue;
+ }
+ IFA_ADDREF(&ia->ia_ifa); /* for us */
+ lck_rw_done(&in6_ifaddr_rwlock);
+ in6_purgeaddr(&ia->ia_ifa);
+ IFA_REMREF(&ia->ia_ifa); /* for us */
+ lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+ /*
+ * Purging the address would have caused
+ * in6_ifaddr_rwlock to be dropped and reacquired;
+ * therefore search again from the beginning
+ * of in6_ifaddrs list.
+ */
+ from_begining = TRUE;
+ break;
}
- IFA_ADDREF(&ia->ia_ifa); /* for us */
- lck_rw_done(&in6_ifaddr_rwlock);
- in6_purgeaddr(&ia->ia_ifa);
- IFA_REMREF(&ia->ia_ifa); /* for us */
- lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
- /*
- * Purging the address would have caused
- * in6_ifaddr_rwlock to be dropped and reacquired;
- * therefore search again from the beginning
- * of in6_ifaddrs list.
- */
- ia = in6_ifaddrs;
}
lck_rw_done(&in6_ifaddr_rwlock);
error = 0;
ifa = &ia->ia_ifa;
+ lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+ IFA_LOCK(&ia->ia_ifa);
+ if (IA6_IS_HASHED(ia)) {
+ in6_iahash_remove(ia);
+ }
+ if ((ifp->if_flags & IFF_POINTOPOINT)) {
+ in6_iahash_insert_ptp(ia);
+ } else {
+ in6_iahash_insert(ia);
+ }
+ IFA_UNLOCK(&ia->ia_ifa);
+ lck_rw_done(&in6_ifaddr_rwlock);
+
/*
* NOTE: SIOCSIFADDR is defined with struct ifreq as parameter,
* but here we are sending it down to the interface with a pointer
error = ifnet_ioctl(ifp, PF_INET6, SIOCSIFADDR, ia);
if (error != 0) {
if (error != EOPNOTSUPP) {
- return error;
+ goto failed;
}
error = 0;
}
IFA_UNLOCK(ifa);
error = rtinit(ifa, RTM_ADD, RTF_UP | RTF_HOST);
if (error != 0) {
- return error;
+ goto failed;
}
IFA_LOCK(ifa);
ia->ia_flags |= IFA_ROUTE;
VERIFY(error == 0);
return 0;
+failed:
+ VERIFY(error != 0);
+ lck_rw_lock_exclusive(&in6_ifaddr_rwlock);
+ IFA_LOCK(&ia->ia_ifa);
+ if (IA6_IS_HASHED(ia)) {
+ in6_iahash_remove(ia);
+ }
+ IFA_UNLOCK(&ia->ia_ifa);
+ lck_rw_done(&in6_ifaddr_rwlock);
+
+ return error;
}
void
struct in6_ifaddr *ia;
lck_rw_lock_shared(&in6_ifaddr_rwlock);
- for (ia = in6_ifaddrs; ia; ia = ia->ia_next) {
+ TAILQ_FOREACH(ia, IN6ADDR_HASH(addr), ia6_hash) {
IFA_LOCK(&ia->ia_ifa);
if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(&ia->ia_ifa))) {
IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for caller */
}
lck_rw_lock_shared(&in6_ifaddr_rwlock);
- for (ia = in6_ifaddrs; ia; ia = ia->ia_next) {
+ TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) {
IFA_LOCK_SPIN(&ia->ia_ifa);
if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr,
&ia->ia_prefixmask.sin6_addr)) {
return 0;
}
- if (ifp->if_subfamily == IFNET_SUBFAMILY_IPSEC ||
- ifp->if_subfamily == IFNET_SUBFAMILY_UTUN) {
+ if (ifp->if_family == IFNET_FAMILY_IPSEC ||
+ ifp->if_family == IFNET_FAMILY_UTUN) {
/*
* Ignore DAD for tunneling virtual interfaces, which get
* their IPv6 address explicitly assigned.
return 64; /* Packet Data over Cellular */
case IFT_BRIDGE:
return 64; /* Transparent bridge interface */
+ case IFT_6LOWPAN:
+ return 64; /* 6LoWPAN */
default:
/*
* Unknown link type:
bzero(in6ifa, in6ifa_size);
in6ifa->ia_ifa.ifa_free = in6_ifaddr_free;
in6ifa->ia_ifa.ifa_debug |= IFD_ALLOC;
+ in6ifa->ia_ifa.ifa_del_wc = &in6ifa->ia_ifa.ifa_debug;
+ in6ifa->ia_ifa.ifa_del_waiters = 0;
ifa_lock_init(&in6ifa->ia_ifa);
if (in6ifa_debug != 0) {
struct in6_ifaddr_dbg *in6ifa_dbg =
bzero(&ev_msg, sizeof(ev_msg));
bzero(&nd6_event, sizeof(nd6_event));
- nd6log0((LOG_INFO, "%s Event %s received for %s\n",
+ nd6log0(info, "%s Event %s received for %s\n",
__func__, in6_event2kev_array[in6_ev_code].in6_event_str,
- ip6_sprintf(p_addr6)));
+ ip6_sprintf(p_addr6));
ev_msg.vendor_code = KEV_VENDOR_APPLE;
ev_msg.kev_class = KEV_NETWORK_CLASS;
nwk_wq_enqueue((struct nwk_wq_entry*)p_in6_ev);
}
+
+/*
+ * Caller must hold in6_ifaddr_rwlock as writer.
+ */
+static void
+in6_iahash_remove(struct in6_ifaddr *ia)
+{
+ LCK_RW_ASSERT(&in6_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE);
+ IFA_LOCK_ASSERT_HELD(&ia->ia_ifa);
+
+ if (!IA6_IS_HASHED(ia)) {
+ panic("%s: attempt to remove wrong ia %p from ipv6 hash table\n", __func__, ia);
+ /* NOTREACHED */
+ }
+ TAILQ_REMOVE(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia, ia6_hash);
+ IA6_HASH_INIT(ia);
+ if (IFA_REMREF_LOCKED(&ia->ia_ifa) == NULL) {
+ panic("%s: unexpected (missing) refcnt ifa=%p", __func__,
+ &ia->ia_ifa);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * Caller must hold in6_ifaddr_rwlock as writer.
+ */
+static void
+in6_iahash_insert(struct in6_ifaddr *ia)
+{
+ LCK_RW_ASSERT(&in6_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE);
+ IFA_LOCK_ASSERT_HELD(&ia->ia_ifa);
+
+ if (ia->ia_addr.sin6_family != AF_INET6) {
+ panic("%s: attempt to insert wrong ia %p into hash table\n", __func__, ia);
+ /* NOTREACHED */
+ } else if (IA6_IS_HASHED(ia)) {
+ panic("%s: attempt to double-insert ia %p into hash table\n", __func__, ia);
+ /* NOTREACHED */
+ }
+ TAILQ_INSERT_HEAD(IN6ADDR_HASH(&ia->ia_addr.sin6_addr),
+ ia, ia6_hash);
+ IFA_ADDREF_LOCKED(&ia->ia_ifa);
+}
+
+/*
+ * Some point to point interfaces that are tunnels borrow the address from
+ * an underlying interface (e.g. VPN server). In order for source address
+ * selection logic to find the underlying interface first, we add the address
+ * of borrowing point to point interfaces at the end of the list.
+ * (see rdar://6733789)
+ *
+ * Caller must hold in6_ifaddr_rwlock as writer.
+ */
+static void
+in6_iahash_insert_ptp(struct in6_ifaddr *ia)
+{
+ struct in6_ifaddr *tmp_ifa;
+ struct ifnet *tmp_ifp;
+
+ LCK_RW_ASSERT(&in6_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE);
+ IFA_LOCK_ASSERT_HELD(&ia->ia_ifa);
+
+ if (ia->ia_addr.sin6_family != AF_INET6) {
+ panic("%s: attempt to insert wrong ia %p into hash table\n", __func__, ia);
+ /* NOTREACHED */
+ } else if (IA6_IS_HASHED(ia)) {
+ panic("%s: attempt to double-insert ia %p into hash table\n", __func__, ia);
+ /* NOTREACHED */
+ }
+ IFA_UNLOCK(&ia->ia_ifa);
+ TAILQ_FOREACH(tmp_ifa, IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia6_hash) {
+ IFA_LOCK(&tmp_ifa->ia_ifa);
+ /* ia->ia_addr won't change, so check without lock */
+ if (IN6_ARE_ADDR_EQUAL(&tmp_ifa->ia_addr.sin6_addr, &ia->ia_addr.sin6_addr)) {
+ IFA_UNLOCK(&tmp_ifa->ia_ifa);
+ break;
+ }
+ IFA_UNLOCK(&tmp_ifa->ia_ifa);
+ }
+ tmp_ifp = (tmp_ifa == NULL) ? NULL : tmp_ifa->ia_ifp;
+
+ IFA_LOCK(&ia->ia_ifa);
+ if (tmp_ifp == NULL) {
+ TAILQ_INSERT_HEAD(IN6ADDR_HASH(&ia->ia_addr.sin6_addr),
+ ia, ia6_hash);
+ } else {
+ TAILQ_INSERT_TAIL(IN6ADDR_HASH(&ia->ia_addr.sin6_addr),
+ ia, ia6_hash);
+ }
+ IFA_ADDREF_LOCKED(&ia->ia_ifa);
+}