+
+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 = NULL;
+ const uint16_t temp_embedded_id = sin6->sin6_addr.s6_addr16[1];
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) &&
+ (temp_embedded_id == 0)) {
+ sin6->sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ }
+
+ 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);
+
+ /* Restore the address that was passed to us */
+ if (temp_embedded_id == 0) {
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ }
+
+ if (rt != NULL) {
+ RT_LOCK(rt);
+ VERIFY(rt->rt_flags & RTF_LLINFO);
+ VERIFY(rt->rt_llinfo);
+
+ ln = rt->rt_llinfo;
+ ND6_CACHE_STATE_TRANSITION(ln, ND6_LLINFO_REACHABLE);
+ ln_setexpire(ln, 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));
+ } else {
+ nd6log((LOG_DEBUG, "%s: host route to %s [lr=0x%llx]\n",
+ __func__, ip6_sprintf(&sin6->sin6_addr),
+ (uint64_t)VM_KERNEL_ADDRPERM(lr)));
+ }
+}
+
+void
+nd6_alt_node_absent(struct ifnet *ifp, struct sockaddr_in6 *sin6)
+{
+ struct rtentry *rt;
+ const uint16_t temp_embedded_id = sin6->sin6_addr.s6_addr16[1];
+
+ nd6log((LOG_DEBUG, "%s: host route to %s\n", __func__,
+ ip6_sprintf(&sin6->sin6_addr)));
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) &&
+ (temp_embedded_id == 0)) {
+ sin6->sin6_addr.s6_addr16[1] = htons(ifp->if_index);
+ }
+
+ 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);
+
+ /* Restore the address that was passed to us */
+ if (temp_embedded_id == 0) {
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ }
+
+ if (rt != NULL) {
+ RT_LOCK(rt);
+
+ if (!(rt->rt_flags & (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);
+}