+
+/*
+ * 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);
+}