+ if (dep[0] != NULL) {
+ IFA_REMREF(&dep[0]->ia_ifa);
+ }
+ return besta;
+ }
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
+ IFA_LOCK(ifa);
+ if (ifa->ifa_addr->sa_family != AF_INET6) {
+ IFA_UNLOCK(ifa);
+ continue;
+ }
+ if (ifa2ia6(ifa)->ia6_flags & IN6_IFF_ANYCAST) {
+ IFA_UNLOCK(ifa);
+ continue; /* XXX: is there any case to allow anycast? */
+ }
+ if (ifa2ia6(ifa)->ia6_flags & (IN6_IFF_NOTREADY | IN6_IFF_CLAT46)) {
+ IFA_UNLOCK(ifa);
+ continue; /* don't use this interface */
+ }
+ if (ifa2ia6(ifa)->ia6_flags & IN6_IFF_DETACHED) {
+ IFA_UNLOCK(ifa);
+ continue;
+ }
+ if (ifa2ia6(ifa)->ia6_flags & IN6_IFF_DEPRECATED) {
+ if (ip6_use_deprecated) {
+ IFA_ADDREF_LOCKED(ifa); /* for dep[1] */
+ IFA_UNLOCK(ifa);
+ if (dep[1] != NULL) {
+ IFA_REMREF(&dep[1]->ia_ifa);
+ }
+ dep[1] = (struct in6_ifaddr *)ifa;
+ } else {
+ IFA_UNLOCK(ifa);
+ }
+ continue;
+ }
+ IFA_ADDREF_LOCKED(ifa); /* for caller */
+ IFA_UNLOCK(ifa);
+ ifnet_lock_done(ifp);
+ if (dep[0] != NULL) {
+ IFA_REMREF(&dep[0]->ia_ifa);
+ }
+ if (dep[1] != NULL) {
+ IFA_REMREF(&dep[1]->ia_ifa);
+ }
+ return (struct in6_ifaddr *)ifa;
+ }
+ ifnet_lock_done(ifp);
+
+ /* use the last-resort values, that are, deprecated addresses */
+ if (dep[0]) {
+ if (dep[1] != NULL) {
+ IFA_REMREF(&dep[1]->ia_ifa);
+ }
+ return dep[0];
+ }
+ if (dep[1]) {
+ return dep[1];
+ }
+
+ return NULL;
+}
+
+/*
+ * perform DAD when interface becomes IFF_UP.
+ */
+static void
+in6_if_up_dad_start(struct ifnet *ifp)
+{
+ struct ifaddr *ifa;
+ struct nd_ifinfo *ndi = NULL;
+
+ ndi = ND_IFINFO(ifp);
+ VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
+ if (!(ndi->flags & ND6_IFF_DAD)) {
+ return;
+ }
+
+ /* start DAD on all the interface addresses */
+ ifnet_lock_exclusive(ifp);
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
+ struct in6_ifaddr *ia6;
+
+ IFA_LOCK_SPIN(ifa);
+ if (ifa->ifa_addr->sa_family != AF_INET6) {
+ IFA_UNLOCK(ifa);
+ continue;
+ }
+ ia6 = (struct in6_ifaddr *)ifa;
+ if (ia6->ia6_flags & IN6_IFF_DADPROGRESS) {
+ int delay = 0; /* delay ticks before DAD output */
+ IFA_UNLOCK(ifa);
+ nd6_dad_start(ifa, &delay);
+ } else {
+ IFA_UNLOCK(ifa);
+ }
+ }
+ ifnet_lock_done(ifp);
+}
+
+int
+in6if_do_dad(
+ struct ifnet *ifp)
+{
+ struct nd_ifinfo *ndi = NULL;
+
+ if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
+ return 0;
+ }
+
+ ndi = ND_IFINFO(ifp);
+ VERIFY((NULL != ndi) && (TRUE == ndi->initialized));
+ if (!(ndi->flags & ND6_IFF_DAD)) {
+ return 0;
+ }
+
+ /*
+ * If we are using the alternative neighbor discovery
+ * interface on this interface, then skip DAD.
+ *
+ * Also, skip it for interfaces marked "local private"
+ * for now, even when not marked as using the alternative
+ * interface. This is for historical reasons.
+ */
+ if (ifp->if_eflags &
+ (IFEF_IPV6_ND6ALT | IFEF_LOCALNET_PRIVATE | IFEF_DIRECTLINK)) {
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ switch (ifp->if_type) {
+#if IFT_DUMMY
+ case IFT_DUMMY:
+#endif
+ case IFT_FAITH:
+ /*
+ * These interfaces do not have the IFF_LOOPBACK flag,
+ * but loop packets back. We do not have to do DAD on such
+ * interfaces. We should even omit it, because loop-backed
+ * NS would confuse the DAD procedure.
+ */
+ return 0;
+ default:
+ /*
+ * Our DAD routine requires the interface up and running.
+ * However, some interfaces can be up before the RUNNING
+ * status. Additionaly, users may try to assign addresses
+ * before the interface becomes up (or running).
+ * We simply skip DAD in such a case as a work around.
+ * XXX: we should rather mark "tentative" on such addresses,
+ * and do DAD after the interface becomes ready.
+ */
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) !=
+ (IFF_UP | IFF_RUNNING)) {
+ return 0;
+ }
+
+ return 1;
+ }
+}
+
+/*
+ * Calculate max IPv6 MTU through all the interfaces and store it
+ * to in6_maxmtu.
+ */
+void
+in6_setmaxmtu(void)
+{
+ u_int32_t maxmtu = 0;
+ struct ifnet *ifp;
+
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_list) {
+ struct nd_ifinfo *ndi = NULL;
+
+ if ((ndi = ND_IFINFO(ifp)) != NULL && !ndi->initialized) {
+ ndi = NULL;
+ }
+ if (ndi != NULL) {
+ lck_mtx_lock(&ndi->lock);
+ }
+ if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
+ IN6_LINKMTU(ifp) > maxmtu) {
+ maxmtu = IN6_LINKMTU(ifp);
+ }
+ if (ndi != NULL) {
+ lck_mtx_unlock(&ndi->lock);
+ }
+ }
+ ifnet_head_done();
+ if (maxmtu) { /* update only when maxmtu is positive */
+ in6_maxmtu = maxmtu;
+ }
+}
+/*
+ * Provide the length of interface identifiers to be used for the link attached
+ * to the given interface. The length should be defined in "IPv6 over
+ * xxx-link" document. Note that address architecture might also define
+ * the length for a particular set of address prefixes, regardless of the
+ * link type. Also see RFC 4862 for additional background.
+ */
+int
+in6_if2idlen(struct ifnet *ifp)
+{
+ switch (ifp->if_type) {
+ case IFT_ETHER: /* RFC2464 */
+ case IFT_IEEE8023ADLAG: /* IEEE802.3ad Link Aggregate */
+#ifdef IFT_PROPVIRTUAL
+ case IFT_PROPVIRTUAL: /* XXX: no RFC. treat it as ether */
+#endif
+#ifdef IFT_L2VLAN
+ case IFT_L2VLAN: /* ditto */
+#endif
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211: /* ditto */
+#endif
+#ifdef IFT_MIP
+ case IFT_MIP: /* ditto */
+#endif
+ return 64;
+ case IFT_FDDI: /* RFC2467 */
+ return 64;
+ case IFT_ISO88025: /* RFC2470 (IPv6 over Token Ring) */
+ return 64;
+ case IFT_PPP: /* RFC2472 */
+ return 64;
+ case IFT_ARCNET: /* RFC2497 */
+ return 64;
+ case IFT_FRELAY: /* RFC2590 */
+ return 64;
+ case IFT_IEEE1394: /* RFC3146 */
+ return 64;
+ case IFT_GIF:
+ return 64; /* draft-ietf-v6ops-mech-v2-07 */
+ case IFT_LOOP:
+ return 64; /* XXX: is this really correct? */
+ case IFT_OTHER:
+ return 64; /* for utun interfaces */
+ case IFT_CELLULAR:
+ return 64; /* Packet Data over Cellular */
+ case IFT_BRIDGE:
+ return 64; /* Transparent bridge interface */
+ case IFT_6LOWPAN:
+ return 64; /* 6LoWPAN */
+ default:
+ /*
+ * Unknown link type:
+ * It might be controversial to use the today's common constant
+ * of 64 for these cases unconditionally. For full compliance,
+ * we should return an error in this case. On the other hand,
+ * if we simply miss the standard for the link type or a new
+ * standard is defined for a new link type, the IFID length
+ * is very likely to be the common constant. As a compromise,
+ * we always use the constant, but make an explicit notice
+ * indicating the "unknown" case.
+ */
+ log(LOG_NOTICE, "%s: unknown link type (%d)\n", __func__,
+ ifp->if_type);
+ return 64;
+ }
+}
+/*
+ * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be
+ * v4 mapped addr or v4 compat addr
+ */
+void
+in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+ bzero(sin, sizeof(*sin));
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = sin6->sin6_port;
+ sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
+}
+
+/* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */
+void
+in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+ bzero(sin6, sizeof(*sin6));
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = sin->sin_port;
+ sin6->sin6_addr.s6_addr32[0] = 0;
+ sin6->sin6_addr.s6_addr32[1] = 0;
+ if (sin->sin_addr.s_addr) {
+ sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP;
+ sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
+ } else {
+ sin6->sin6_addr.s6_addr32[2] = 0;
+ sin6->sin6_addr.s6_addr32[3] = 0;
+ }
+}
+
+/* Convert sockaddr_in6 into sockaddr_in. */
+void
+in6_sin6_2_sin_in_sock(struct sockaddr *nam)
+{
+ struct sockaddr_in *sin_p;
+ struct sockaddr_in6 sin6;
+
+ /*
+ * Save original sockaddr_in6 addr and convert it
+ * to sockaddr_in.
+ */
+ sin6 = *(struct sockaddr_in6 *)(void *)nam;
+ sin_p = (struct sockaddr_in *)(void *)nam;
+ in6_sin6_2_sin(sin_p, &sin6);
+}
+
+/* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */
+int
+in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam)
+{
+ struct sockaddr_in *sin_p;
+ struct sockaddr_in6 *sin6_p;
+
+ MALLOC(sin6_p, struct sockaddr_in6 *, sizeof(*sin6_p), M_SONAME,
+ M_WAITOK);
+ if (sin6_p == NULL) {
+ return ENOBUFS;
+ }
+ sin_p = (struct sockaddr_in *)(void *)*nam;
+ in6_sin_2_v4mapsin6(sin_p, sin6_p);
+ FREE(*nam, M_SONAME);
+ *nam = (struct sockaddr *)sin6_p;
+
+ return 0;
+}
+
+/*
+ * Posts in6_event_data message kernel events.
+ *
+ * To get the same size of kev_in6_data between ILP32 and LP64 data models
+ * we are using a special version of the in6_addrlifetime structure that
+ * uses only 32 bits fields to be compatible with Leopard, and that
+ * are large enough to span 68 years.
+ */
+void
+in6_post_msg(struct ifnet *ifp, u_int32_t event_code, struct in6_ifaddr *ifa,
+ uint8_t *mac)
+{
+ struct kev_msg ev_msg;
+ struct kev_in6_data in6_event_data;
+ struct in6_addrlifetime ia6_lt;
+
+ bzero(&in6_event_data, sizeof(struct kev_in6_data));
+ bzero(&ev_msg, sizeof(struct kev_msg));
+ ev_msg.vendor_code = KEV_VENDOR_APPLE;
+ ev_msg.kev_class = KEV_NETWORK_CLASS;
+ ev_msg.kev_subclass = KEV_INET6_SUBCLASS;
+ ev_msg.event_code = event_code;
+
+ if (ifa) {
+ IFA_LOCK(&ifa->ia_ifa);
+ in6_event_data.ia_addr = ifa->ia_addr;
+ in6_event_data.ia_net = ifa->ia_net;
+ in6_event_data.ia_dstaddr = ifa->ia_dstaddr;
+ in6_event_data.ia_prefixmask = ifa->ia_prefixmask;
+ in6_event_data.ia_plen = ifa->ia_plen;
+ in6_event_data.ia6_flags = (u_int32_t)ifa->ia6_flags;
+
+ /* retrieve time as calendar time (last arg is 1) */
+ in6ifa_getlifetime(ifa, &ia6_lt, 1);
+ in6_event_data.ia_lifetime.ia6t_expire = (u_int32_t)ia6_lt.ia6t_expire;
+ in6_event_data.ia_lifetime.ia6t_preferred = (u_int32_t)ia6_lt.ia6t_preferred;
+ in6_event_data.ia_lifetime.ia6t_vltime = ia6_lt.ia6t_vltime;
+ in6_event_data.ia_lifetime.ia6t_pltime = ia6_lt.ia6t_pltime;
+ IFA_UNLOCK(&ifa->ia_ifa);
+ }
+
+ if (ifp != NULL) {
+ (void) strlcpy(&in6_event_data.link_data.if_name[0],
+ ifp->if_name, IFNAMSIZ);
+ in6_event_data.link_data.if_family = ifp->if_family;
+ in6_event_data.link_data.if_unit = (u_int32_t)ifp->if_unit;
+ }
+
+ if (mac != NULL) {
+ memcpy(&in6_event_data.ia_mac, mac,
+ sizeof(in6_event_data.ia_mac));
+ }
+
+ ev_msg.dv[0].data_ptr = &in6_event_data;
+ ev_msg.dv[0].data_length = sizeof(in6_event_data);
+ ev_msg.dv[1].data_length = 0;
+
+ dlil_post_complete_msg(NULL, &ev_msg);
+}
+
+/*
+ * Called as part of ip6_init
+ */
+void
+in6_ifaddr_init(void)
+{
+ in6_cga_init();
+ in6_multi_init();
+
+ PE_parse_boot_argn("ifa_debug", &in6ifa_debug, sizeof(in6ifa_debug));
+
+ vm_size_t in6ifa_size = (in6ifa_debug == 0) ? sizeof(struct in6_ifaddr) :
+ sizeof(struct in6_ifaddr_dbg);
+
+ in6ifa_zone = zone_create(IN6IFA_ZONE_NAME, in6ifa_size, ZC_ZFREE_CLEARMEM);
+
+ lck_mtx_init(&in6ifa_trash_lock, ifa_mtx_grp, ifa_mtx_attr);
+ TAILQ_INIT(&in6ifa_trash_head);
+}
+
+static struct in6_ifaddr *
+in6_ifaddr_alloc(zalloc_flags_t how)
+{
+ struct in6_ifaddr *in6ifa;
+
+ in6ifa = zalloc_flags(in6ifa_zone, how | Z_ZERO);
+ if (in6ifa != NULL) {
+ 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 =
+ (struct in6_ifaddr_dbg *)in6ifa;
+ in6ifa->ia_ifa.ifa_debug |= IFD_DEBUG;
+ in6ifa->ia_ifa.ifa_trace = in6_ifaddr_trace;
+ in6ifa->ia_ifa.ifa_attached = in6_ifaddr_attached;
+ in6ifa->ia_ifa.ifa_detached = in6_ifaddr_detached;
+ ctrace_record(&in6ifa_dbg->in6ifa_alloc);
+ }
+ }
+
+ return in6ifa;
+}
+
+static void
+in6_ifaddr_free(struct ifaddr *ifa)
+{
+ IFA_LOCK_ASSERT_HELD(ifa);
+
+ if (ifa->ifa_refcnt != 0) {
+ panic("%s: ifa %p bad ref cnt", __func__, ifa);
+ /* NOTREACHED */
+ } else if (!(ifa->ifa_debug & IFD_ALLOC)) {
+ panic("%s: ifa %p cannot be freed", __func__, ifa);
+ /* NOTREACHED */
+ }
+ if (ifa->ifa_debug & IFD_DEBUG) {
+ struct in6_ifaddr_dbg *in6ifa_dbg =
+ (struct in6_ifaddr_dbg *)ifa;
+ ctrace_record(&in6ifa_dbg->in6ifa_free);
+ bcopy(&in6ifa_dbg->in6ifa, &in6ifa_dbg->in6ifa_old,
+ sizeof(struct in6_ifaddr));
+ if (ifa->ifa_debug & IFD_TRASHED) {
+ /* Become a regular mutex, just in case */
+ IFA_CONVERT_LOCK(ifa);
+ lck_mtx_lock(&in6ifa_trash_lock);
+ TAILQ_REMOVE(&in6ifa_trash_head, in6ifa_dbg,
+ in6ifa_trash_link);
+ lck_mtx_unlock(&in6ifa_trash_lock);
+ ifa->ifa_debug &= ~IFD_TRASHED;
+ }
+ }
+ IFA_UNLOCK(ifa);
+ ifa_lock_destroy(ifa);
+ bzero(ifa, sizeof(struct in6_ifaddr));
+ zfree(in6ifa_zone, ifa);
+}
+
+static void
+in6_ifaddr_attached(struct ifaddr *ifa)
+{
+ struct in6_ifaddr_dbg *in6ifa_dbg = (struct in6_ifaddr_dbg *)ifa;
+
+ IFA_LOCK_ASSERT_HELD(ifa);
+
+ if (!(ifa->ifa_debug & IFD_DEBUG)) {
+ panic("%s: ifa %p has no debug structure", __func__, ifa);
+ /* NOTREACHED */
+ }
+ if (ifa->ifa_debug & IFD_TRASHED) {
+ /* Become a regular mutex, just in case */
+ IFA_CONVERT_LOCK(ifa);
+ lck_mtx_lock(&in6ifa_trash_lock);
+ TAILQ_REMOVE(&in6ifa_trash_head, in6ifa_dbg, in6ifa_trash_link);
+ lck_mtx_unlock(&in6ifa_trash_lock);
+ ifa->ifa_debug &= ~IFD_TRASHED;
+ }
+}
+
+static void
+in6_ifaddr_detached(struct ifaddr *ifa)
+{
+ struct in6_ifaddr_dbg *in6ifa_dbg = (struct in6_ifaddr_dbg *)ifa;
+
+ IFA_LOCK_ASSERT_HELD(ifa);
+
+ if (!(ifa->ifa_debug & IFD_DEBUG)) {
+ panic("%s: ifa %p has no debug structure", __func__, ifa);
+ /* NOTREACHED */
+ } else if (ifa->ifa_debug & IFD_TRASHED) {
+ panic("%s: ifa %p is already in trash list", __func__, ifa);
+ /* NOTREACHED */
+ }
+ ifa->ifa_debug |= IFD_TRASHED;
+ /* Become a regular mutex, just in case */
+ IFA_CONVERT_LOCK(ifa);
+ lck_mtx_lock(&in6ifa_trash_lock);
+ TAILQ_INSERT_TAIL(&in6ifa_trash_head, in6ifa_dbg, in6ifa_trash_link);
+ lck_mtx_unlock(&in6ifa_trash_lock);
+}
+
+static void
+in6_ifaddr_trace(struct ifaddr *ifa, int refhold)
+{
+ struct in6_ifaddr_dbg *in6ifa_dbg = (struct in6_ifaddr_dbg *)ifa;
+ ctrace_t *tr;
+ u_int32_t idx;
+ u_int16_t *cnt;
+
+ if (!(ifa->ifa_debug & IFD_DEBUG)) {
+ panic("%s: ifa %p has no debug structure", __func__, ifa);
+ /* NOTREACHED */
+ }
+ if (refhold) {
+ cnt = &in6ifa_dbg->in6ifa_refhold_cnt;
+ tr = in6ifa_dbg->in6ifa_refhold;
+ } else {
+ cnt = &in6ifa_dbg->in6ifa_refrele_cnt;
+ tr = in6ifa_dbg->in6ifa_refrele;
+ }
+
+ idx = atomic_add_16_ov(cnt, 1) % IN6IFA_TRACE_HIST_SIZE;
+ ctrace_record(&tr[idx]);
+}
+
+/*
+ * Handle SIOCGASSOCIDS ioctl for PF_INET6 domain.
+ */
+static int
+in6_getassocids(struct socket *so, uint32_t *cnt, user_addr_t aidp)
+{
+ struct in6pcb *in6p = sotoin6pcb(so);
+ sae_associd_t aid;
+
+ if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD) {
+ return EINVAL;
+ }
+
+ /* IN6PCB has no concept of association */
+ aid = SAE_ASSOCID_ANY;
+ *cnt = 0;
+
+ /* just asking how many there are? */
+ if (aidp == USER_ADDR_NULL) {
+ return 0;
+ }
+
+ return copyout(&aid, aidp, sizeof(aid));
+}
+
+/*
+ * Handle SIOCGCONNIDS ioctl for PF_INET6 domain.
+ */
+static int
+in6_getconnids(struct socket *so, sae_associd_t aid, uint32_t *cnt,
+ user_addr_t cidp)
+{
+ struct in6pcb *in6p = sotoin6pcb(so);
+ sae_connid_t cid;
+
+ if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD) {
+ return EINVAL;
+ }
+
+ if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL) {
+ return EINVAL;
+ }
+
+ /* if connected, return 1 connection count */
+ *cnt = ((so->so_state & SS_ISCONNECTED) ? 1 : 0);
+
+ /* just asking how many there are? */
+ if (cidp == USER_ADDR_NULL) {
+ return 0;
+ }
+
+ /* if IN6PCB is connected, assign it connid 1 */
+ cid = ((*cnt != 0) ? 1 : SAE_CONNID_ANY);
+
+ return copyout(&cid, cidp, sizeof(cid));
+}
+
+/*
+ * Handle SIOCGCONNINFO ioctl for PF_INET6 domain.
+ */
+int
+in6_getconninfo(struct socket *so, sae_connid_t cid, uint32_t *flags,
+ uint32_t *ifindex, int32_t *soerror, user_addr_t src, socklen_t *src_len,
+ user_addr_t dst, socklen_t *dst_len, uint32_t *aux_type,
+ user_addr_t aux_data, uint32_t *aux_len)
+{
+ struct in6pcb *in6p = sotoin6pcb(so);
+ struct sockaddr_in6 sin6;
+ struct ifnet *ifp = NULL;
+ int error = 0;
+ u_int32_t copy_len = 0;
+
+ /*
+ * Don't test for INPCB_STATE_DEAD since this may be called
+ * after SOF_PCBCLEARING is set, e.g. after tcp_close().
+ */
+ if (in6p == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (cid != SAE_CONNID_ANY && cid != SAE_CONNID_ALL && cid != 1) {
+ error = EINVAL;
+ goto out;
+ }
+
+ ifp = in6p->in6p_last_outifp;
+ *ifindex = ((ifp != NULL) ? ifp->if_index : 0);
+ *soerror = so->so_error;
+ *flags = 0;
+ if (so->so_state & SS_ISCONNECTED) {
+ *flags |= (CIF_CONNECTED | CIF_PREFERRED);
+ }
+ if (in6p->in6p_flags & INP_BOUND_IF) {
+ *flags |= CIF_BOUND_IF;
+ }
+ if (!(in6p->in6p_flags & INP_IN6ADDR_ANY)) {
+ *flags |= CIF_BOUND_IP;
+ }
+ if (!(in6p->in6p_flags & INP_ANONPORT)) {
+ *flags |= CIF_BOUND_PORT;
+ }
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+
+ /* source address and port */
+ sin6.sin6_port = in6p->in6p_lport;
+ in6_recoverscope(&sin6, &in6p->in6p_laddr, NULL);
+ if (*src_len == 0) {
+ *src_len = sin6.sin6_len;
+ } else {
+ if (src != USER_ADDR_NULL) {
+ copy_len = min(*src_len, sizeof(sin6));
+ error = copyout(&sin6, src, copy_len);
+ if (error != 0) {
+ goto out;
+ }
+ *src_len = copy_len;
+ }
+ }
+
+ /* destination address and port */
+ sin6.sin6_port = in6p->in6p_fport;
+ in6_recoverscope(&sin6, &in6p->in6p_faddr, NULL);
+ if (*dst_len == 0) {
+ *dst_len = sin6.sin6_len;
+ } else {
+ if (dst != USER_ADDR_NULL) {
+ copy_len = min(*dst_len, sizeof(sin6));
+ error = copyout(&sin6, dst, copy_len);
+ if (error != 0) {
+ goto out;
+ }
+ *dst_len = copy_len;
+ }
+ }
+
+ if (SOCK_PROTO(so) == IPPROTO_TCP) {
+ struct conninfo_tcp tcp_ci;
+
+ *aux_type = CIAUX_TCP;
+ if (*aux_len == 0) {
+ *aux_len = sizeof(tcp_ci);
+ } else {
+ if (aux_data != USER_ADDR_NULL) {
+ copy_len = min(*aux_len, sizeof(tcp_ci));
+ bzero(&tcp_ci, sizeof(tcp_ci));
+ tcp_getconninfo(so, &tcp_ci);
+ error = copyout(&tcp_ci, aux_data, copy_len);
+ if (error != 0) {
+ goto out;
+ }
+ *aux_len = copy_len;
+ }
+ }
+ } else {
+ *aux_type = 0;
+ *aux_len = 0;
+ }
+
+out:
+ return error;
+}
+
+/*
+ * 'u' group ioctls.
+ *
+ * The switch statement below does nothing at runtime, as it serves as a
+ * compile time check to ensure that all of the socket 'u' ioctls (those
+ * in the 'u' group going thru soo_ioctl) that are made available by the
+ * networking stack is unique. This works as long as this routine gets
+ * updated each time a new interface ioctl gets added.
+ *
+ * Any failures at compile time indicates duplicated ioctl values.
+ */
+static __attribute__((unused)) void
+in6ioctl_cassert(void)
+{
+ /*
+ * This is equivalent to _CASSERT() and the compiler wouldn't
+ * generate any instructions, thus for compile time only.
+ */
+ switch ((u_long)0) {
+ case 0:
+
+ /* bsd/netinet6/in6_var.h */
+ case SIOCAADDRCTL_POLICY:
+ case SIOCDADDRCTL_POLICY:
+ case SIOCDRADD_IN6_32:
+ case SIOCDRADD_IN6_64:
+ case SIOCDRDEL_IN6_32:
+ case SIOCDRDEL_IN6_64:
+ ;
+ }
+}
+
+struct in6_llentry {
+ struct llentry base;
+};
+
+#define IN6_LLTBL_DEFAULT_HSIZE 32
+#define IN6_LLTBL_HASH(k, h) \
+ ((((((((k) >> 8) ^ (k)) >> 8) ^ (k)) >> 8) ^ (k)) & ((h) - 1))
+
+/*
+ * Do actual deallocation of @lle.
+ */
+static void
+in6_lltable_destroy_lle_unlocked(struct llentry *lle)
+{
+ LLE_LOCK_DESTROY(lle);
+ LLE_REQ_DESTROY(lle);
+ FREE(lle, M_LLTABLE);
+}
+
+/*
+ * Called by LLE_FREE_LOCKED when number of references
+ * drops to zero.
+ */
+static void
+in6_lltable_destroy_lle(struct llentry *lle)
+{
+ LLE_WUNLOCK(lle);
+ /* XXX TBD */
+ //thread_call_free(lle->lle_timer);
+ in6_lltable_destroy_lle_unlocked(lle);
+}
+
+
+static struct llentry *
+in6_lltable_new(const struct in6_addr *addr6, uint16_t flags)
+{
+#pragma unused(flags)
+ struct in6_llentry *lle;
+
+ MALLOC(lle, struct in6_llentry *, sizeof(struct in6_llentry), M_LLTABLE, M_NOWAIT | M_ZERO);
+ if (lle == NULL) { /* NB: caller generates msg */
+ return NULL;
+ }
+
+ lle->base.r_l3addr.addr6 = *addr6;
+ lle->base.lle_refcnt = 1;
+ lle->base.lle_free = in6_lltable_destroy_lle;
+ LLE_LOCK_INIT(&lle->base);
+ LLE_REQ_INIT(&lle->base);
+#if 0
+ /* XXX TBD */
+ lle->base.lle_timer = thread_call_allocate(nd6_llinfo_timer, lle);
+
+ if (lle->base.lle_timer == NULL) {
+ printf("lle_timer thread call could not be allocated.\n");
+ LLE_LOCK_DESTROY(&lle->base);
+ LLE_REQ_DESTROY(&lle->base);
+ FREE(lle, M_LLTABLE);
+ return NULL;
+ }
+#endif
+ return &lle->base;
+}
+
+static int
+in6_lltable_match_prefix(const struct sockaddr *saddr,
+ const struct sockaddr *smask, uint16_t flags, struct llentry *lle)
+{
+ const struct in6_addr *addr, *mask, *lle_addr;
+
+ addr = &((const struct sockaddr_in6 *)(const void *)saddr)->sin6_addr;
+ mask = &((const struct sockaddr_in6 *)(const void *)smask)->sin6_addr;
+ lle_addr = &lle->r_l3addr.addr6;
+
+ if (IN6_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0) {
+ return 0;
+ }
+
+ if (lle->la_flags & LLE_IFADDR) {
+ /*
+ * Delete LLE_IFADDR records IFF address & flag matches.
+ * Note that addr is the interface address within prefix
+ * being matched.
+ */
+ if (IN6_ARE_ADDR_EQUAL(addr, lle_addr) &&
+ (flags & LLE_STATIC) != 0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /* flags & LLE_STATIC means deleting both dynamic and static entries */
+ if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+in6_lltable_free_entry(struct lltable *llt, struct llentry *lle)
+{
+ struct ifnet *ifp;
+
+ LLE_WLOCK_ASSERT(lle);
+ KASSERT(llt != NULL, ("lltable is NULL"));
+
+ /* Unlink entry from table */
+ if ((lle->la_flags & LLE_LINKED) != 0) {
+ ifp = llt->llt_ifp;
+ if_afdata_wlock_assert(ifp, llt->llt_af);
+ lltable_unlink_entry(llt, lle);
+ }
+
+#if 0
+ /* XXX TBD */
+ if (thread_call_cancel(lle->lle_timer) == TRUE) {
+ LLE_REMREF(lle);
+ }
+#endif
+ llentry_free(lle);
+}
+
+static int
+in6_lltable_rtcheck(struct ifnet *ifp,
+ uint16_t flags, const struct sockaddr *l3addr)
+{
+#pragma unused(flags)
+ struct rtentry *rt;
+
+ KASSERT(l3addr->sa_family == AF_INET6,
+ ("sin_family %d", l3addr->sa_family));
+ /* XXX rtalloc1 should take a const param */
+ rt = rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0);
+ if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) {
+ struct ifaddr *ifa;
+ /*
+ * Create an ND6 cache for an IPv6 neighbor
+ * that is not covered by our own prefix.
+ */
+ /* XXX ifaof_ifpforaddr should take a const param */
+ ifa = ifaof_ifpforaddr(__DECONST(struct sockaddr *, l3addr), ifp);
+ if (ifa != NULL) {
+ IFA_REMREF(ifa);
+ if (rt != NULL) {
+ rtfree(rt);
+ }
+ return 0;
+ }
+ log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n",
+ ip6_sprintf(&((const struct sockaddr_in6 *)(const void *)l3addr)->sin6_addr));
+ if (rt != NULL) {
+ rtfree(rt);
+ }
+ return EINVAL;