+
+static void
+in6_ifaddr_set_dadprogress(struct in6_ifaddr *ia)
+{
+ uint32_t flags = IN6_IFF_TENTATIVE;
+ uint32_t optdad = nd6_optimistic_dad;
+
+ if (optdad && (ia->ia_ifp->if_eflags & IFEF_IPV6_ROUTER) == 0) {
+ if ((optdad & ND6_OPTIMISTIC_DAD_LINKLOCAL) &&
+ IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr))
+ flags = IN6_IFF_OPTIMISTIC;
+ else if ((optdad & ND6_OPTIMISTIC_DAD_AUTOCONF) &&
+ (ia->ia6_flags & IN6_IFF_AUTOCONF)) {
+ if (ia->ia6_flags & IN6_IFF_TEMPORARY) {
+ if (optdad & ND6_OPTIMISTIC_DAD_TEMPORARY)
+ flags = IN6_IFF_OPTIMISTIC;
+ } else if (ia->ia6_flags & IN6_IFF_SECURED) {
+ if (optdad & ND6_OPTIMISTIC_DAD_SECURED)
+ flags = IN6_IFF_OPTIMISTIC;
+ }
+ } else if ((optdad & ND6_OPTIMISTIC_DAD_DYNAMIC) &&
+ (ia->ia6_flags & IN6_IFF_DYNAMIC)) {
+ if (ia->ia6_flags & IN6_IFF_TEMPORARY) {
+ if (optdad & ND6_OPTIMISTIC_DAD_TEMPORARY)
+ flags = IN6_IFF_OPTIMISTIC;
+ } else {
+ flags = IN6_IFF_OPTIMISTIC;
+ }
+ }
+ }
+
+ ia->ia6_flags &= ~(IN6_IFF_DUPLICATED | IN6_IFF_DADPROGRESS);
+ ia->ia6_flags |= flags;
+}
+
+/*
+ * 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);
+ associd_t aid;
+
+ if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD)
+ return (EINVAL);
+
+ /* IN6PCB has no concept of association */
+ aid = 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, associd_t aid, uint32_t *cnt,
+ user_addr_t cidp)
+{
+ struct in6pcb *in6p = sotoin6pcb(so);
+ connid_t cid;
+
+ if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD)
+ return (EINVAL);
+
+ if (aid != ASSOCID_ANY && aid != 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 : CONNID_ANY);
+
+ return (copyout(&cid, cidp, sizeof (cid)));
+}
+
+/*
+ * Handle SIOCGCONNINFO ioctl for PF_INET6 domain.
+ */
+static int
+in6_getconninfo(struct socket *so, 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)
+{
+#pragma unused(aux_data)
+ 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 != CONNID_ANY && cid != 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;
+ bcopy(&in6p->in6p_laddr, &sin6.sin6_addr, sizeof (struct in6_addr));
+ 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;
+ bcopy(&in6p->in6p_faddr, &sin6.sin6_addr, sizeof (struct in6_addr));
+ 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;
+ }
+ }
+
+ *aux_type = 0;
+ *aux_len = 0;
+ 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;
+ }
+ }
+ }
+
+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 SIOCGETSGCNT_IN6:
+ case SIOCGETMIFCNT_IN6_32:
+ case SIOCGETMIFCNT_IN6_64:
+ case SIOCAADDRCTL_POLICY:
+ case SIOCDADDRCTL_POLICY:
+ case SIOCDRADD_IN6_32:
+ case SIOCDRADD_IN6_64:
+ case SIOCDRDEL_IN6_32:
+ case SIOCDRDEL_IN6_64:
+ ;
+ }
+}