+ nd6_dad_duplicated(ifa, FALSE);
+}
+
+static void
+dad_addref(struct dadq *dp, int locked)
+{
+ if (!locked)
+ DAD_LOCK_SPIN(dp);
+ else
+ DAD_LOCK_ASSERT_HELD(dp);
+
+ if (++dp->dad_refcount == 0) {
+ panic("%s: dad %p wraparound refcnt\n", __func__, dp);
+ /* NOTREACHED */
+ }
+ if (!locked)
+ DAD_UNLOCK(dp);
+}
+
+static void
+dad_remref(struct dadq *dp)
+{
+ struct ifaddr *ifa;
+
+ DAD_LOCK_SPIN(dp);
+ if (dp->dad_refcount == 0)
+ panic("%s: dad %p negative refcnt\n", __func__, dp);
+ --dp->dad_refcount;
+ if (dp->dad_refcount > 0) {
+ DAD_UNLOCK(dp);
+ return;
+ }
+ DAD_UNLOCK(dp);
+
+ if (dp->dad_attached ||
+ dp->dad_list.tqe_next != NULL || dp->dad_list.tqe_prev != NULL) {
+ panic("%s: attached dad=%p is being freed", __func__, dp);
+ /* NOTREACHED */
+ }
+
+ if ((ifa = dp->dad_ifa) != NULL) {
+ IFA_REMREF(ifa); /* drop dad_ifa reference */
+ dp->dad_ifa = NULL;
+ }
+
+ lck_mtx_destroy(&dp->dad_lock, ifa_mtx_grp);
+ zfree(dad_zone, dp);
+}
+
+void
+nd6_llreach_set_reachable(struct ifnet *ifp, void *addr, unsigned int alen)
+{
+ /* Nothing more to do if it's disabled */
+ if (nd6_llreach_base == 0)
+ return;
+
+ ifnet_llreach_set_reachable(ifp, ETHERTYPE_IPV6, addr, alen);
+}
+
+void
+nd6_alt_node_addr_decompose(struct ifnet *ifp, struct sockaddr *sa,
+ struct sockaddr_dl* sdl, struct sockaddr_in6 *sin6)
+{
+ static const size_t EUI64_LENGTH = 8;
+
+ VERIFY(nd6_need_cache(ifp));
+ VERIFY(sa);
+ VERIFY(sdl && (void *)sa != (void *)sdl);
+ VERIFY(sin6 && (void *)sa != (void *)sin6);
+
+ bzero(sin6, sizeof *sin6);
+ sin6->sin6_len = sizeof *sin6;
+ sin6->sin6_family = AF_INET6;
+
+ bzero(sdl, sizeof *sdl);
+ sdl->sdl_len = sizeof *sdl;
+ sdl->sdl_family = AF_LINK;
+ sdl->sdl_type = ifp->if_type;
+ sdl->sdl_index = ifp->if_index;
+
+ switch (sa->sa_family) {
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6a = (struct sockaddr_in6 *)(void *)sa;
+ struct in6_addr *in6 = &sin6a->sin6_addr;
+
+ VERIFY(sa->sa_len == sizeof *sin6);
+
+ sdl->sdl_nlen = strlen(ifp->if_name);
+ bcopy(ifp->if_name, sdl->sdl_data, sdl->sdl_nlen);
+ if (in6->s6_addr[11] == 0xff && in6->s6_addr[12] == 0xfe) {
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ LLADDR(sdl)[0] = (in6->s6_addr[8] ^ ND6_EUI64_UBIT);
+ LLADDR(sdl)[1] = in6->s6_addr[9];
+ LLADDR(sdl)[2] = in6->s6_addr[10];
+ LLADDR(sdl)[3] = in6->s6_addr[13];
+ LLADDR(sdl)[4] = in6->s6_addr[14];
+ LLADDR(sdl)[5] = in6->s6_addr[15];
+ }
+ else {
+ sdl->sdl_alen = EUI64_LENGTH;
+ bcopy(&in6->s6_addr[8], LLADDR(sdl), EUI64_LENGTH);
+ }
+
+ sdl->sdl_slen = 0;
+ break;
+ }
+ case AF_LINK: {
+ struct sockaddr_dl *sdla = (struct sockaddr_dl *)(void *)sa;
+ struct in6_addr *in6 = &sin6->sin6_addr;
+ caddr_t lla = LLADDR(sdla);
+
+ VERIFY(sa->sa_len <= sizeof *sdl);
+ bcopy(sa, sdl, sa->sa_len);
+
+ sin6->sin6_scope_id = sdla->sdl_index;
+ if (sin6->sin6_scope_id == 0)
+ sin6->sin6_scope_id = ifp->if_index;
+ in6->s6_addr[0] = 0xfe;
+ in6->s6_addr[1] = 0x80;
+ if (sdla->sdl_alen == EUI64_LENGTH)
+ bcopy(lla, &in6->s6_addr[8], EUI64_LENGTH);
+ else {
+ VERIFY(sdla->sdl_alen == ETHER_ADDR_LEN);
+
+ in6->s6_addr[8] = ((uint8_t) lla[0] ^ ND6_EUI64_UBIT);
+ in6->s6_addr[9] = (uint8_t) lla[1];
+ in6->s6_addr[10] = (uint8_t) lla[2];
+ in6->s6_addr[11] = 0xff;
+ in6->s6_addr[12] = 0xfe;
+ in6->s6_addr[13] = (uint8_t) lla[3];
+ in6->s6_addr[14] = (uint8_t) lla[4];
+ in6->s6_addr[15] = (uint8_t) lla[5];
+ }
+
+ break;
+ }
+ default:
+ VERIFY(false);
+ break;
+ }
+}
+
+void
+nd6_alt_node_present(struct ifnet *ifp, struct sockaddr_in6 *sin6,
+ struct sockaddr_dl *sdl, int32_t rssi, int lqm, int npm)
+{
+ struct rtentry *rt;
+ struct llinfo_nd6 *ln;
+ struct if_llreach *lr;
+
+ nd6_cache_lladdr(ifp, &sin6->sin6_addr, LLADDR(sdl),
+ sdl->sdl_alen, ND_NEIGHBOR_ADVERT, 0);
+
+ lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_NOTOWNED);
+ lck_mtx_lock(rnh_lock);
+
+ rt = rtalloc1_scoped_locked((struct sockaddr *)sin6, 1, 0,
+ ifp->if_index);
+ if (rt != NULL) {
+ RT_LOCK(rt);
+ VERIFY(rt->rt_flags & RTF_LLINFO);
+ VERIFY(rt->rt_llinfo);
+
+ ln = rt->rt_llinfo;
+ ln->ln_state = ND6_LLINFO_REACHABLE;
+ ln->ln_expire = 0;
+
+ lr = ln->ln_llreach;
+ if (lr) {
+ IFLR_LOCK(lr);
+ lr->lr_rssi = rssi;
+ lr->lr_lqm = (int32_t) lqm;
+ lr->lr_npm = (int32_t) npm;
+ IFLR_UNLOCK(lr);
+ }
+
+ RT_UNLOCK(rt);
+ RT_REMREF(rt);
+ }
+
+ lck_mtx_unlock(rnh_lock);
+
+ if (rt == NULL) {
+ log(LOG_ERR, "%s: failed to add/update host route to %s.\n",
+ __func__, ip6_sprintf(&sin6->sin6_addr));
+ }
+}
+
+void
+nd6_alt_node_absent(struct ifnet *ifp, struct sockaddr_in6 *sin6)
+{
+ struct rtentry *rt;
+
+ lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_NOTOWNED);
+ lck_mtx_lock(rnh_lock);
+
+ rt = rtalloc1_scoped_locked((struct sockaddr *)sin6, 0, 0,
+ ifp->if_index);
+ if (rt != NULL) {
+ RT_LOCK(rt);
+
+ if (!(rt->rt_flags & (RTF_PINNED|RTF_CLONING|RTF_PRCLONING)) &&
+ (rt->rt_flags & (RTF_HOST|RTF_LLINFO|RTF_WASCLONED)) ==
+ (RTF_HOST|RTF_LLINFO|RTF_WASCLONED)) {
+ rt->rt_flags |= RTF_CONDEMNED;
+ RT_UNLOCK(rt);
+
+ (void) rtrequest_locked(RTM_DELETE, rt_key(rt),
+ (struct sockaddr *)NULL, rt_mask(rt), 0,
+ (struct rtentry **)NULL);
+
+ rtfree_locked(rt);
+ }
+ else {
+ RT_REMREF_LOCKED(rt);
+ RT_UNLOCK(rt);
+ }
+ }
+
+ lck_mtx_unlock(rnh_lock);