]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/in6_mcast.c
xnu-4570.41.2.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_mcast.c
index 05670d211420ac2d139ed6d7af0317164801989a..3bd00c8a2162e663914534926b938541eff2e8eb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2010-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -80,6 +80,7 @@
 
 #include <net/if.h>
 #include <net/if_dl.h>
+#include <net/net_api_stats.h>
 #include <net/route.h>
 
 #include <netinet/in.h>
 #include <netinet6/mld6_var.h>
 #include <netinet6/scope6_var.h>
 
-#ifndef __SOCKUNION_DECLARED
-union sockunion {
-       struct sockaddr_storage ss;
-       struct sockaddr         sa;
-       struct sockaddr_dl      sdl;
-       struct sockaddr_in6     sin6;
-};
-typedef union sockunion sockunion_t;
-#define __SOCKUNION_DECLARED
-#endif /* __SOCKUNION_DECLARED */
-
 static void    im6f_commit(struct in6_mfilter *);
 static int     im6f_get_source(struct in6_mfilter *imf,
                    const struct sockaddr_in6 *psin,
@@ -119,10 +109,10 @@ static void       im6f_rollback(struct in6_mfilter *);
 static void    im6f_reap(struct in6_mfilter *);
 static int     im6o_grow(struct ip6_moptions *, size_t);
 static size_t  im6o_match_group(const struct ip6_moptions *,
-                   const struct ifnet *, const struct sockaddr *);
+                   const struct ifnet *, const struct sockaddr_in6 *);
 static struct in6_msource *
-               im6o_match_source(const struct ip6_moptions *, const size_t,
-                   const struct sockaddr *);
+               im6o_match_source(const struct ip6_moptions *,
+                   const size_t, const struct sockaddr_in6 *);
 static void    im6s_merge(struct ip6_msource *ims,
                    const struct in6_msource *lims, const int rollback);
 static int     in6_mc_get(struct ifnet *, const struct in6_addr *,
@@ -246,9 +236,6 @@ static void ip6ms_free(struct ip6_msource *);
 static struct in6_msource *in6ms_alloc(int);
 static void in6ms_free(struct in6_msource *);
 
-#define        IM6O_CAST_TO_NONCONST(x) ((struct ip6_moptions *)(void *)(uintptr_t)x)
-#define        IN6M_CAST_TO_NONCONST(x) ((struct in6_multi *)(void *)(uintptr_t)x)
-
 /*
  * IPv6 source tree comparison function.
  *
@@ -342,16 +329,16 @@ im6o_grow(struct ip6_moptions *imo, size_t newmax)
  */
 static size_t
 im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp,
-    const struct sockaddr *group)
+    const struct sockaddr_in6 *group)
 {
        const struct sockaddr_in6 *gsin6;
        struct in6_multi *pinm;
        int               idx;
        int               nmships;
 
-       IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
+       IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions *, imo));
 
-       gsin6 = (const struct sockaddr_in6 *)group;
+       gsin6 = group;
 
        /* The im6o_membership array may be lazy allocated. */
        if (imo->im6o_membership == NULL || imo->im6o_num_memberships == 0)
@@ -390,16 +377,16 @@ im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp,
  */
 static struct in6_msource *
 im6o_match_source(const struct ip6_moptions *imo, const size_t gidx,
-    const struct sockaddr *src)
+    const struct sockaddr_in6 *src)
 {
        struct ip6_msource       find;
        struct in6_mfilter      *imf;
        struct ip6_msource      *ims;
-       const sockunion_t       *psa;
+       const struct sockaddr_in6 *psa;
 
-       IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
+       IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions *, imo));
 
-       VERIFY(src->sa_family == AF_INET6);
+       VERIFY(src->sin6_family == AF_INET6);
        VERIFY(gidx != (size_t)-1 && gidx < imo->im6o_num_memberships);
 
        /* The im6o_mfilters array may be lazy allocated. */
@@ -407,8 +394,8 @@ im6o_match_source(const struct ip6_moptions *imo, const size_t gidx,
                return (NULL);
        imf = &imo->im6o_mfilters[gidx];
 
-       psa = (const sockunion_t *)src;
-       find.im6s_addr = psa->sin6.sin6_addr;
+       psa = src;
+       find.im6s_addr = psa->sin6_addr;
        in6_clearscope(&find.im6s_addr);                /* XXX */
        ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
 
@@ -423,13 +410,13 @@ im6o_match_source(const struct ip6_moptions *imo, const size_t gidx,
  */
 int
 im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp,
-    const struct sockaddr *group, const struct sockaddr *src)
+    const struct sockaddr_in6 *group, const struct sockaddr_in6 *src)
 {
        size_t gidx;
        struct in6_msource *ims;
        int mode;
 
-       IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
+       IM6O_LOCK_ASSERT_HELD(__DECONST(struct ip6_moptions *, imo));
        VERIFY(ifp != NULL);
 
        gidx = im6o_match_group(imo, ifp, group);
@@ -795,7 +782,8 @@ im6f_rollback(struct in6_mfilter *imf)
                        lims->im6sl_st[1] = lims->im6sl_st[0];
                } else {
                        /* revert source added t1 */
-                       MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
+                       MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(lims)));
                        RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
                        in6ms_free(lims);
                        imf->im6f_nsrc--;
@@ -855,7 +843,8 @@ im6f_reap(struct in6_mfilter *imf)
                lims = (struct in6_msource *)ims;
                if ((lims->im6sl_st[0] == MCAST_UNDEFINED) &&
                    (lims->im6sl_st[1] == MCAST_UNDEFINED)) {
-                       MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
+                       MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(lims)));
                        RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
                        in6ms_free(lims);
                        imf->im6f_nsrc--;
@@ -876,7 +865,8 @@ im6f_purge(struct in6_mfilter *imf)
 
        RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) {
                lims = (struct in6_msource *)ims;
-               MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
+               MLD_PRINTF(("%s: free in6ms 0x%llx\n", __func__,
+                   (uint64_t)VM_KERNEL_ADDRPERM(lims)));
                RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
                in6ms_free(lims);
                imf->im6f_nsrc--;
@@ -916,8 +906,8 @@ in6m_get_source(struct in6_multi *inm, const struct in6_addr *addr,
                RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims);
                ++inm->in6m_nsrc;
                ims = nims;
-               MLD_PRINTF(("%s: allocated %s as %p\n", __func__,
-                   ip6_sprintf(addr), ims));
+               MLD_PRINTF(("%s: allocated %s as 0x%llx\n", __func__,
+                   ip6_sprintf(addr), (uint64_t)VM_KERNEL_ADDRPERM(ims)));
        }
 
        *pims = ims;
