+
+/*
+ * This is the ND pre-output routine; care must be taken to ensure that
+ * the "hint" route never gets freed via rtfree(), since the caller may
+ * have stored it inside a struct route with a reference held for that
+ * placeholder.
+ */
+errno_t
+nd6_lookup_ipv6(ifnet_t ifp, const struct sockaddr_in6 *ip6_dest,
+ struct sockaddr_dl *ll_dest, size_t ll_dest_len, route_t hint,
+ mbuf_t packet)
+{
+ route_t route = hint;
+ errno_t result = 0;
+ struct sockaddr_dl *sdl = NULL;
+ size_t copy_len;
+
+ if (ip6_dest->sin6_family != AF_INET6)
+ return (EAFNOSUPPORT);
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+ return (ENETDOWN);
+
+ if (hint != NULL) {
+ /*
+ * Callee holds a reference on the route and returns
+ * with the route entry locked, upon success.
+ */
+ result = arp_route_to_gateway_route(
+ (const struct sockaddr*)ip6_dest, hint, &route);
+ if (result != 0)
+ return (result);
+ if (route != NULL)
+ RT_LOCK_ASSERT_HELD(route);
+ }
+
+ if ((packet->m_flags & M_MCAST) != 0) {
+ if (route != NULL)
+ RT_UNLOCK(route);
+ result = dlil_resolve_multi(ifp,
+ (const struct sockaddr*)ip6_dest,
+ (struct sockaddr *)ll_dest, ll_dest_len);
+ if (route != NULL)
+ RT_LOCK(route);
+ goto release;
+ }
+
+ if (route == NULL) {
+ /*
+ * This could happen, if we could not allocate memory or
+ * if arp_route_to_gateway_route() didn't return a route.
+ */
+ result = ENOBUFS;
+ goto release;
+ }
+
+ if (route->rt_gateway->sa_family != AF_LINK) {
+ printf("nd6_lookup_ipv6: gateway address not AF_LINK\n");
+ result = EADDRNOTAVAIL;
+ goto release;
+ }
+
+ sdl = SDL(route->rt_gateway);
+ if (sdl->sdl_alen == 0) {
+ /* this should be impossible, but we bark here for debugging */
+ printf("nd6_lookup_ipv6: sdl_alen == 0\n");
+ result = EHOSTUNREACH;
+ goto release;
+ }
+
+ copy_len = sdl->sdl_len <= ll_dest_len ? sdl->sdl_len : ll_dest_len;
+ bcopy(sdl, ll_dest, copy_len);
+
+release:
+ if (route != NULL) {
+ if (route == hint) {
+ RT_REMREF_LOCKED(route);
+ RT_UNLOCK(route);
+ } else {
+ RT_UNLOCK(route);
+ rtfree(route);
+ }
+ }
+ return (result);
+}
+
+SYSCTL_DECL(_net_inet6_icmp6);
+
+static int
+nd6_sysctl_drlist SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error = 0;
+ char buf[1024];
+ struct nd_defrouter *dr;
+ int p64 = proc_is64bit(req->p);
+
+ if (req->newptr)
+ return (EPERM);
+
+ lck_mtx_lock(nd6_mutex);
+ if (p64) {
+ struct in6_defrouter_64 *d, *de;
+
+ for (dr = TAILQ_FIRST(&nd_defrouter);
+ dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ d = (struct in6_defrouter_64 *)buf;
+ de = (struct in6_defrouter_64 *)(buf + sizeof (buf));
+
+ if (d + 1 <= de) {
+ bzero(d, sizeof (*d));
+ d->rtaddr.sin6_family = AF_INET6;
+ d->rtaddr.sin6_len = sizeof (d->rtaddr);
+ if (in6_recoverscope(&d->rtaddr, &dr->rtaddr,
+ dr->ifp) != 0)
+ log(LOG_ERR,
+ "scope error in "
+ "default router list (%s)\n",
+ ip6_sprintf(&dr->rtaddr));
+ d->flags = dr->flags;
+ d->rtlifetime = dr->rtlifetime;
+ d->expire = dr->expire;
+ d->if_index = dr->ifp->if_index;
+ } else {
+ panic("buffer too short");
+ }
+ error = SYSCTL_OUT(req, buf, sizeof (*d));
+ if (error)
+ break;
+ }
+ } else {
+ struct in6_defrouter_32 *d_32, *de_32;
+
+ for (dr = TAILQ_FIRST(&nd_defrouter);
+ dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ d_32 = (struct in6_defrouter_32 *)buf;
+ de_32 = (struct in6_defrouter_32 *)(buf + sizeof (buf));
+
+ if (d_32 + 1 <= de_32) {
+ bzero(d_32, sizeof (*d_32));
+ d_32->rtaddr.sin6_family = AF_INET6;
+ d_32->rtaddr.sin6_len = sizeof (d_32->rtaddr);
+ if (in6_recoverscope(&d_32->rtaddr, &dr->rtaddr,
+ dr->ifp) != 0)
+ log(LOG_ERR,
+ "scope error in "
+ "default router list (%s)\n",
+ ip6_sprintf(&dr->rtaddr));
+ d_32->flags = dr->flags;
+ d_32->rtlifetime = dr->rtlifetime;
+ d_32->expire = dr->expire;
+ d_32->if_index = dr->ifp->if_index;
+ } else {
+ panic("buffer too short");
+ }
+ error = SYSCTL_OUT(req, buf, sizeof (*d_32));
+ if (error)
+ break;
+ }
+ }
+ lck_mtx_unlock(nd6_mutex);
+ return (error);
+}
+
+static int
+nd6_sysctl_prlist SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error = 0;
+ char buf[1024];
+ struct nd_prefix *pr;
+ int p64 = proc_is64bit(req->p);
+
+ if (req->newptr)
+ return (EPERM);
+
+ lck_mtx_lock(nd6_mutex);
+ if (p64) {
+ struct in6_prefix_64 *p, *pe;
+
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ u_short advrtrs = 0;
+ size_t advance;
+ struct sockaddr_in6 *sin6, *s6;
+ struct nd_pfxrouter *pfr;
+
+ p = (struct in6_prefix_64 *)buf;
+ pe = (struct in6_prefix_64 *)(buf + sizeof (buf));
+
+ if (p + 1 <= pe) {
+ bzero(p, sizeof (*p));
+ sin6 = (struct sockaddr_in6 *)(p + 1);
+
+ p->prefix = pr->ndpr_prefix;
+ if (in6_recoverscope(&p->prefix,
+ &p->prefix.sin6_addr, pr->ndpr_ifp) != 0)
+ log(LOG_ERR,
+ "scope error in prefix list (%s)\n",
+ ip6_sprintf(&p->prefix.sin6_addr));
+ p->raflags = pr->ndpr_raf;
+ p->prefixlen = pr->ndpr_plen;
+ p->vltime = pr->ndpr_vltime;
+ p->pltime = pr->ndpr_pltime;
+ p->if_index = pr->ndpr_ifp->if_index;
+ p->expire = pr->ndpr_expire;
+ p->refcnt = pr->ndpr_refcnt;
+ p->flags = pr->ndpr_stateflags;
+ p->origin = PR_ORIG_RA;
+ advrtrs = 0;
+ for (pfr = pr->ndpr_advrtrs.lh_first;
+ pfr;
+ pfr = pfr->pfr_next) {
+ if ((void *)&sin6[advrtrs + 1] >
+ (void *)pe) {
+ advrtrs++;
+ continue;
+ }
+ s6 = &sin6[advrtrs];
+ bzero(s6, sizeof (*s6));
+ s6->sin6_family = AF_INET6;
+ s6->sin6_len = sizeof (*sin6);
+ if (in6_recoverscope(s6,
+ &pfr->router->rtaddr,
+ pfr->router->ifp) != 0)
+ log(LOG_ERR, "scope error in "
+ "prefix list (%s)\n",
+ ip6_sprintf(&pfr->router->
+ rtaddr));
+ advrtrs++;
+ }
+ p->advrtrs = advrtrs;
+ } else {
+ panic("buffer too short");
+ }
+ advance = sizeof (*p) + sizeof (*sin6) * advrtrs;
+ error = SYSCTL_OUT(req, buf, advance);
+ if (error)
+ break;
+ }
+ } else {
+ struct in6_prefix_32 *p_32, *pe_32;
+
+ for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
+ u_short advrtrs = 0;
+ size_t advance;
+ struct sockaddr_in6 *sin6, *s6;
+ struct nd_pfxrouter *pfr;
+
+ p_32 = (struct in6_prefix_32 *)buf;
+ pe_32 = (struct in6_prefix_32 *)(buf + sizeof (buf));
+
+ if (p_32 + 1 <= pe_32) {
+ bzero(p_32, sizeof (*p_32));
+ sin6 = (struct sockaddr_in6 *)(p_32 + 1);
+
+ p_32->prefix = pr->ndpr_prefix;
+ if (in6_recoverscope(&p_32->prefix,
+ &p_32->prefix.sin6_addr, pr->ndpr_ifp) != 0)
+ log(LOG_ERR, "scope error in prefix "
+ "list (%s)\n", ip6_sprintf(&p_32->
+ prefix.sin6_addr));
+ p_32->raflags = pr->ndpr_raf;
+ p_32->prefixlen = pr->ndpr_plen;
+ p_32->vltime = pr->ndpr_vltime;
+ p_32->pltime = pr->ndpr_pltime;
+ p_32->if_index = pr->ndpr_ifp->if_index;
+ p_32->expire = pr->ndpr_expire;
+ p_32->refcnt = pr->ndpr_refcnt;
+ p_32->flags = pr->ndpr_stateflags;
+ p_32->origin = PR_ORIG_RA;
+ advrtrs = 0;
+ for (pfr = pr->ndpr_advrtrs.lh_first;
+ pfr;
+ pfr = pfr->pfr_next) {
+ if ((void *)&sin6[advrtrs + 1] >
+ (void *)pe_32) {
+ advrtrs++;
+ continue;
+ }
+ s6 = &sin6[advrtrs];
+ bzero(s6, sizeof (*s6));
+ s6->sin6_family = AF_INET6;
+ s6->sin6_len = sizeof (*sin6);
+ if (in6_recoverscope(s6,
+ &pfr->router->rtaddr,
+ pfr->router->ifp) != 0)
+ log(LOG_ERR, "scope error in "
+ "prefix list (%s)\n",
+ ip6_sprintf(&pfr->router->
+ rtaddr));
+ advrtrs++;
+ }
+ p_32->advrtrs = advrtrs;
+ } else {
+ panic("buffer too short");
+ }
+ advance = sizeof (*p_32) + sizeof (*sin6) * advrtrs;
+ error = SYSCTL_OUT(req, buf, advance);
+ if (error)
+ break;
+ }
+ }
+ lck_mtx_unlock(nd6_mutex);
+ return (error);
+}
+SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist,
+ CTLFLAG_RD, 0, 0, nd6_sysctl_drlist, "S,in6_defrouter","");
+SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist,
+ CTLFLAG_RD, 0, 0, nd6_sysctl_prlist, "S,in6_defrouter","");
+