+ in_multi_init();
+
+ PE_parse_boot_argn("ifa_debug", &inifa_debug, sizeof (inifa_debug));
+
+ inifa_size = (inifa_debug == 0) ? sizeof (struct in_ifaddr) :
+ sizeof (struct in_ifaddr_dbg);
+
+ inifa_zone = zinit(inifa_size, INIFA_ZONE_MAX * inifa_size,
+ 0, INIFA_ZONE_NAME);
+ if (inifa_zone == NULL) {
+ panic("%s: failed allocating %s", __func__, INIFA_ZONE_NAME);
+ /* NOTREACHED */
+ }
+ zone_change(inifa_zone, Z_EXPAND, TRUE);
+ zone_change(inifa_zone, Z_CALLERACCT, FALSE);
+
+ lck_mtx_init(&inifa_trash_lock, ifa_mtx_grp, ifa_mtx_attr);
+ TAILQ_INIT(&inifa_trash_head);
+}
+
+static struct in_ifaddr *
+in_ifaddr_alloc(int how)
+{
+ struct in_ifaddr *inifa;
+
+ inifa = (how == M_WAITOK) ? zalloc(inifa_zone) :
+ zalloc_noblock(inifa_zone);
+ if (inifa != NULL) {
+ bzero(inifa, inifa_size);
+ inifa->ia_ifa.ifa_free = in_ifaddr_free;
+ inifa->ia_ifa.ifa_debug |= IFD_ALLOC;
+ ifa_lock_init(&inifa->ia_ifa);
+ if (inifa_debug != 0) {
+ struct in_ifaddr_dbg *inifa_dbg =
+ (struct in_ifaddr_dbg *)inifa;
+ inifa->ia_ifa.ifa_debug |= IFD_DEBUG;
+ inifa->ia_ifa.ifa_trace = in_ifaddr_trace;
+ inifa->ia_ifa.ifa_attached = in_ifaddr_attached;
+ inifa->ia_ifa.ifa_detached = in_ifaddr_detached;
+ ctrace_record(&inifa_dbg->inifa_alloc);
+ }
+ }
+ return (inifa);
+}
+
+static void
+in_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 */
+ } if (!(ifa->ifa_debug & IFD_ALLOC)) {
+ panic("%s: ifa %p cannot be freed", __func__, ifa);
+ /* NOTREACHED */
+ }
+ if (ifa->ifa_debug & IFD_DEBUG) {
+ struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa;
+ ctrace_record(&inifa_dbg->inifa_free);
+ bcopy(&inifa_dbg->inifa, &inifa_dbg->inifa_old,
+ sizeof (struct in_ifaddr));
+ if (ifa->ifa_debug & IFD_TRASHED) {
+ /* Become a regular mutex, just in case */
+ IFA_CONVERT_LOCK(ifa);
+ lck_mtx_lock(&inifa_trash_lock);
+ TAILQ_REMOVE(&inifa_trash_head, inifa_dbg,
+ inifa_trash_link);
+ lck_mtx_unlock(&inifa_trash_lock);
+ ifa->ifa_debug &= ~IFD_TRASHED;
+ }
+ }
+ IFA_UNLOCK(ifa);
+ ifa_lock_destroy(ifa);
+ bzero(ifa, sizeof (struct in_ifaddr));
+ zfree(inifa_zone, ifa);
+}
+
+static void
+in_ifaddr_attached(struct ifaddr *ifa)
+{
+ struct in_ifaddr_dbg *inifa_dbg = (struct in_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(&inifa_trash_lock);
+ TAILQ_REMOVE(&inifa_trash_head, inifa_dbg, inifa_trash_link);
+ lck_mtx_unlock(&inifa_trash_lock);
+ ifa->ifa_debug &= ~IFD_TRASHED;
+ }
+}
+
+static void
+in_ifaddr_detached(struct ifaddr *ifa)
+{
+ struct in_ifaddr_dbg *inifa_dbg = (struct in_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(&inifa_trash_lock);
+ TAILQ_INSERT_TAIL(&inifa_trash_head, inifa_dbg, inifa_trash_link);
+ lck_mtx_unlock(&inifa_trash_lock);
+}
+
+static void
+in_ifaddr_trace(struct ifaddr *ifa, int refhold)
+{
+ struct in_ifaddr_dbg *inifa_dbg = (struct in_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 = &inifa_dbg->inifa_refhold_cnt;
+ tr = inifa_dbg->inifa_refhold;
+ } else {
+ cnt = &inifa_dbg->inifa_refrele_cnt;
+ tr = inifa_dbg->inifa_refrele;
+ }
+
+ idx = atomic_add_16_ov(cnt, 1) % INIFA_TRACE_HIST_SIZE;
+ ctrace_record(&tr[idx]);
+}
+
+/*
+ * Handle SIOCGASSOCIDS ioctl for PF_INET domain.
+ */
+static int
+in_getassocids(struct socket *so, uint32_t *cnt, user_addr_t aidp)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ sae_associd_t aid;
+
+ if (inp == NULL || inp->inp_state == INPCB_STATE_DEAD)
+ return (EINVAL);
+
+ /* INPCB 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_INET domain.
+ */
+static int
+in_getconnids(struct socket *so, sae_associd_t aid, uint32_t *cnt,
+ user_addr_t cidp)
+{
+ struct inpcb *inp = sotoinpcb(so);
+ sae_connid_t cid;
+
+ if (inp == NULL || inp->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 INPCB is connected, assign it connid 1 */
+ cid = ((*cnt != 0) ? 1 : SAE_CONNID_ANY);
+
+ return (copyout(&cid, cidp, sizeof (cid)));
+}
+
+/*
+ * Handle SIOCGCONNINFO ioctl for PF_INET domain.
+ */
+int
+in_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 inpcb *inp = sotoinpcb(so);