@@ -936,7 +926,7 @@ uint8_t
 im6s_get_mode(const struct in6_multi *inm, const struct ip6_msource *ims,
     uint8_t t)
 {
-       IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm));
+       IN6M_LOCK_ASSERT_HELD(__DECONST(struct in6_multi *, inm));
 
        t = !!t;
        if (inm->in6m_st[t].iss_ex > 0 &&
@@ -995,7 +985,7 @@ im6s_merge(struct ip6_msource *ims, const struct in6_msource *lims,
 static int
 in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
 {
-       struct ip6_msource      *ims, *nims;
+       struct ip6_msource      *ims, *nims = NULL;
        struct in6_msource      *lims;
        int                      schanged, error;
        int                      nsrc0, nsrc1;
@@ -1107,7 +1097,9 @@ in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
                inm->in6m_st[1].iss_asm++;
        }
 
-       MLD_PRINTF(("%s: merged imf %p to inm %p\n", __func__, imf, inm));
+       MLD_PRINTF(("%s: merged imf 0x%llx to inm 0x%llx\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(imf),
+           (uint64_t)VM_KERNEL_ADDRPERM(inm)));
        in6m_print(inm);
 
 out_reap:
@@ -1129,7 +1121,8 @@ in6m_commit(struct in6_multi *inm)
 
        IN6M_LOCK_ASSERT_HELD(inm);
 
-       MLD_PRINTF(("%s: commit inm %p\n", __func__, inm));
+       MLD_PRINTF(("%s: commit inm 0x%llx\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(inm)));
        MLD_PRINTF(("%s: pre commit:\n", __func__));
        in6m_print(inm);
 
@@ -1154,7 +1147,8 @@ in6m_reap(struct in6_multi *inm)
                    ims->im6s_st[1].ex > 0 || ims->im6s_st[1].in > 0 ||
                    ims->im6s_stp != 0)
                        continue;
-               MLD_PRINTF(("%s: free ims %p\n", __func__, ims));
+               MLD_PRINTF(("%s: free ims 0x%llx\n", __func__,
+                   (uint64_t)VM_KERNEL_ADDRPERM(ims)));
                RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims);
                ip6ms_free(ims);
                inm->in6m_nsrc--;
@@ -1172,7 +1166,8 @@ in6m_purge(struct in6_multi *inm)
        IN6M_LOCK_ASSERT_HELD(inm);
 
        RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) {
-               MLD_PRINTF(("%s: free ims %p\n", __func__, ims));
+               MLD_PRINTF(("%s: free ims 0x%llx\n", __func__,
+                   (uint64_t)VM_KERNEL_ADDRPERM(ims)));
                RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims);
                ip6ms_free(ims);
                inm->in6m_nsrc--;
@@ -1199,8 +1194,6 @@ in6_joingroup(struct ifnet *ifp, struct in6_addr *mcaddr,
                return (NULL);
        }
 
-       delay = (delay * PR_SLOWHZ) / hz;
-
        error = in6_mc_join(ifp, mcaddr, NULL, &imm->i6mm_maddr, delay);
        if (error) {
                *errorp = error;
@@ -1244,6 +1237,7 @@ in6_mc_join(struct ifnet *ifp, const struct in6_addr *mcaddr,
        struct in6_mfilter       timf;
        struct in6_multi        *inm = NULL;
        int                      error = 0;
+       struct mld_tparams       mtp;
 
        /*
         * Sanity: Check scope zone ID was set for ifp, if and
@@ -1255,9 +1249,11 @@ in6_mc_join(struct ifnet *ifp, const struct in6_addr *mcaddr,
                VERIFY(mcaddr->s6_addr16[1] != 0);
        }
 
-       MLD_PRINTF(("%s: join %s on %p(%s%d))\n", __func__,
-           ip6_sprintf(mcaddr), ifp, ifp->if_name, ifp->if_unit));
+       MLD_PRINTF(("%s: join %s on 0x%llx(%s))\n", __func__,
+           ip6_sprintf(mcaddr), (uint64_t)VM_KERNEL_ADDRPERM(ifp),
+           if_name(ifp)));
 
+       bzero(&mtp, sizeof (mtp));
        *pinm = NULL;
 
        /*
@@ -1285,15 +1281,17 @@ in6_mc_join(struct ifnet *ifp, const struct in6_addr *mcaddr,
        }
 
        MLD_PRINTF(("%s: doing mld downcall\n", __func__));
-       error = mld_change_state(inm, delay);
+       error = mld_change_state(inm, &mtp, delay);
        if (error) {
                MLD_PRINTF(("%s: failed to update source\n", __func__));
+               im6f_rollback(imf);
                goto out_in6m_release;
        }
 
 out_in6m_release:
        if (error) {
-               MLD_PRINTF(("%s: dropping ref on %p\n", __func__, inm));
+               MLD_PRINTF(("%s: dropping ref on 0x%llx\n", __func__,
+                   (uint64_t)VM_KERNEL_ADDRPERM(inm)));
                IN6M_UNLOCK(inm);
                IN6M_REMREF(inm);
        } else {
@@ -1301,6 +1299,9 @@ out_in6m_release:
                *pinm = inm;    /* keep refcount from in6_mc_get() */
        }
 
+       /* schedule timer now that we've dropped the lock(s) */
+       mld_set_timeout(&mtp);
+
        return (error);
 }
 
@@ -1322,7 +1323,9 @@ in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
 {
        struct in6_mfilter       timf;
        int                      error, lastref;
+       struct mld_tparams       mtp;
 
+       bzero(&mtp, sizeof (mtp));
        error = 0;
 
        IN6M_LOCK_ASSERT_NOTHELD(inm);
@@ -1330,10 +1333,10 @@ in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
        in6_multihead_lock_exclusive();
        IN6M_LOCK(inm);
 
-       MLD_PRINTF(("%s: leave inm %p, %s/%s%d, imf %p\n", __func__,
-           inm, ip6_sprintf(&inm->in6m_addr),
+       MLD_PRINTF(("%s: leave inm 0x%llx, %s/%s%d, imf 0x%llx\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(inm), ip6_sprintf(&inm->in6m_addr),
            (in6m_is_ifp_detached(inm) ? "null" : inm->in6m_ifp->if_name),
-           inm->in6m_ifp->if_unit, imf));
+           inm->in6m_ifp->if_unit, (uint64_t)VM_KERNEL_ADDRPERM(imf)));
 
        /*
         * If no imf was specified (i.e. kernel consumer),
@@ -1357,7 +1360,7 @@ in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
        KASSERT(error == 0, ("%s: failed to merge inm state\n", __func__));
 
        MLD_PRINTF(("%s: doing mld downcall\n", __func__));
-       error = mld_change_state(inm, 0);
+       error = mld_change_state(inm, &mtp, 0);
 #if MLD_DEBUG
        if (error)
                MLD_PRINTF(("%s: failed mld downcall\n", __func__));
@@ -1371,6 +1374,9 @@ in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
        if (lastref)
                IN6M_REMREF(inm);       /* for in6_multihead list */
 
+       /* schedule timer now that we've dropped the lock(s) */
+       mld_set_timeout(&mtp);
+
        return (error);
 }
 
@@ -1387,7 +1393,7 @@ static int
 in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
 {
        struct group_source_req          gsr;
-       sockunion_t                     *gsa, *ssa;
+       struct sockaddr_in6             *gsa, *ssa;
        struct ifnet                    *ifp;
        struct in6_mfilter              *imf;
        struct ip6_moptions             *imo;
@@ -1396,14 +1402,16 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
        size_t                           idx;
        uint16_t                         fmode;
        int                              error, doblock;
+       struct mld_tparams               mtp;
 
+       bzero(&mtp, sizeof (mtp));
        ifp = NULL;
        error = 0;
        doblock = 0;
 
        memset(&gsr, 0, sizeof(struct group_source_req));
-       gsa = (sockunion_t *)&gsr.gsr_group;
-       ssa = (sockunion_t *)&gsr.gsr_source;
+       gsa = (struct sockaddr_in6 *)&gsr.gsr_group;
+       ssa = (struct sockaddr_in6 *)&gsr.gsr_source;
 
        switch (sopt->sopt_name) {
        case MCAST_BLOCK_SOURCE:
@@ -1414,12 +1422,12 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
                if (error)
                        return (error);
 
-               if (gsa->sin6.sin6_family != AF_INET6 ||
-                   gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+               if (gsa->sin6_family != AF_INET6 ||
+                   gsa->sin6_len != sizeof(struct sockaddr_in6))
                        return (EINVAL);
 
-               if (ssa->sin6.sin6_family != AF_INET6 ||
-                   ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+               if (ssa->sin6_family != AF_INET6 ||
+                   ssa->sin6_len != sizeof(struct sockaddr_in6))
                        return (EINVAL);
 
                ifnet_head_lock_shared();
@@ -1443,13 +1451,12 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
                MLD_PRINTF(("%s: unknown sopt_name %d\n",
                    __func__, sopt->sopt_name));
                return (EOPNOTSUPP);
-               break;
        }
 
-       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
                return (EINVAL);
 
-       (void) in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+       (void) in6_setscope(&gsa->sin6_addr, ifp, NULL);
 
        /*
         * Check if we are actually a member of this group.
@@ -1459,7 +1466,7 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
                return (ENOMEM);
 
        IM6O_LOCK(imo);
-       idx = im6o_match_group(imo, ifp, &gsa->sa);
+       idx = im6o_match_group(imo, ifp, gsa);
        if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
                error = EADDRNOTAVAIL;
                goto out_imo_locked;
@@ -1485,10 +1492,10 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
         *  Asked to unblock, but nothing to unblock.
         * If adding a new block entry, allocate it.
         */
-       ims = im6o_match_source(imo, idx, &ssa->sa);
+       ims = im6o_match_source(imo, idx, ssa);
        if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
                MLD_PRINTF(("%s: source %s %spresent\n", __func__,
-                   ip6_sprintf(&ssa->sin6.sin6_addr),
+                   ip6_sprintf(&ssa->sin6_addr),
                    doblock ? "" : "not "));
                error = EADDRNOTAVAIL;
                goto out_imo_locked;
@@ -1499,12 +1506,12 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
         */
        if (doblock) {
                MLD_PRINTF(("%s: %s source\n", __func__, "block"));
-               ims = im6f_graft(imf, fmode, &ssa->sin6);
+               ims = im6f_graft(imf, fmode, ssa);
                if (ims == NULL)
                        error = ENOMEM;
        } else {
                MLD_PRINTF(("%s: %s source\n", __func__, "allow"));
-               error = im6f_prune(imf, &ssa->sin6);
+               error = im6f_prune(imf, ssa);
        }
 
        if (error) {
@@ -1525,7 +1532,7 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
        }
 
        MLD_PRINTF(("%s: doing mld downcall\n", __func__));
-       error = mld_change_state(inm, 0);
+       error = mld_change_state(inm, &mtp, 0);
        IN6M_UNLOCK(inm);
 #if MLD_DEBUG
        if (error)
@@ -1543,6 +1550,10 @@ out_im6f_rollback:
 out_imo_locked:
        IM6O_UNLOCK(imo);
        IM6O_REMREF(imo);       /* from in6p_findmoptions() */
+
+       /* schedule timer now that we've dropped the lock(s) */
+       mld_set_timeout(&mtp);
+
        return (error);
 }
 
@@ -1608,9 +1619,9 @@ in6p_findmoptions(struct inpcb *inp)
 static int
 in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
 {
-       struct __msfilterreq64  msfr, msfr64;
+       struct __msfilterreq64  msfr = {}, msfr64;
        struct __msfilterreq32  msfr32;
-       sockunion_t             *gsa;
+       struct sockaddr_in6     *gsa;
        struct ifnet            *ifp;
        struct ip6_moptions     *imo;
        struct in6_mfilter      *imf;
@@ -1633,7 +1644,7 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
                if (error)
                        return (error);
                /* we never use msfr.msfr_srcs; */
-               memcpy(&msfr, &msfr64, sizeof(msfr));
+               memcpy(&msfr, &msfr64, sizeof(msfr64));
        } else {
                error = sooptcopyin(sopt, &msfr32,
                    sizeof(struct __msfilterreq32),
@@ -1641,15 +1652,15 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
                if (error)
                        return (error);
                /* we never use msfr.msfr_srcs; */
-               memcpy(&msfr, &msfr32, sizeof(msfr));
+               memcpy(&msfr, &msfr32, sizeof(msfr32));
        }
 
        if (msfr.msfr_group.ss_family != AF_INET6 ||
            msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6))
                return (EINVAL);
 
-       gsa = (sockunion_t *)&msfr.msfr_group;
-       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+       gsa = (struct sockaddr_in6 *)&msfr.msfr_group;
+       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
                return (EINVAL);
 
        ifnet_head_lock_shared();
@@ -1662,17 +1673,21 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
 
        if (ifp == NULL)
                return (EADDRNOTAVAIL);
-               
+
+       if ((size_t) msfr.msfr_nsrcs >
+           UINT32_MAX / sizeof(struct sockaddr_storage))
+               msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage);
+
        if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc)
                msfr.msfr_nsrcs = in6_mcast_maxsocksrc;
 
-       (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+       (void)in6_setscope(&gsa->sin6_addr, ifp, NULL);
 
        IM6O_LOCK(imo);
        /*
         * Lookup group on the socket.
         */
-       idx = im6o_match_group(imo, ifp, &gsa->sa);
+       idx = im6o_match_group(imo, ifp, gsa);
        if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
                IM6O_UNLOCK(imo);
                return (EADDRNOTAVAIL);
@@ -1703,7 +1718,7 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
                tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
 
        if (tmp_ptr != USER_ADDR_NULL && msfr.msfr_nsrcs > 0) {
-               tss = _MALLOC(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
+               tss = _MALLOC((size_t) msfr.msfr_nsrcs * sizeof(*tss),
                    M_TEMP, M_WAITOK | M_ZERO);
                if (tss == NULL) {
                        IM6O_UNLOCK(imo);
@@ -1738,8 +1753,7 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
        IM6O_UNLOCK(imo);
 
        if (tss != NULL) {
-               error = copyout(tss, tmp_ptr,
-                   sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
+               error = copyout(tss, tmp_ptr, ncsrcs * sizeof(*tss));
                FREE(tss, M_TEMP);
                if (error)
                        return (error);
@@ -1758,7 +1772,7 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
                msfr32.msfr_ifindex = msfr.msfr_ifindex;
                msfr32.msfr_fmode   = msfr.msfr_fmode;
                msfr32.msfr_nsrcs   = msfr.msfr_nsrcs;
-               memcpy(&msfr64.msfr_group, &msfr.msfr_group,
+               memcpy(&msfr32.msfr_group, &msfr.msfr_group,
                    sizeof(struct sockaddr_storage));
                error = sooptcopyout(sopt, &msfr32,
                    sizeof(struct __msfilterreq32));
@@ -1782,9 +1796,9 @@ ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt)
         * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
         * or is a divert socket, reject it.
         */
-       if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
-           (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
-           inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) {
+       if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT ||
+           (SOCK_TYPE(inp->inp_socket) != SOCK_RAW &&
+           SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM)) {
                return (EOPNOTSUPP);
        }
 
@@ -1870,7 +1884,7 @@ in6p_lookup_mcast_ifp(const struct inpcb *in6p,
                return NULL;
 
        if (in6p != NULL && (in6p->inp_flags & INP_BOUND_IF))
-               ifscope = in6p->inp_boundif;
+               ifscope = in6p->inp_boundifp->if_index;
 
        ifp = NULL;
        memset(&ro6, 0, sizeof(struct route_in6));
@@ -1879,8 +1893,8 @@ in6p_lookup_mcast_ifp(const struct inpcb *in6p,
        if (ro6.ro_rt != NULL) {
                ifp = ro6.ro_rt->rt_ifp;
                VERIFY(ifp != NULL);
-               rtfree(ro6.ro_rt);
        }
+       ROUTE_RELEASE(&ro6);
 
        return (ifp);
 }
@@ -1911,7 +1925,7 @@ in6p_lookup_v4addr(struct ipv6_mreq *mreq, struct ip_mreq *v4mreq)
        ifa = ifa_ifpgetprimary(ifp, AF_INET);
        if (ifa == NULL)
                return (EADDRNOTAVAIL);
-       sin = (struct sockaddr_in *)ifa->ifa_addr;
+       sin = (struct sockaddr_in *)(uintptr_t)(size_t)ifa->ifa_addr;
        v4mreq->imr_interface.s_addr = sin->sin_addr.s_addr;
        IFA_REMREF(ifa);
 
@@ -1928,7 +1942,7 @@ static int
 in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
 {
        struct group_source_req          gsr;
-       sockunion_t                     *gsa, *ssa;
+       struct sockaddr_in6             *gsa, *ssa;
        struct ifnet                    *ifp;
        struct in6_mfilter              *imf;
        struct ip6_moptions             *imo;
@@ -1937,17 +1951,17 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
        size_t                           idx;
        int                              error, is_new;
        uint32_t                        scopeid = 0;
+       struct mld_tparams              mtp;
 
+       bzero(&mtp, sizeof (mtp));
        ifp = NULL;
        imf = NULL;
        error = 0;
        is_new = 0;
 
        memset(&gsr, 0, sizeof(struct group_source_req));
-       gsa = (sockunion_t *)&gsr.gsr_group;
-       gsa->ss.ss_family = AF_UNSPEC;
-       ssa = (sockunion_t *)&gsr.gsr_source;
-       ssa->ss.ss_family = AF_UNSPEC;
+       gsa = (struct sockaddr_in6 *)&gsr.gsr_group;
+       ssa = (struct sockaddr_in6 *)&gsr.gsr_source;
 
        /*
         * Chew everything into struct group_source_req.
@@ -1958,7 +1972,6 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
        switch (sopt->sopt_name) {
        case IPV6_JOIN_GROUP: {
                struct ipv6_mreq mreq;
-               struct sockaddr_in6 *gsin6;
 
                error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
                    sizeof(struct ipv6_mreq));
@@ -1985,19 +1998,17 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
 
                        return (inp_join_group(inp, &v4sopt));
                }
-               gsa->sin6.sin6_family = AF_INET6;
-               gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
-               gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
-
-               gsin6 = &gsa->sin6;
+               gsa->sin6_family = AF_INET6;
+               gsa->sin6_len = sizeof(struct sockaddr_in6);
+               gsa->sin6_addr = mreq.ipv6mr_multiaddr;
 
                /* Only allow IPv6 multicast addresses */       
-               if (IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr) == 0) {  
+               if (IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr) == 0) {
                        return (EINVAL);
                }
 
                if (mreq.ipv6mr_interface == 0) {
-                       ifp = in6p_lookup_mcast_ifp(inp, gsin6);
+                       ifp = in6p_lookup_mcast_ifp(inp, gsa);
                } else {
                        ifnet_head_lock_shared();
                        if ((u_int)if_index < mreq.ipv6mr_interface) {
@@ -2007,8 +2018,9 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
                        ifp = ifindex2ifnet[mreq.ipv6mr_interface];
                        ifnet_head_done();
                }
-               MLD_PRINTF(("%s: ipv6mr_interface = %d, ifp = %p\n",
-                   __func__, mreq.ipv6mr_interface, ifp));
+               MLD_PRINTF(("%s: ipv6mr_interface = %d, ifp = 0x%llx\n",
+                   __func__, mreq.ipv6mr_interface,
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
                break;
        }
 
@@ -2026,24 +2038,24 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
                if (error)
                        return (error);
 
-               if (gsa->sin6.sin6_family != AF_INET6 ||
-                   gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+               if (gsa->sin6_family != AF_INET6 ||
+                   gsa->sin6_len != sizeof(struct sockaddr_in6))
                        return (EINVAL);
 
                if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
-                       if (ssa->sin6.sin6_family != AF_INET6 ||
-                           ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+                       if (ssa->sin6_family != AF_INET6 ||
+                           ssa->sin6_len != sizeof(struct sockaddr_in6))
                                return (EINVAL);
-                       if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
+                       if (IN6_IS_ADDR_MULTICAST(&ssa->sin6_addr))
                                return (EINVAL);
                        /*
                         * TODO: Validate embedded scope ID in source
                         * list entry against passed-in ifp, if and only
                         * if source list filter entry is iface or node local.
                         */
-                       in6_clearscope(&ssa->sin6.sin6_addr);
-                       ssa->sin6.sin6_port = 0;
-                       ssa->sin6.sin6_scope_id = 0;
+                       in6_clearscope(&ssa->sin6_addr);
+                       ssa->sin6_port = 0;
+                       ssa->sin6_scope_id = 0;
                }
 
                ifnet_head_lock_shared();
@@ -2060,30 +2072,38 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
                MLD_PRINTF(("%s: unknown sopt_name %d\n",
                    __func__, sopt->sopt_name));
                return (EOPNOTSUPP);
-               break;
        }
 
-       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
                return (EINVAL);
 
        if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
                return (EADDRNOTAVAIL);
 
-       gsa->sin6.sin6_port = 0;
-       gsa->sin6.sin6_scope_id = 0;
+       INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_total);
+       /*
+        * TBD: revisit the criteria for non-OS initiated joins
+        */
+       if (inp->inp_lport == htons(5353)) {
+               INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_mcast_join_os_total);
+       }
+
+       gsa->sin6_port = 0;
+       gsa->sin6_scope_id = 0;
 
        /*
         * Always set the scope zone ID on memberships created from userland.
         * Use the passed-in ifp to do this.
         */
-       (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, &scopeid);
+       (void)in6_setscope(&gsa->sin6_addr, ifp, &scopeid);
        /*
         * Some addresses are not valid without an embedded scopeid.
         * This check must be present because otherwise we will later hit
         * a VERIFY() in in6_mc_join().
         */
-       if ((IN6_IS_ADDR_MC_LINKLOCAL(&gsa->sin6.sin6_addr) ||
-           IN6_IS_ADDR_MC_INTFACELOCAL(&gsa->sin6.sin6_addr)) && scopeid == 0)
+       if ((IN6_IS_ADDR_MC_LINKLOCAL(&gsa->sin6_addr) ||
+           IN6_IS_ADDR_MC_INTFACELOCAL(&gsa->sin6_addr)) &&
+           (scopeid == 0 || gsa->sin6_addr.s6_addr16[1] == 0))
                return (EINVAL);
 
        imo = in6p_findmoptions(inp);
@@ -2091,13 +2111,13 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
                return (ENOMEM);
 
        IM6O_LOCK(imo);
-       idx = im6o_match_group(imo, ifp, &gsa->sa);
+       idx = im6o_match_group(imo, ifp, gsa);
        if (idx == (size_t)-1) {
                is_new = 1;
        } else {
                inm = imo->im6o_membership[idx];
                imf = &imo->im6o_mfilters[idx];
-               if (ssa->ss.ss_family != AF_UNSPEC) {
+               if (ssa->sin6_family != AF_UNSPEC) {
                        /*
                         * MCAST_JOIN_SOURCE_GROUP on an exclusive membership
                         * is an error. On an existing inclusive membership,
@@ -2123,7 +2143,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
                         * full-state SSM API with the delta-based API,
                         * which is discouraged in the relevant RFCs.
                         */
-                       lims = im6o_match_source(imo, idx, &ssa->sa);
+                       lims = im6o_match_source(imo, idx, ssa);
                        if (lims != NULL /*&&
                            lims->im6sl_st[1] == MCAST_INCLUDE*/) {
                                error = EADDRNOTAVAIL;
@@ -2186,7 +2206,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
         * XXX: Should check for non-NULL lims (node exists but may
         * not be in-mode) for interop with full-state API.
         */
-       if (ssa->ss.ss_family != AF_UNSPEC) {
+       if (ssa->sin6_family != AF_UNSPEC) {
                /* Membership starts in IN mode */
                if (is_new) {
                        MLD_PRINTF(("%s: new join w/source\n", __func__);
@@ -2194,7 +2214,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
                } else {
                        MLD_PRINTF(("%s: %s source\n", __func__, "allow"));
                }
-               lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6);
+               lims = im6f_graft(imf, MCAST_INCLUDE, ssa);
                if (lims == NULL) {
                        MLD_PRINTF(("%s: merge imf state failed\n",
                            __func__));
@@ -2214,9 +2234,21 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
         */
 
        if (is_new) {
+               /*
+                * See inp_join_group() for why we need to unlock
+                */
+               IM6O_ADDREF_LOCKED(imo);
+               IM6O_UNLOCK(imo);
+               socket_unlock(inp->inp_socket, 0);
+
                VERIFY(inm == NULL);
-               error = in6_mc_join(ifp, &gsa->sin6.sin6_addr, imf, &inm, 0);
+               error = in6_mc_join(ifp, &gsa->sin6_addr, imf, &inm, 0);
                VERIFY(inm != NULL || error != 0);
+
+               socket_lock(inp->inp_socket, 0);
+               IM6O_REMREF(imo);
+               IM6O_LOCK(imo);
+
                if (error)
                        goto out_im6o_free;
                imo->im6o_membership[idx] = inm; /* from in6_mc_join() */
@@ -2231,7 +2263,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
                        goto out_im6f_rollback;
                }
                MLD_PRINTF(("%s: doing mld downcall\n", __func__));
-               error = mld_change_state(inm, 0);
+               error = mld_change_state(inm, &mtp, 0);
                IN6M_UNLOCK(inm);
                if (error) {
                        MLD_PRINTF(("%s: failed mld downcall\n",
@@ -2261,6 +2293,10 @@ out_im6o_free:
 out_imo_locked:
        IM6O_UNLOCK(imo);
        IM6O_REMREF(imo);       /* from in6p_findmoptions() */
+
+       /* schedule timer now that we've dropped the lock(s) */
+       mld_set_timeout(&mtp);
+
        return (error);
 }
 
@@ -2272,7 +2308,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
 {
        struct ipv6_mreq                 mreq;
        struct group_source_req          gsr;
-       sockunion_t                     *gsa, *ssa;
+       struct sockaddr_in6             *gsa, *ssa;
        struct ifnet                    *ifp;
        struct in6_mfilter              *imf;
        struct ip6_moptions             *imo;
@@ -2281,16 +2317,16 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
        uint32_t                         ifindex = 0;
        size_t                           idx;
        int                              error, is_final;
+       struct mld_tparams               mtp;
 
+       bzero(&mtp, sizeof (mtp));
        ifp = NULL;
        error = 0;
        is_final = 1;
 
        memset(&gsr, 0, sizeof(struct group_source_req));
-       gsa = (sockunion_t *)&gsr.gsr_group;
-       gsa->ss.ss_family = AF_UNSPEC;
-       ssa = (sockunion_t *)&gsr.gsr_source;
-       ssa->ss.ss_family = AF_UNSPEC;
+       gsa = (struct sockaddr_in6 *)&gsr.gsr_group;
+       ssa = (struct sockaddr_in6 *)&gsr.gsr_source;
 
        /*
         * Chew everything passed in up into a struct group_source_req
@@ -2301,7 +2337,6 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
         */
        switch (sopt->sopt_name) {
        case IPV6_LEAVE_GROUP: {
-               struct sockaddr_in6 *gsin6;
 
                error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
                    sizeof(struct ipv6_mreq));
@@ -2328,15 +2363,14 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
 
                        return (inp_leave_group(inp, &v4sopt));
                }
-               gsa->sin6.sin6_family = AF_INET6;
-               gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
-               gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
-               gsa->sin6.sin6_port = 0;
-               gsa->sin6.sin6_scope_id = 0;
+               gsa->sin6_family = AF_INET6;
+               gsa->sin6_len = sizeof(struct sockaddr_in6);
+               gsa->sin6_addr = mreq.ipv6mr_multiaddr;
+               gsa->sin6_port = 0;
+               gsa->sin6_scope_id = 0;
                ifindex = mreq.ipv6mr_interface;
-               gsin6 = &gsa->sin6;
                /* Only allow IPv6 multicast addresses */       
-               if (IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr) == 0) {  
+               if (IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr) == 0) {
                        return (EINVAL);
                }
                break;
@@ -2356,24 +2390,24 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
                if (error)
                        return (error);
 
-               if (gsa->sin6.sin6_family != AF_INET6 ||
-                   gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+               if (gsa->sin6_family != AF_INET6 ||
+                   gsa->sin6_len != sizeof(struct sockaddr_in6))
                        return (EINVAL);
                if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
-                       if (ssa->sin6.sin6_family != AF_INET6 ||
-                           ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
+                       if (ssa->sin6_family != AF_INET6 ||
+                           ssa->sin6_len != sizeof(struct sockaddr_in6))
                                return (EINVAL);
-                       if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
+                       if (IN6_IS_ADDR_MULTICAST(&ssa->sin6_addr))
                                return (EINVAL);
                        /*
                         * TODO: Validate embedded scope ID in source
                         * list entry against passed-in ifp, if and only
                         * if source list filter entry is iface or node local.
                         */
-                       in6_clearscope(&ssa->sin6.sin6_addr);
+                       in6_clearscope(&ssa->sin6_addr);
                }
-               gsa->sin6.sin6_port = 0;
-               gsa->sin6.sin6_scope_id = 0;
+               gsa->sin6_port = 0;
+               gsa->sin6_scope_id = 0;
                ifindex = gsr.gsr_interface;
                break;
 
@@ -2381,10 +2415,9 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
                MLD_PRINTF(("%s: unknown sopt_name %d\n",
                    __func__, sopt->sopt_name));
                return (EOPNOTSUPP);
-               break;
        }
 
-       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
                return (EINVAL);
 
        /*
@@ -2404,9 +2437,9 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
                ifnet_head_done();
                if (ifp == NULL)
                        return (EADDRNOTAVAIL);
-               (void) in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+               (void) in6_setscope(&gsa->sin6_addr, ifp, NULL);
        } else {
-               error = sa6_embedscope(&gsa->sin6, ip6_use_defzone);
+               error = sa6_embedscope(gsa, ip6_use_defzone);
                if (error)
                        return (EADDRNOTAVAIL);
                /*
@@ -2419,13 +2452,15 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
                 * directly until such time as this implementation is
                 * refactored, assuming the scope IDs are the way to go.
                 */
-               ifindex = ntohs(gsa->sin6.sin6_addr.s6_addr16[1]);
+               ifindex = ntohs(gsa->sin6_addr.s6_addr16[1]);
                if (ifindex == 0) {
                        MLD_PRINTF(("%s: warning: no ifindex, looking up "
                            "ifp for group %s.\n", __func__,
-                           ip6_sprintf(&gsa->sin6.sin6_addr)));
-                       ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6);
+                           ip6_sprintf(&gsa->sin6_addr)));
+                       ifp = in6p_lookup_mcast_ifp(inp, gsa);
                } else {
+                       if (!IF_INDEX_IN_RANGE(ifindex))
+                               return (EADDRNOTAVAIL);
                        ifnet_head_lock_shared();
                        ifp = ifindex2ifnet[ifindex];
                        ifnet_head_done();
@@ -2435,7 +2470,8 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
        }
 
        VERIFY(ifp != NULL);
-       MLD_PRINTF(("%s: ifp = %p\n", __func__, ifp));
+       MLD_PRINTF(("%s: ifp = 0x%llx\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(ifp)));
 
        /*
         * Find the membership in the membership array.
@@ -2445,7 +2481,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
                return (ENOMEM);
 
        IM6O_LOCK(imo);
-       idx = im6o_match_group(imo, ifp, &gsa->sa);
+       idx = im6o_match_group(imo, ifp, gsa);
        if (idx == (size_t)-1) {
                error = EADDRNOTAVAIL;
                goto out_locked;
@@ -2453,7 +2489,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
        inm = imo->im6o_membership[idx];
        imf = &imo->im6o_mfilters[idx];
 
-       if (ssa->ss.ss_family != AF_UNSPEC)
+       if (ssa->sin6_family != AF_UNSPEC)
                is_final = 0;
 
        /*
@@ -2471,16 +2507,16 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
                        error = EADDRNOTAVAIL;
                        goto out_locked;
                }
-               ims = im6o_match_source(imo, idx, &ssa->sa);
+               ims = im6o_match_source(imo, idx, ssa);
                if (ims == NULL) {
-                       MLD_PRINTF(("%s: source %p %spresent\n", __func__,
-                           ip6_sprintf(&ssa->sin6.sin6_addr),
+                       MLD_PRINTF(("%s: source %s %spresent\n", __func__,
+                           ip6_sprintf(&ssa->sin6_addr),
                            "not "));
                        error = EADDRNOTAVAIL;
                        goto out_locked;
                }
                MLD_PRINTF(("%s: %s source\n", __func__, "block"));
-               error = im6f_prune(imf, &ssa->sin6);
+               error = im6f_prune(imf, ssa);
                if (error) {
                        MLD_PRINTF(("%s: merge imf state failed\n",
                            __func__));
@@ -2511,7 +2547,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
                }
 
                MLD_PRINTF(("%s: doing mld downcall\n", __func__));
-               error = mld_change_state(inm, 0);
+               error = mld_change_state(inm, &mtp, 0);
                if (error) {
                        MLD_PRINTF(("%s: failed mld downcall\n", __func__));
                }
@@ -2530,7 +2566,20 @@ out_im6f_rollback:
                /* Remove the gap in the membership array. */
                VERIFY(inm == imo->im6o_membership[idx]);
                imo->im6o_membership[idx] = NULL;
+
+               /*
+                * See inp_join_group() for why we need to unlock
+                */
+               IM6O_ADDREF_LOCKED(imo);
+               IM6O_UNLOCK(imo);
+               socket_unlock(inp->inp_socket, 0);
+
                IN6M_REMREF(inm);
+
+               socket_lock(inp->inp_socket, 0);
+               IM6O_REMREF(imo);
+               IM6O_LOCK(imo);
+
                for (++idx; idx < imo->im6o_num_memberships; ++idx) {
                        imo->im6o_membership[idx-1] = imo->im6o_membership[idx];
                        imo->im6o_mfilters[idx-1] = imo->im6o_mfilters[idx];
@@ -2541,6 +2590,10 @@ out_im6f_rollback:
 out_locked:
        IM6O_UNLOCK(imo);
        IM6O_REMREF(imo);       /* from in6p_findmoptions() */
+
+       /* schedule timer now that we've dropped the lock(s) */
+       mld_set_timeout(&mtp);
+
        return (error);
 }
 
@@ -2597,16 +2650,19 @@ in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
 static int
 in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
 {
-       struct __msfilterreq64   msfr, msfr64;
+       struct __msfilterreq64   msfr = {}, msfr64;
        struct __msfilterreq32   msfr32;
-       sockunion_t             *gsa;
+       struct sockaddr_in6     *gsa;
        struct ifnet            *ifp;
        struct in6_mfilter      *imf;
        struct ip6_moptions     *imo;
        struct in6_multi        *inm;
        size_t                   idx;
        int                      error;
-       user_addr_t              tmp_ptr;
+       user_addr_t              tmp_ptr;
+       struct mld_tparams       mtp;
+
+       bzero(&mtp, sizeof (mtp));
 
        if (IS_64BIT_PROCESS(current_proc())) {
                error = sooptcopyin(sopt, &msfr64,
@@ -2626,6 +2682,10 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
                memcpy(&msfr, &msfr32, sizeof(msfr));
        }
 
+       if ((size_t) msfr.msfr_nsrcs >
+           UINT32_MAX / sizeof(struct sockaddr_storage))
+               msfr.msfr_nsrcs = UINT32_MAX / sizeof(struct sockaddr_storage);
+
        if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc)
                return (ENOBUFS);
 
@@ -2637,11 +2697,11 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
            msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6))
                return (EINVAL);
 
-       gsa = (sockunion_t *)&msfr.msfr_group;
-       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
+       gsa = (struct sockaddr_in6 *)&msfr.msfr_group;
+       if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6_addr))
                return (EINVAL);
 
-       gsa->sin6.sin6_port = 0;        /* ignore port */
+       gsa->sin6_port = 0;     /* ignore port */
 
        ifnet_head_lock_shared();
        if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
@@ -2653,7 +2713,7 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
        if (ifp == NULL)
                return (EADDRNOTAVAIL);
 
-       (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
+       (void)in6_setscope(&gsa->sin6_addr, ifp, NULL);
 
        /*
         * Take the INP write lock.
@@ -2664,7 +2724,7 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
                return (ENOMEM);
 
        IM6O_LOCK(imo);
-       idx = im6o_match_group(imo, ifp, &gsa->sa);
+       idx = im6o_match_group(imo, ifp, gsa);
        if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
                error = EADDRNOTAVAIL;
                goto out_imo_locked;
@@ -2697,7 +2757,7 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
 
                MLD_PRINTF(("%s: loading %lu source list entries\n",
                    __func__, (unsigned long)msfr.msfr_nsrcs));
-               kss = _MALLOC(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
+               kss = _MALLOC((size_t) msfr.msfr_nsrcs * sizeof(*kss),
                    M_TEMP, M_WAITOK);
                if (kss == NULL) {
                        error = ENOMEM;
@@ -2705,7 +2765,7 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
                }
 
                error = copyin(tmp_ptr, kss,
-                   sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
+                   (size_t) msfr.msfr_nsrcs * sizeof(*kss));
                if (error) {
                        FREE(kss, M_TEMP);
                        goto out_imo_locked;
@@ -2774,7 +2834,7 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
        }
 
        MLD_PRINTF(("%s: doing mld downcall\n", __func__));
-       error = mld_change_state(inm, 0);
+       error = mld_change_state(inm, &mtp, 0);
        IN6M_UNLOCK(inm);
 #if MLD_DEBUG
        if (error)
@@ -2793,6 +2853,9 @@ out_imo_locked:
        IM6O_UNLOCK(imo);
        IM6O_REMREF(imo);       /* from in6p_findmoptions() */
 
+       /* schedule timer now that we've dropped the lock(s) */
+       mld_set_timeout(&mtp);
+
        return (error);
 }
 
@@ -2818,9 +2881,9 @@ ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt)
         * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
         * or is a divert socket, reject it.
         */
-       if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
-           (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
-            inp->inp_socket->so_proto->pr_type != SOCK_DGRAM))
+       if (SOCK_PROTO(inp->inp_socket) == IPPROTO_DIVERT ||
+           (SOCK_TYPE(inp->inp_socket) != SOCK_RAW &&
+            SOCK_TYPE(inp->inp_socket) != SOCK_DGRAM))
                return (EOPNOTSUPP);
 
        switch (sopt->sopt_name) {
@@ -2992,7 +3055,8 @@ sysctl_ip6_mcast_filters SYSCTL_HANDLER_ARGS
                        break;          /* abort */
                }
                RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) {
-                       MLD_PRINTF(("%s: visit node %p\n", __func__, ims));
+                       MLD_PRINTF(("%s: visit node 0x%llx\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(ims)));
                        /*
                         * Only copy-out sources which are in-mode.
                         */
@@ -3138,10 +3202,6 @@ in6_multi_attach(struct in6_multi *in6m)
                panic("%s: Attempt to attach an already attached in6m=%p",
                    __func__, in6m);
                /* NOTREACHED */
-       } else if (in6m->in6m_debug & IFD_TRASHED) {
-               panic("%s: Attempt to reattach a detached in6m=%p",
-                   __func__, in6m);
-               /* NOTREACHED */
        }
 
        in6m->in6m_reqcnt++;
@@ -3359,7 +3419,10 @@ in6_multihead_lock_shared(void)
 void
 in6_multihead_lock_assert(int what)
 {
-       lck_rw_assert(&in6_multihead_lock, what);
+#if !MACH_ASSERT
+#pragma unused(what)
+#endif
+       LCK_RW_ASSERT(&in6_multihead_lock, what);
 }
 
 void
@@ -3421,6 +3484,7 @@ in6m_mode_str(const int mode)
 static const char *in6m_statestrs[] = {
        "not-member\n",
        "silent\n",
+       "reporting\n",
        "idle\n",
        "lazy\n",
        "sleeping\n",
@@ -3446,25 +3510,25 @@ in6m_print(const struct in6_multi *inm)
 {
        int t;
 
-       IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm));
+       IN6M_LOCK_ASSERT_HELD(__DECONST(struct in6_multi *, inm));
 
        if (mld_debug == 0)
                return;
 
-       printf("%s: --- begin in6m %p ---\n", __func__, inm);
-       printf("addr %s ifp %p(%s%d) ifma %p\n",
+       printf("%s: --- begin in6m 0x%llx ---\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(inm));
+       printf("addr %s ifp 0x%llx(%s) ifma 0x%llx\n",
            ip6_sprintf(&inm->in6m_addr),
-           inm->in6m_ifp,
-           inm->in6m_ifp->if_name,
-           inm->in6m_ifp->if_unit,
-           inm->in6m_ifma);
+           (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
+           if_name(inm->in6m_ifp),
+           (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifma));
        printf("timer %u state %s refcount %u scq.len %u\n",
            inm->in6m_timer,
            in6m_state_str(inm->in6m_state),
            inm->in6m_refcount,
            inm->in6m_scq.ifq_len);
-       printf("mli %p nsrc %lu sctimer %u scrv %u\n",
-           inm->in6m_mli,
+       printf("mli 0x%llx nsrc %lu sctimer %u scrv %u\n",
+           (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_mli),
            inm->in6m_nsrc,
            inm->in6m_sctimer,
            inm->in6m_scrv);
@@ -3476,7 +3540,8 @@ in6m_print(const struct in6_multi *inm)
                    inm->in6m_st[t].iss_in,
                    inm->in6m_st[t].iss_rec);
        }
-       printf("%s: --- end in6m %p ---\n", __func__, inm);
+       printf("%s: --- end in6m 0x%llx ---\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(inm));
 }
 
 #else