]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/mld6.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / bsd / netinet6 / mld6.c
index 11f02db1e2209a8598e75ec4ee1c4b6673450d7b..60b7777cded0d469975979fb8cca3b803ef4d57a 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*-
 #include <sys/malloc.h>
 #include <sys/mcache.h>
 
+#include <dev/random/randomdev.h>
+
 #include <kern/zalloc.h>
 
 #include <net/if.h>
 #include <netinet6/mld6.h>
 #include <netinet6/mld6_var.h>
 
-/* Lock group and attribute for mld6_mtx */
+/* Lock group and attribute for mld_mtx */
 static lck_attr_t       *mld_mtx_attr;
 static lck_grp_t        *mld_mtx_grp;
 static lck_grp_attr_t   *mld_mtx_grp_attr;
@@ -153,7 +155,7 @@ static lck_grp_attr_t   *mld_mtx_grp_attr;
  * the state changes to MLD_LEAVING_MEMBER, and later dropped in the timeout
  * handler.  Each in6_multi holds a reference to the underlying mld_ifinfo.
  *
- * Thus, the permitted lock oder is:
+ * Thus, the permitted lock order is:
  *
  *     mld_mtx, in6_multihead_lock, inm6_lock, mli_lock
  *
@@ -164,109 +166,101 @@ static decl_lck_mtx_data(, mld_mtx);
 
 SLIST_HEAD(mld_in6m_relhead, in6_multi);
 
-static void    mli_initvar(struct mld_ifinfo *, struct ifnet *, int);
+static void     mli_initvar(struct mld_ifinfo *, struct ifnet *, int);
 static struct mld_ifinfo *mli_alloc(int);
-static void    mli_free(struct mld_ifinfo *);
-static void    mli_delete(const struct ifnet *, struct mld_in6m_relhead *);
-static void    mld_dispatch_packet(struct mbuf *);
-static void    mld_final_leave(struct in6_multi *, struct mld_ifinfo *);
-static int     mld_handle_state_change(struct in6_multi *,
-                   struct mld_ifinfo *);
-static int     mld_initial_join(struct in6_multi *, struct mld_ifinfo *,
-                   const int);
+static void     mli_free(struct mld_ifinfo *);
+static void     mli_delete(const struct ifnet *, struct mld_in6m_relhead *);
+static void     mld_dispatch_packet(struct mbuf *);
+static void     mld_final_leave(struct in6_multi *, struct mld_ifinfo *,
+    struct mld_tparams *);
+static int      mld_handle_state_change(struct in6_multi *, struct mld_ifinfo *,
+    struct mld_tparams *);
+static int      mld_initial_join(struct in6_multi *, struct mld_ifinfo *,
+    struct mld_tparams *, const int);
 #ifdef MLD_DEBUG
-static const char *    mld_rec_type_to_str(const int);
+static const char *     mld_rec_type_to_str(const int);
 #endif
-static void    mld_set_version(struct mld_ifinfo *, const int);
-static void    mld_flush_relq(struct mld_ifinfo *, struct mld_in6m_relhead *);
-static void    mld_dispatch_queue(struct mld_ifinfo *, struct ifqueue *, int);
-static int     mld_v1_input_query(struct ifnet *, const struct ip6_hdr *,
-                   /*const*/ struct mld_hdr *);
-static int     mld_v1_input_report(struct ifnet *, const struct ip6_hdr *,
-                   /*const*/ struct mld_hdr *);
-static void    mld_v1_process_group_timer(struct in6_multi *, const int);
-static void    mld_v1_process_querier_timers(struct mld_ifinfo *);
-static int     mld_v1_transmit_report(struct in6_multi *, const int);
-static void    mld_v1_update_group(struct in6_multi *, const int);
-static void    mld_v2_cancel_link_timers(struct mld_ifinfo *);
-static void    mld_v2_dispatch_general_query(struct mld_ifinfo *);
+static uint32_t mld_set_version(struct mld_ifinfo *, const int);
+static void     mld_flush_relq(struct mld_ifinfo *, struct mld_in6m_relhead *);
+static void     mld_dispatch_queue_locked(struct mld_ifinfo *, struct ifqueue *, int);
+static int      mld_v1_input_query(struct ifnet *, const struct ip6_hdr *,
+    /*const*/ struct mld_hdr *);
+static int      mld_v1_input_report(struct ifnet *, struct mbuf *,
+    const struct ip6_hdr *, /*const*/ struct mld_hdr *);
+static void     mld_v1_process_group_timer(struct in6_multi *, const int);
+static void     mld_v1_process_querier_timers(struct mld_ifinfo *);
+static int      mld_v1_transmit_report(struct in6_multi *, const int);
+static uint32_t mld_v1_update_group(struct in6_multi *, const int);
+static void     mld_v2_cancel_link_timers(struct mld_ifinfo *);
+static uint32_t mld_v2_dispatch_general_query(struct mld_ifinfo *);
 static struct mbuf *
-               mld_v2_encap_report(struct ifnet *, struct mbuf *);
-static int     mld_v2_enqueue_filter_change(struct ifqueue *,
-                   struct in6_multi *);
-static int     mld_v2_enqueue_group_record(struct ifqueue *,
-                   struct in6_multi *, const int, const int, const int,
-                   const int);
-static int     mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
-                   struct mbuf *, const int, const int);
-static int     mld_v2_merge_state_changes(struct in6_multi *,
-                   struct ifqueue *);
-static void    mld_v2_process_group_timers(struct mld_ifinfo *,
-                   struct ifqueue *, struct ifqueue *,
-                   struct in6_multi *, const int);
-static int     mld_v2_process_group_query(struct in6_multi *,
-                   int, struct mbuf *, const int);
-static int     sysctl_mld_gsr SYSCTL_HANDLER_ARGS;
-static int     sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS;
+mld_v2_encap_report(struct ifnet *, struct mbuf *);
+static int      mld_v2_enqueue_filter_change(struct ifqueue *,
+    struct in6_multi *);
+static int      mld_v2_enqueue_group_record(struct ifqueue *,
+    struct in6_multi *, const int, const int, const int,
+    const int);
+static int      mld_v2_input_query(struct ifnet *, const struct ip6_hdr *,
+    struct mbuf *, const int, const int);
+static int      mld_v2_merge_state_changes(struct in6_multi *,
+    struct ifqueue *);
+static void     mld_v2_process_group_timers(struct mld_ifinfo *,
+    struct ifqueue *, struct ifqueue *,
+    struct in6_multi *, const int);
+static int      mld_v2_process_group_query(struct in6_multi *,
+    int, struct mbuf *, const int);
+static int      sysctl_mld_gsr SYSCTL_HANDLER_ARGS;
+static int      sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS;
+static int      sysctl_mld_v2enable SYSCTL_HANDLER_ARGS;
+
+static int mld_timeout_run;             /* MLD timer is scheduled to run */
+static void mld_timeout(void *);
+static void mld_sched_timeout(void);
 
 /*
  * Normative references: RFC 2710, RFC 3590, RFC 3810.
- *
- *  XXX LOR PREVENTION
- *  A special case for IPv6 is the in6_setscope() routine. ip6_output()
- *  will not accept an ifp; it wants an embedded scope ID, unlike
- *  ip_output(), which happily takes the ifp given to it. The embedded
- *  scope ID is only used by MLD to select the outgoing interface.
- *
- *  As such, we exploit the fact that the scope ID is just the interface
- *  index, and embed it in the IPv6 destination address accordingly.
- *  This is potentially NOT VALID for MLDv1 reports, as they
- *  are always sent to the multicast group itself; as MLDv2
- *  reports are always sent to ff02::16, this is not an issue
- *  when MLDv2 is in use.
  */
-
-#define        MLD_EMBEDSCOPE(pin6, zoneid) \
-       (pin6)->s6_addr16[1] = htons((zoneid) & 0xFFFF)
-
 static struct timeval mld_gsrdelay = {10, 0};
 static LIST_HEAD(, mld_ifinfo) mli_head;
 
+static int querier_present_timers_running6;
 static int interface_timers_running6;
 static int state_change_timers_running6;
 static int current_state_timers_running6;
 
-static decl_lck_mtx_data(, mld6_mtx);
-
-#define        MLD_LOCK()                      \
-       lck_mtx_lock(&mld6_mtx)
-#define        MLD_LOCK_ASSERT_HELD()          \
-       lck_mtx_assert(&mld6_mtx, LCK_MTX_ASSERT_OWNED)
-#define        MLD_LOCK_ASSERT_NOTHELD()       \
-       lck_mtx_assert(&mld6_mtx, LCK_MTX_ASSERT_NOTOWNED)
-#define        MLD_UNLOCK()                    \
-       lck_mtx_unlock(&mld6_mtx)
-
-#define        MLD_ADD_DETACHED_IN6M(_head, _in6m) {                           \
-       SLIST_INSERT_HEAD(_head, _in6m, in6m_dtle);                     \
+static unsigned int mld_mli_list_genid;
+/*
+ * Subsystem lock macros.
+ */
+#define MLD_LOCK()                      \
+       lck_mtx_lock(&mld_mtx)
+#define MLD_LOCK_ASSERT_HELD()          \
+       LCK_MTX_ASSERT(&mld_mtx, LCK_MTX_ASSERT_OWNED)
+#define MLD_LOCK_ASSERT_NOTHELD()       \
+       LCK_MTX_ASSERT(&mld_mtx, LCK_MTX_ASSERT_NOTOWNED)
+#define MLD_UNLOCK()                    \
+       lck_mtx_unlock(&mld_mtx)
+
+#define MLD_ADD_DETACHED_IN6M(_head, _in6m) {                           \
+       SLIST_INSERT_HEAD(_head, _in6m, in6m_dtle);                     \
 }
 
-#define        MLD_REMOVE_DETACHED_IN6M(_head) {                               \
-       struct in6_multi *_in6m, *_inm_tmp;                             \
-       SLIST_FOREACH_SAFE(_in6m, _head, in6m_dtle, _inm_tmp) {         \
-               SLIST_REMOVE(_head, _in6m, in6_multi, in6m_dtle);       \
-               IN6M_REMREF(_in6m);                                     \
-       }                                                               \
-       VERIFY(SLIST_EMPTY(_head));                                     \
+#define MLD_REMOVE_DETACHED_IN6M(_head) {                               \
+       struct in6_multi *_in6m, *_inm_tmp;                             \
+       SLIST_FOREACH_SAFE(_in6m, _head, in6m_dtle, _inm_tmp) {         \
+               SLIST_REMOVE(_head, _in6m, in6_multi, in6m_dtle);       \
+               IN6M_REMREF(_in6m);                                     \
+       }                                                               \
+       VERIFY(SLIST_EMPTY(_head));                                     \
 }
 
-#define        MLI_ZONE_MAX            64              /* maximum elements in zone */
-#define        MLI_ZONE_NAME           "mld_ifinfo"    /* zone name */
+#define MLI_ZONE_MAX            64              /* maximum elements in zone */
+#define MLI_ZONE_NAME           "mld_ifinfo"    /* zone name */
 
-static unsigned int mli_size;                  /* size of zone element */
-static struct zone *mli_zone;                  /* zone for mld_ifinfo */
+static unsigned int mli_size;                   /* size of zone element */
+static struct zone *mli_zone;                   /* zone for mld_ifinfo */
 
-SYSCTL_DECL(_net_inet6);       /* Note: Not in any common header. */
+SYSCTL_DECL(_net_inet6);        /* Note: Not in any common header. */
 
 SYSCTL_NODE(_net_inet6, OID_AUTO, mld, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
     "IPv6 Multicast Listener Discovery");
@@ -276,28 +270,34 @@ SYSCTL_PROC(_net_inet6_mld, OID_AUTO, gsrdelay,
     "Rate limit for MLDv2 Group-and-Source queries in seconds");
 
 SYSCTL_NODE(_net_inet6_mld, OID_AUTO, ifinfo, CTLFLAG_RD | CTLFLAG_LOCKED,
-   sysctl_mld_ifinfo, "Per-interface MLDv2 state");
+    sysctl_mld_ifinfo, "Per-interface MLDv2 state");
 
-static int     mld_v1enable = 1;
+static int      mld_v1enable = 1;
 SYSCTL_INT(_net_inet6_mld, OID_AUTO, v1enable, CTLFLAG_RW | CTLFLAG_LOCKED,
     &mld_v1enable, 0, "Enable fallback to MLDv1");
 
-static int     mld_use_allow = 1;
+static int      mld_v2enable = 1;
+SYSCTL_PROC(_net_inet6_mld, OID_AUTO, v2enable,
+    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
+    &mld_v2enable, 0, sysctl_mld_v2enable, "I",
+    "Enable MLDv2 (debug purposes only)");
+
+static int      mld_use_allow = 1;
 SYSCTL_INT(_net_inet6_mld, OID_AUTO, use_allow, CTLFLAG_RW | CTLFLAG_LOCKED,
     &mld_use_allow, 0, "Use ALLOW/BLOCK for RFC 4604 SSM joins/leaves");
 
 #ifdef MLD_DEBUG
 int mld_debug = 0;
 SYSCTL_INT(_net_inet6_mld, OID_AUTO,
-       debug, CTLFLAG_RW | CTLFLAG_LOCKED,     &mld_debug, 0, "");
+    debug, CTLFLAG_RW | CTLFLAG_LOCKED, &mld_debug, 0, "");
 #endif
 /*
  * Packed Router Alert option structure declaration.
  */
 struct mld_raopt {
-       struct ip6_hbh          hbh;
-       struct ip6_opt          pad;
-       struct ip6_opt_router   ra;
+       struct ip6_hbh          hbh;
+       struct ip6_opt          pad;
+       struct ip6_opt_router   ra;
 } __packed;
 
 /*
@@ -307,14 +307,39 @@ static struct mld_raopt mld_ra = {
        .hbh = { 0, 0 },
        .pad = { .ip6o_type = IP6OPT_PADN, 0 },
        .ra = {
-           .ip6or_type = (u_int8_t)IP6OPT_ROUTER_ALERT,
-           .ip6or_len = (u_int8_t)(IP6OPT_RTALERT_LEN - 2),
-           .ip6or_value =  {((IP6OPT_RTALERT_MLD >> 8) & 0xFF),
-               (IP6OPT_RTALERT_MLD & 0xFF) }
+               .ip6or_type = (u_int8_t)IP6OPT_ROUTER_ALERT,
+               .ip6or_len = (u_int8_t)(IP6OPT_RTALERT_LEN - 2),
+               .ip6or_value =  {((IP6OPT_RTALERT_MLD >> 8) & 0xFF),
+                                (IP6OPT_RTALERT_MLD & 0xFF) }
        }
 };
 static struct ip6_pktopts mld_po;
 
+/* Store MLDv2 record count in the module private scratch space */
+#define vt_nrecs        pkt_mpriv.__mpriv_u.__mpriv32[0].__mpriv32_u.__val16[0]
+
+static __inline void
+mld_save_context(struct mbuf *m, struct ifnet *ifp)
+{
+       m->m_pkthdr.rcvif = ifp;
+}
+
+static __inline void
+mld_scrub_context(struct mbuf *m)
+{
+       m->m_pkthdr.rcvif = NULL;
+}
+
+/*
+ * Restore context from a queued output chain.
+ * Return saved ifp.
+ */
+static __inline struct ifnet *
+mld_restore_context(struct mbuf *m)
+{
+       return m->m_pkthdr.rcvif;
+}
+
 /*
  * Retrieve or set threshold between group-source queries in seconds.
  */
@@ -330,8 +355,9 @@ sysctl_mld_gsr SYSCTL_HANDLER_ARGS
        i = mld_gsrdelay.tv_sec;
 
        error = sysctl_handle_int(oidp, &i, 0, req);
-       if (error || !req->newptr)
+       if (error || !req->newptr) {
                goto out_locked;
+       }
 
        if (i < -1 || i >= 60) {
                error = EINVAL;
@@ -342,7 +368,7 @@ sysctl_mld_gsr SYSCTL_HANDLER_ARGS
 
 out_locked:
        MLD_UNLOCK();
-       return (error);
+       return error;
 }
 /*
  * Expose struct mld_ifinfo to userland, keyed by ifindex.
@@ -353,21 +379,23 @@ static int
 sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS
 {
 #pragma unused(oidp)
-       int                     *name;
-       int                      error;
-       u_int                    namelen;
-       struct ifnet            *ifp;
-       struct mld_ifinfo       *mli;
-       struct mld_ifinfo_u     mli_u;
+       int                     *name;
+       int                      error;
+       u_int                    namelen;
+       struct ifnet            *ifp;
+       struct mld_ifinfo       *mli;
+       struct mld_ifinfo_u     mli_u;
 
        name = (int *)arg1;
        namelen = arg2;
 
-       if (req->newptr != USER_ADDR_NULL)
-               return (EPERM);
+       if (req->newptr != USER_ADDR_NULL) {
+               return EPERM;
+       }
 
-       if (namelen != 1)
-               return (EINVAL);
+       if (namelen != 1) {
+               return EINVAL;
+       }
 
        MLD_LOCK();
 
@@ -381,10 +409,11 @@ sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS
        ifnet_head_lock_shared();
        ifp = ifindex2ifnet[name[0]];
        ifnet_head_done();
-       if (ifp == NULL)
+       if (ifp == NULL) {
                goto out_locked;
+       }
 
-       bzero(&mli_u, sizeof (mli_u));
+       bzero(&mli_u, sizeof(mli_u));
 
        LIST_FOREACH(mli, &mli_head, mli_link) {
                MLI_LOCK(mli);
@@ -404,44 +433,115 @@ sysctl_mld_ifinfo SYSCTL_HANDLER_ARGS
                mli_u.mli_uri = mli->mli_uri;
                MLI_UNLOCK(mli);
 
-               error = SYSCTL_OUT(req, &mli_u, sizeof (mli_u));
+               error = SYSCTL_OUT(req, &mli_u, sizeof(mli_u));
                break;
        }
 
 out_locked:
        MLD_UNLOCK();
-       return (error);
+       return error;
+}
+
+static int
+sysctl_mld_v2enable SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+       int error;
+       int i;
+       struct mld_ifinfo *mli;
+       struct mld_tparams mtp = { 0, 0, 0, 0 };
+
+       MLD_LOCK();
+
+       i = mld_v2enable;
+
+       error = sysctl_handle_int(oidp, &i, 0, req);
+       if (error || !req->newptr) {
+               goto out_locked;
+       }
+
+       if (i < 0 || i > 1) {
+               error = EINVAL;
+               goto out_locked;
+       }
+
+       mld_v2enable = i;
+       /*
+        * If we enabled v2, the state transition will take care of upgrading
+        * the MLD version back to v2. Otherwise, we have to explicitly
+        * downgrade. Note that this functionality is to be used for debugging.
+        */
+       if (mld_v2enable == 1) {
+               goto out_locked;
+       }
+
+       LIST_FOREACH(mli, &mli_head, mli_link) {
+               MLI_LOCK(mli);
+               if (mld_set_version(mli, MLD_VERSION_1) > 0) {
+                       mtp.qpt = 1;
+               }
+               MLI_UNLOCK(mli);
+       }
+
+out_locked:
+       MLD_UNLOCK();
+
+       mld_set_timeout(&mtp);
+
+       return error;
 }
 
 /*
  * Dispatch an entire queue of pending packet chains.
  *
  * Must not be called with in6m_lock held.
+ * XXX This routine unlocks MLD global lock and also mli locks.
+ * Make sure that the calling routine takes reference on the mli
+ * before calling this routine.
+ * Also if we are traversing mli_head, remember to check for
+ * mli list generation count and restart the loop if generation count
+ * has changed.
  */
 static void
-mld_dispatch_queue(struct mld_ifinfo *mli, struct ifqueue *ifq, int limit)
+mld_dispatch_queue_locked(struct mld_ifinfo *mli, struct ifqueue *ifq, int limit)
 {
        struct mbuf *m;
 
-       if (mli != NULL)
+       MLD_LOCK_ASSERT_HELD();
+
+       if (mli != NULL) {
                MLI_LOCK_ASSERT_HELD(mli);
+       }
 
        for (;;) {
                IF_DEQUEUE(ifq, m);
-               if (m == NULL)
+               if (m == NULL) {
                        break;
-               MLD_PRINTF(("%s: dispatch %p from %p\n", __func__, ifq, m));
-               if (mli != NULL)
+               }
+               MLD_PRINTF(("%s: dispatch 0x%llx from 0x%llx\n", __func__,
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifq),
+                   (uint64_t)VM_KERNEL_ADDRPERM(m)));
+
+               if (mli != NULL) {
                        MLI_UNLOCK(mli);
+               }
+               MLD_UNLOCK();
+
                mld_dispatch_packet(m);
-               if (mli != NULL)
+
+               MLD_LOCK();
+               if (mli != NULL) {
                        MLI_LOCK(mli);
-               if (--limit == 0)
+               }
+
+               if (--limit == 0) {
                        break;
+               }
        }
 
-       if (mli != NULL)
+       if (mli != NULL) {
                MLI_LOCK_ASSERT_HELD(mli);
+       }
 }
 
 /*
@@ -459,20 +559,21 @@ mld_dispatch_queue(struct mld_ifinfo *mli, struct ifqueue *ifq, int limit)
 static __inline__ int
 mld_is_addr_reported(const struct in6_addr *addr)
 {
-
        VERIFY(IN6_IS_ADDR_MULTICAST(addr));
 
-       if (IPV6_ADDR_MC_SCOPE(addr) == IPV6_ADDR_SCOPE_NODELOCAL)
-               return (0);
+       if (IPV6_ADDR_MC_SCOPE(addr) == IPV6_ADDR_SCOPE_NODELOCAL) {
+               return 0;
+       }
 
        if (IPV6_ADDR_MC_SCOPE(addr) == IPV6_ADDR_SCOPE_LINKLOCAL) {
                struct in6_addr tmp = *addr;
                in6_clearscope(&tmp);
-               if (IN6_ARE_ADDR_EQUAL(&tmp, &in6addr_linklocal_allnodes))
-                       return (0);
+               if (IN6_ARE_ADDR_EQUAL(&tmp, &in6addr_linklocal_allnodes)) {
+                       return 0;
+               }
        }
 
-       return (1);
+       return 1;
 }
 
 /*
@@ -483,12 +584,13 @@ mld_domifattach(struct ifnet *ifp, int how)
 {
        struct mld_ifinfo *mli;
 
-       MLD_PRINTF(("%s: called for ifp %p(%s%d)\n",
-           __func__, ifp, ifp->if_name, ifp->if_unit));
+       MLD_PRINTF(("%s: called for ifp 0x%llx(%s)\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
 
        mli = mli_alloc(how);
-       if (mli == NULL)
-               return (NULL);
+       if (mli == NULL) {
+               return NULL;
+       }
 
        MLD_LOCK();
 
@@ -498,15 +600,19 @@ mld_domifattach(struct ifnet *ifp, int how)
        MLI_ADDREF_LOCKED(mli); /* hold a reference for mli_head */
        MLI_ADDREF_LOCKED(mli); /* hold a reference for caller */
        MLI_UNLOCK(mli);
+       ifnet_lock_shared(ifp);
+       mld6_initsilent(ifp, mli);
+       ifnet_lock_done(ifp);
 
        LIST_INSERT_HEAD(&mli_head, mli, mli_link);
+       mld_mli_list_genid++;
 
        MLD_UNLOCK();
 
-       MLD_PRINTF(("allocate mld_ifinfo for ifp %p(%s%d)\n",
-            ifp, ifp->if_name, ifp->if_unit));
+       MLD_PRINTF(("%s: allocate mld_ifinfo for ifp 0x%llx(%s)\n",
+           __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
 
-       return (mli);
+       return mli;
 }
 
 /*
@@ -528,13 +634,17 @@ mld_domifreattach(struct mld_ifinfo *mli)
        mli->mli_debug |= IFD_ATTACHED;
        MLI_ADDREF_LOCKED(mli); /* hold a reference for mli_head */
        MLI_UNLOCK(mli);
+       ifnet_lock_shared(ifp);
+       mld6_initsilent(ifp, mli);
+       ifnet_lock_done(ifp);
 
        LIST_INSERT_HEAD(&mli_head, mli, mli_link);
+       mld_mli_list_genid++;
 
        MLD_UNLOCK();
 
-       MLD_PRINTF(("reattached mld_ifinfo for ifp %p(%s%d)\n",
-            ifp, ifp->if_name, ifp->if_unit));
+       MLD_PRINTF(("%s: reattached mld_ifinfo for ifp 0x%llx(%s)\n",
+           __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
 }
 
 /*
@@ -543,12 +653,12 @@ mld_domifreattach(struct mld_ifinfo *mli)
 void
 mld_domifdetach(struct ifnet *ifp)
 {
-       SLIST_HEAD(, in6_multi) in6m_dthead;
+       SLIST_HEAD(, in6_multi) in6m_dthead;
 
        SLIST_INIT(&in6m_dthead);
 
-       MLD_PRINTF(("%s: called for ifp %p(%s%d)\n",
-           __func__, ifp, ifp->if_name, ifp->if_unit));
+       MLD_PRINTF(("%s: called for ifp 0x%llx(%s)\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
 
        MLD_LOCK();
        mli_delete(ifp, (struct mld_in6m_relhead *)&in6m_dthead);
@@ -586,11 +696,29 @@ mli_delete(const struct ifnet *ifp, struct mld_in6m_relhead *in6m_dthead)
 
                        LIST_REMOVE(mli, mli_link);
                        MLI_REMREF(mli); /* release mli_head reference */
+                       mld_mli_list_genid++;
                        return;
                }
                MLI_UNLOCK(mli);
        }
-       panic("%s: mld_ifinfo not found for ifp %p\n", __func__,  ifp);
+       panic("%s: mld_ifinfo not found for ifp %p(%s)\n", __func__,
+           ifp, ifp->if_xname);
+}
+
+__private_extern__ void
+mld6_initsilent(struct ifnet *ifp, struct mld_ifinfo *mli)
+{
+       ifnet_lock_assert(ifp, IFNET_LCK_ASSERT_OWNED);
+
+       MLI_LOCK_ASSERT_NOTHELD(mli);
+       MLI_LOCK(mli);
+       if (!(ifp->if_flags & IFF_MULTICAST) &&
+           (ifp->if_eflags & (IFEF_IPV6_ND6ALT | IFEF_LOCALNET_PRIVATE))) {
+               mli->mli_flags |= MLIF_SILENT;
+       } else {
+               mli->mli_flags &= ~MLIF_SILENT;
+       }
+       MLI_UNLOCK(mli);
 }
 
 static void
@@ -599,20 +727,23 @@ mli_initvar(struct mld_ifinfo *mli, struct ifnet *ifp, int reattach)
        MLI_LOCK_ASSERT_HELD(mli);
 
        mli->mli_ifp = ifp;
-       mli->mli_version = MLD_VERSION_2;
+       if (mld_v2enable) {
+               mli->mli_version = MLD_VERSION_2;
+       } else {
+               mli->mli_version = MLD_VERSION_1;
+       }
        mli->mli_flags = 0;
        mli->mli_rv = MLD_RV_INIT;
        mli->mli_qi = MLD_QI_INIT;
        mli->mli_qri = MLD_QRI_INIT;
        mli->mli_uri = MLD_URI_INIT;
 
-       /* ifnet is not yet attached; no need to hold ifnet lock */
-       if (!(ifp->if_flags & IFF_MULTICAST))
-               mli->mli_flags |= MLIF_SILENT;
-       if (mld_use_allow)
+       if (mld_use_allow) {
                mli->mli_flags |= MLIF_USEALLOW;
-       if (!reattach)
+       }
+       if (!reattach) {
                SLIST_INIT(&mli->mli_relinmhead);
+       }
 
        /*
         * Responses to general queries are subject to bounds.
@@ -632,7 +763,7 @@ mli_alloc(int how)
                lck_mtx_init(&mli->mli_lock, mld_mtx_grp, mld_mtx_attr);
                mli->mli_debug |= IFD_ALLOC;
        }
-       return (mli);
+       return mli;
 }
 
 static void
@@ -662,23 +793,25 @@ mli_free(struct mld_ifinfo *mli)
 void
 mli_addref(struct mld_ifinfo *mli, int locked)
 {
-       if (!locked)
+       if (!locked) {
                MLI_LOCK_SPIN(mli);
-       else
+       } else {
                MLI_LOCK_ASSERT_HELD(mli);
+       }
 
        if (++mli->mli_refcnt == 0) {
                panic("%s: mli=%p wraparound refcnt", __func__, mli);
                /* NOTREACHED */
        }
-       if (!locked)
+       if (!locked) {
                MLI_UNLOCK(mli);
+       }
 }
 
 void
 mli_remref(struct mld_ifinfo *mli)
 {
-       SLIST_HEAD(, in6_multi) in6m_dthead;
+       SLIST_HEAD(, in6_multi) in6m_dthead;
        struct ifnet *ifp;
 
        MLI_LOCK_SPIN(mli);
@@ -706,8 +839,8 @@ mli_remref(struct mld_ifinfo *mli)
        /* Now that we're dropped all locks, release detached records */
        MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
 
-       MLD_PRINTF(("%s: freeing mld_ifinfo for ifp %p(%s%d)\n",
-           __func__, ifp, ifp->if_name, ifp->if_unit));
+       MLD_PRINTF(("%s: freeing mld_ifinfo for ifp 0x%llx(%s)\n",
+           __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
 
        mli_free(mli);
 }
@@ -723,18 +856,21 @@ static int
 mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
     /*const*/ struct mld_hdr *mld)
 {
-       struct mld_ifinfo       *mli;
-       struct in6_multi        *inm;
-       int                      is_general_query;
-       uint16_t                 timer;
+       struct mld_ifinfo       *mli;
+       struct in6_multi        *inm;
+       int                      err = 0, is_general_query;
+       uint16_t                 timer;
+       struct mld_tparams       mtp = { 0, 0, 0, 0 };
+
+       MLD_LOCK_ASSERT_NOTHELD();
 
        is_general_query = 0;
 
        if (!mld_v1enable) {
-               MLD_PRINTF(("ignore v1 query %s on ifp %p(%s%d)\n",
-                   ip6_sprintf(&mld->mld_addr),
-                   ifp, ifp->if_name, ifp->if_unit));
-               return (0);
+               MLD_PRINTF(("%s: ignore v1 query %s on ifp 0x%llx(%s)\n",
+                   __func__, ip6_sprintf(&mld->mld_addr),
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+               goto done;
        }
 
        /*
@@ -742,10 +878,10 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
         * a router's link-local address.
         */
        if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
-               MLD_PRINTF(("ignore v1 query src %s on ifp %p(%s%d)\n",
-                   ip6_sprintf(&ip6->ip6_src),
-                   ifp, ifp->if_name, ifp->if_unit));
-               return (0);
+               MLD_PRINTF(("%s: ignore v1 query src %s on ifp 0x%llx(%s)\n",
+                   __func__, ip6_sprintf(&ip6->ip6_src),
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+               goto done;
        }
 
        /*
@@ -757,12 +893,14 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
                 * MLDv1 General Query.
                 * If this was not sent to the all-nodes group, ignore it.
                 */
-               struct in6_addr          dst;
+               struct in6_addr          dst;
 
                dst = ip6->ip6_dst;
                in6_clearscope(&dst);
-               if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes))
-                       return (EINVAL);
+               if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes)) {
+                       err = EINVAL;
+                       goto done;
+               }
                is_general_query = 1;
        } else {
                /*
@@ -779,18 +917,19 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
        VERIFY(mli != NULL);
 
        MLI_LOCK(mli);
-       mld_set_version(mli, MLD_VERSION_1);
+       mtp.qpt = mld_set_version(mli, MLD_VERSION_1);
        MLI_UNLOCK(mli);
 
-       timer = (ntohs(mld->mld_maxdelay) * PR_SLOWHZ) / MLD_TIMER_SCALE;
-       if (timer == 0)
+       timer = ntohs(mld->mld_maxdelay) / MLD_TIMER_SCALE;
+       if (timer == 0) {
                timer = 1;
+       }
 
        if (is_general_query) {
                struct in6_multistep step;
 
-               MLD_PRINTF(("process v1 general query on ifp %p(%s%d)\n",
-                   ifp, ifp->if_name, ifp->if_unit));
+               MLD_PRINTF(("%s: process v1 general query on ifp 0x%llx(%s)\n",
+                   __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
                /*
                 * For each reporting group joined on this
                 * interface, kick the report timer.
@@ -799,8 +938,9 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
                IN6_FIRST_MULTI(step, inm);
                while (inm != NULL) {
                        IN6M_LOCK(inm);
-                       if (inm->in6m_ifp == ifp)
-                               mld_v1_update_group(inm, timer);
+                       if (inm->in6m_ifp == ifp) {
+                               mtp.cst += mld_v1_update_group(inm, timer);
+                       }
                        IN6M_UNLOCK(inm);
                        IN6_NEXT_MULTI(step, inm);
                }
@@ -817,18 +957,21 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
 
                if (inm != NULL) {
                        IN6M_LOCK(inm);
-                       MLD_PRINTF(("process v1 query %s on ifp %p(%s%d)\n",
+                       MLD_PRINTF(("%s: process v1 query %s on "
+                           "ifp 0x%llx(%s)\n", __func__,
                            ip6_sprintf(&mld->mld_addr),
-                           ifp, ifp->if_name, ifp->if_unit));
-                       mld_v1_update_group(inm, timer);
+                           (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+                       mtp.cst = mld_v1_update_group(inm, timer);
                        IN6M_UNLOCK(inm);
                        IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
                }
                /* XXX Clear embedded scope ID as userland won't expect it. */
                in6_clearscope(&mld->mld_addr);
        }
+done:
+       mld_set_timeout(&mtp);
 
-       return (0);
+       return err;
 }
 
 /*
@@ -841,19 +984,19 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
  * We may be updating the group for the first time since we switched
  * to MLDv2. If we are, then we must clear any recorded source lists,
  * and transition to REPORTING state; the group timer is overloaded
- * for group and group-source query responses. 
+ * for group and group-source query responses.
  *
  * Unlike MLDv2, the delay per group should be jittered
  * to avoid bursts of MLDv1 reports.
  */
-static void
+static uint32_t
 mld_v1_update_group(struct in6_multi *inm, const int timer)
 {
        IN6M_LOCK_ASSERT_HELD(inm);
 
-       MLD_PRINTF(("%s: %s/%s%d timer=%d\n", __func__,
+       MLD_PRINTF(("%s: %s/%s timer=%d\n", __func__,
            ip6_sprintf(&inm->in6m_addr),
-           inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit, timer));
+           if_name(inm->in6m_ifp), timer));
 
        switch (inm->in6m_state) {
        case MLD_NOT_MEMBER:
@@ -866,7 +1009,7 @@ mld_v1_update_group(struct in6_multi *inm, const int timer)
                            "skipping.\n", __func__));
                        break;
                }
-               /* FALLTHROUGH */
+       /* FALLTHROUGH */
        case MLD_SG_QUERY_PENDING_MEMBER:
        case MLD_G_QUERY_PENDING_MEMBER:
        case MLD_IDLE_MEMBER:
@@ -875,7 +1018,6 @@ mld_v1_update_group(struct in6_multi *inm, const int timer)
                MLD_PRINTF(("%s: ->REPORTING\n", __func__));
                inm->in6m_state = MLD_REPORTING_MEMBER;
                inm->in6m_timer = MLD_RANDOM_DELAY(timer);
-               current_state_timers_running6 = 1;
                break;
        case MLD_SLEEPING_MEMBER:
                MLD_PRINTF(("%s: ->AWAKENING\n", __func__));
@@ -884,6 +1026,8 @@ mld_v1_update_group(struct in6_multi *inm, const int timer)
        case MLD_LEAVING_MEMBER:
                break;
        }
+
+       return inm->in6m_timer;
 }
 
 /*
@@ -898,40 +1042,51 @@ static int
 mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
     struct mbuf *m, const int off, const int icmp6len)
 {
-       struct mld_ifinfo       *mli;
-       struct mldv2_query      *mld;
-       struct in6_multi        *inm;
-       uint32_t                 maxdelay, nsrc, qqi;
-       int                      is_general_query;
-       uint16_t                 timer;
-       uint8_t                  qrv;
+       struct mld_ifinfo       *mli;
+       struct mldv2_query      *mld;
+       struct in6_multi        *inm;
+       uint32_t                 maxdelay, nsrc, qqi;
+       int                      err = 0, is_general_query;
+       uint16_t                 timer;
+       uint8_t                  qrv;
+       struct mld_tparams       mtp = { 0, 0, 0, 0 };
+
+       MLD_LOCK_ASSERT_NOTHELD();
 
        is_general_query = 0;
 
+       if (!mld_v2enable) {
+               MLD_PRINTF(("%s: ignore v2 query %s on ifp 0x%llx(%s)\n",
+                   __func__, ip6_sprintf(&ip6->ip6_src),
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+               goto done;
+       }
+
        /*
         * RFC3810 Section 6.2: MLD queries must originate from
         * a router's link-local address.
         */
        if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
-               MLD_PRINTF(("ignore v1 query src %s on ifp %p(%s%d)\n",
-                   ip6_sprintf(&ip6->ip6_src),
-                   ifp, ifp->if_name, ifp->if_unit));
-               return (0);
+               MLD_PRINTF(("%s: ignore v1 query src %s on ifp 0x%llx(%s)\n",
+                   __func__, ip6_sprintf(&ip6->ip6_src),
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+               goto done;
        }
 
-       MLD_PRINTF(("input v2 query on ifp %p(%s%d)\n", ifp, ifp->if_name,
-           ifp->if_unit));
+       MLD_PRINTF(("%s: input v2 query on ifp 0x%llx(%s)\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
 
        mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off);
 
-       maxdelay = ntohs(mld->mld_maxdelay);    /* in 1/10ths of a second */
-       if (maxdelay >= 32678) {
+       maxdelay = ntohs(mld->mld_maxdelay);    /* in 1/10ths of a second */
+       if (maxdelay >= 32768) {
                maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) <<
-                          (MLD_MRC_EXP(maxdelay) + 3);
+                   (MLD_MRC_EXP(maxdelay) + 3);
        }
-       timer = (maxdelay * PR_SLOWHZ) / MLD_TIMER_SCALE;
-       if (timer == 0)
+       timer = maxdelay / MLD_TIMER_SCALE;
+       if (timer == 0) {
                timer = 1;
+       }
 
        qrv = MLD_QRV(mld->mld_misc);
        if (qrv < 2) {
@@ -943,15 +1098,19 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
        qqi = mld->mld_qqi;
        if (qqi >= 128) {
                qqi = MLD_QQIC_MANT(mld->mld_qqi) <<
-                    (MLD_QQIC_EXP(mld->mld_qqi) + 3);
+                   (MLD_QQIC_EXP(mld->mld_qqi) + 3);
        }
 
        nsrc = ntohs(mld->mld_numsrc);
-       if (nsrc > MLD_MAX_GS_SOURCES)
-               return (EMSGSIZE);
+       if (nsrc > MLD_MAX_GS_SOURCES) {
+               err = EMSGSIZE;
+               goto done;
+       }
        if (icmp6len < sizeof(struct mldv2_query) +
-           (nsrc * sizeof(struct in6_addr)))
-               return (EMSGSIZE);
+           (nsrc * sizeof(struct in6_addr))) {
+               err = EMSGSIZE;
+               goto done;
+       }
 
        /*
         * Do further input validation upfront to avoid resetting timers
@@ -959,17 +1118,13 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
         */
        if (IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
                /*
-                * General Queries SHOULD be directed to ff02::1.
                 * A general query with a source list has undefined
                 * behaviour; discard it.
                 */
-               struct in6_addr          dst;
-
-               dst = ip6->ip6_dst;
-               in6_clearscope(&dst);
-               if (!IN6_ARE_ADDR_EQUAL(&dst, &in6addr_linklocal_allnodes) ||
-                   nsrc > 0)
-                       return (EINVAL);
+               if (nsrc > 0) {
+                       err = EINVAL;
+                       goto done;
+               }
                is_general_query = 1;
        } else {
                /*
@@ -991,16 +1146,16 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
         */
        if (mli->mli_version != MLD_VERSION_2) {
                MLI_UNLOCK(mli);
-               return (0);
+               goto done;
        }
 
-       mld_set_version(mli, MLD_VERSION_2);
+       mtp.qpt = mld_set_version(mli, MLD_VERSION_2);
        mli->mli_rv = qrv;
        mli->mli_qi = qqi;
-       mli->mli_qri = maxdelay;
+       mli->mli_qri = MAX(timer, MLD_QRI_MIN);
 
-       MLD_PRINTF(("%s: qrv %d qi %d maxdelay %d\n", __func__, qrv, qqi,
-           maxdelay));
+       MLD_PRINTF(("%s: qrv %d qi %d qri %d\n", __func__, mli->mli_rv,
+           mli->mli_qi, mli->mli_qri));
 
        if (is_general_query) {
                /*
@@ -1014,11 +1169,10 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
                 * not schedule any other reports.
                 * Otherwise, reset the interface timer.
                 */
-               MLD_PRINTF(("process v2 general query on ifp %p(%s%d)\n",
-                   ifp, ifp->if_name, ifp->if_unit));
+               MLD_PRINTF(("%s: process v2 general query on ifp 0x%llx(%s)\n",
+                   __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
                if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) {
-                       mli->mli_v2_timer = MLD_RANDOM_DELAY(timer);
-                       interface_timers_running6 = 1;
+                       mtp.it = mli->mli_v2_timer = MLD_RANDOM_DELAY(timer);
                }
                MLI_UNLOCK(mli);
        } else {
@@ -1034,12 +1188,11 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
                in6_multihead_lock_shared();
                IN6_LOOKUP_MULTI(&mld->mld_addr, ifp, inm);
                in6_multihead_lock_done();
-               if (inm == NULL)
-                       return (0);
+               if (inm == NULL) {
+                       goto done;
+               }
 
                IN6M_LOCK(inm);
-#ifndef __APPLE__
-               /* TODO: need ratecheck equivalent */
                if (nsrc > 0) {
                        if (!ratecheck(&inm->in6m_lastgsrtv,
                            &mld_gsrdelay)) {
@@ -1047,12 +1200,11 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
                                    __func__));
                                IN6M_UNLOCK(inm);
                                IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
-                               return (0);
+                               goto done;
                        }
                }
-#endif
-               MLD_PRINTF(("process v2 group query on ifp %p(%s%d)\n",
-                    ifp, ifp->if_name, ifp->if_unit));
+               MLD_PRINTF(("%s: process v2 group query on ifp 0x%llx(%s)\n",
+                   __func__, (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
                /*
                 * If there is a pending General Query response
                 * scheduled sooner than the selected delay, no
@@ -1061,19 +1213,26 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
                 * group-specific or group-and-source query.
                 */
                MLI_LOCK(mli);
-               if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) {
-                       MLI_UNLOCK(mli);
-                       mld_v2_process_group_query(inm, timer, m, off);
-               } else {
-                       MLI_UNLOCK(mli);
+               mtp.it = mli->mli_v2_timer;
+               MLI_UNLOCK(mli);
+               if (mtp.it == 0 || mtp.it >= timer) {
+                       (void) mld_v2_process_group_query(inm, timer, m, off);
+                       mtp.cst = inm->in6m_timer;
                }
                IN6M_UNLOCK(inm);
                IN6M_REMREF(inm); /* from IN6_LOOKUP_MULTI */
                /* XXX Clear embedded scope ID as userland won't expect it. */
                in6_clearscope(&mld->mld_addr);
        }
+done:
+       if (mtp.it > 0) {
+               MLD_PRINTF(("%s: v2 general query response scheduled in "
+                   "T+%d seconds on ifp 0x%llx(%s)\n", __func__, mtp.it,
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+       }
+       mld_set_timeout(&mtp);
 
-       return (0);
+       return err;
 }
 
 /*
@@ -1085,9 +1244,9 @@ static int
 mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
     const int off)
 {
-       struct mldv2_query      *mld;
-       int                      retval;
-       uint16_t                 nsrc;
+       struct mldv2_query      *mld;
+       int                      retval;
+       uint16_t                 nsrc;
 
        IN6M_LOCK_ASSERT_HELD(inm);
 
@@ -1102,8 +1261,7 @@ mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
        case MLD_AWAKENING_MEMBER:
        case MLD_IDLE_MEMBER:
        case MLD_LEAVING_MEMBER:
-               return (retval);
-               break;
+               return retval;
        case MLD_REPORTING_MEMBER:
        case MLD_G_QUERY_PENDING_MEMBER:
        case MLD_SG_QUERY_PENDING_MEMBER:
@@ -1126,8 +1284,7 @@ mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
                }
                inm->in6m_state = MLD_G_QUERY_PENDING_MEMBER;
                inm->in6m_timer = MLD_RANDOM_DELAY(timer);
-               current_state_timers_running6 = 1;
-               return (retval);
+               return retval;
        }
 
        /*
@@ -1137,8 +1294,7 @@ mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
        if (inm->in6m_state == MLD_G_QUERY_PENDING_MEMBER) {
                timer = min(inm->in6m_timer, timer);
                inm->in6m_timer = MLD_RANDOM_DELAY(timer);
-               current_state_timers_running6 = 1;
-               return (retval);
+               return retval;
        }
 
        /*
@@ -1153,10 +1309,10 @@ mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
         * report for those sources.
         */
        if (inm->in6m_nsrc > 0) {
-               struct mbuf             *m;
-               uint8_t                 *sp;
-               int                      i, nrecorded;
-               int                      soff;
+               struct mbuf             *m;
+               uint8_t                 *sp;
+               int                      i, nrecorded;
+               int                      soff;
 
                m = m0;
                soff = off + sizeof(struct mldv2_query);
@@ -1164,28 +1320,29 @@ mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
                for (i = 0; i < nsrc; i++) {
                        sp = mtod(m, uint8_t *) + soff;
                        retval = in6m_record_source(inm,
-                           (const struct in6_addr *)sp);
-                       if (retval < 0)
+                           (const struct in6_addr *)(void *)sp);
+                       if (retval < 0) {
                                break;
+                       }
                        nrecorded += retval;
                        soff += sizeof(struct in6_addr);
                        if (soff >= m->m_len) {
                                soff = soff - m->m_len;
                                m = m->m_next;
-                               if (m == NULL)
+                               if (m == NULL) {
                                        break;
+                               }
                        }
                }
                if (nrecorded > 0) {
-                       MLD_PRINTF(( "%s: schedule response to SG query\n",
+                       MLD_PRINTF(("%s: schedule response to SG query\n",
                            __func__));
                        inm->in6m_state = MLD_SG_QUERY_PENDING_MEMBER;
                        inm->in6m_timer = MLD_RANDOM_DELAY(timer);
-                       current_state_timers_running6 = 1;
                }
        }
 
-       return (retval);
+       return retval;
 }
 
 /*
@@ -1196,22 +1353,24 @@ mld_v2_process_group_query(struct in6_multi *inm, int timer, struct mbuf *m0,
  * mld_addr. This is OK as we own the mbuf chain.
  */
 static int
-mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
-    /*const*/ struct mld_hdr *mld)
+mld_v1_input_report(struct ifnet *ifp, struct mbuf *m,
+    const struct ip6_hdr *ip6, /*const*/ struct mld_hdr *mld)
 {
-       struct in6_addr          src, dst;
-       struct in6_ifaddr       *ia;
-       struct in6_multi        *inm;
+       struct in6_addr          src, dst;
+       struct in6_ifaddr       *ia;
+       struct in6_multi        *inm;
 
        if (!mld_v1enable) {
-               MLD_PRINTF(("ignore v1 report %s on ifp %p(%s%d)\n",
-                   ip6_sprintf(&mld->mld_addr),
-                   ifp, ifp->if_name, ifp->if_unit));
-               return (0);
+               MLD_PRINTF(("%s: ignore v1 report %s on ifp 0x%llx(%s)\n",
+                   __func__, ip6_sprintf(&mld->mld_addr),
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+               return 0;
        }
 
-       if (ifp->if_flags & IFF_LOOPBACK)
-               return (0);
+       if ((ifp->if_flags & IFF_LOOPBACK) ||
+           (m->m_pkthdr.pkt_flags & PKTF_LOOP)) {
+               return 0;
+       }
 
        /*
         * MLDv1 reports must originate from a host's link-local address,
@@ -1220,10 +1379,10 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
        src = ip6->ip6_src;
        in6_clearscope(&src);
        if (!IN6_IS_SCOPE_LINKLOCAL(&src) && !IN6_IS_ADDR_UNSPECIFIED(&src)) {
-               MLD_PRINTF(("ignore v1 query src %s on ifp %p(%s%d)\n",
-                   ip6_sprintf(&ip6->ip6_src),
-                   ifp, ifp->if_name, ifp->if_unit));
-               return (EINVAL);
+               MLD_PRINTF(("%s: ignore v1 query src %s on ifp 0x%llx(%s)\n",
+                   __func__, ip6_sprintf(&ip6->ip6_src),
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+               return EINVAL;
        }
 
        /*
@@ -1234,10 +1393,10 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
        in6_clearscope(&dst);
        if (!IN6_IS_ADDR_MULTICAST(&mld->mld_addr) ||
            !IN6_ARE_ADDR_EQUAL(&mld->mld_addr, &dst)) {
-               MLD_PRINTF(("ignore v1 query dst %s on ifp %p(%s%d)\n",
-                   ip6_sprintf(&ip6->ip6_dst),
-                   ifp, ifp->if_name, ifp->if_unit));
-               return (EINVAL);
+               MLD_PRINTF(("%s: ignore v1 query dst %s on ifp 0x%llx(%s)\n",
+                   __func__, ip6_sprintf(&ip6->ip6_dst),
+                   (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
+               return EINVAL;
        }
 
        /*
@@ -1250,29 +1409,31 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
         * returned by in6ifa_ifpforlinklocal(), but SHOULD NOT be
         * performed for the on-wire address.
         */
-       ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
+       ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
        if (ia != NULL) {
                IFA_LOCK(&ia->ia_ifa);
-               if ((IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, IA6_IN6(ia)))){
+               if ((IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, IA6_IN6(ia)))) {
                        IFA_UNLOCK(&ia->ia_ifa);
                        IFA_REMREF(&ia->ia_ifa);
-                       return (0);
+                       return 0;
                }
                IFA_UNLOCK(&ia->ia_ifa);
                IFA_REMREF(&ia->ia_ifa);
        } else if (IN6_IS_ADDR_UNSPECIFIED(&src)) {
-               return (0);
+               return 0;
        }
 
-       MLD_PRINTF(("process v1 report %s on ifp %p(%s%d)\n",
-           ip6_sprintf(&mld->mld_addr), ifp, ifp->if_name, ifp->if_unit));
+       MLD_PRINTF(("%s: process v1 report %s on ifp 0x%llx(%s)\n",
+           __func__, ip6_sprintf(&mld->mld_addr),
+           (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
 
        /*
         * Embed scope ID of receiving interface in MLD query for lookup
         * whilst we don't hold other locks (due to KAME locking lameness).
         */
-       if (!IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr))
+       if (!IN6_IS_ADDR_UNSPECIFIED(&mld->mld_addr)) {
                in6_setscope(&mld->mld_addr, ifp, NULL);
+       }
 
        /*
         * MLDv1 report suppression.
@@ -1314,9 +1475,10 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6,
                case MLD_REPORTING_MEMBER:
                case MLD_IDLE_MEMBER:
                case MLD_AWAKENING_MEMBER:
-                       MLD_PRINTF(("report suppressed for %s on ifp %p(%s%d)\n",
+                       MLD_PRINTF(("%s: report suppressed for %s on "
+                           "ifp 0x%llx(%s)\n", __func__,
                            ip6_sprintf(&mld->mld_addr),
-                           ifp, ifp->if_name, ifp->if_unit));
+                           (uint64_t)VM_KERNEL_ADDRPERM(ifp), if_name(ifp)));
                case MLD_LAZY_MEMBER:
                        inm->in6m_state = MLD_LAZY_MEMBER;
                        break;
@@ -1333,7 +1495,7 @@ out:
        /* XXX Clear embedded scope ID as userland won't expect it. */
        in6_clearscope(&mld->mld_addr);
 
-       return (0);
+       return 0;
 }
 
 /*
@@ -1350,12 +1512,13 @@ out:
 int
 mld_input(struct mbuf *m, int off, int icmp6len)
 {
-       struct ifnet    *ifp;
-       struct ip6_hdr  *ip6;
-       struct mld_hdr  *mld;
-       int              mldlen;
+       struct ifnet    *ifp;
+       struct ip6_hdr  *ip6;
+       struct mld_hdr  *mld;
+       int              mldlen;
 
-       MLD_PRINTF(("%s: called w/mbuf (%p,%d)\n", __func__, m, off));
+       MLD_PRINTF(("%s: called w/mbuf (0x%llx,%d)\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(m), off));
 
        ifp = m->m_pkthdr.rcvif;
 
@@ -1369,10 +1532,12 @@ mld_input(struct mbuf *m, int off, int icmp6len)
        } else {
                mldlen = sizeof(struct mld_hdr);
        }
+       // check if mldv2_query/mld_hdr fits in the first mbuf
+       IP6_EXTHDR_CHECK(m, off, mldlen, return IPPROTO_DONE);
        IP6_EXTHDR_GET(mld, struct mld_hdr *, m, off, mldlen);
        if (mld == NULL) {
                icmp6stat.icp6s_badlen++;
-               return (IPPROTO_DONE);
+               return IPPROTO_DONE;
        }
 
        /*
@@ -1383,18 +1548,21 @@ mld_input(struct mbuf *m, int off, int icmp6len)
        case MLD_LISTENER_QUERY:
                icmp6_ifstat_inc(ifp, ifs6_in_mldquery);
                if (icmp6len == sizeof(struct mld_hdr)) {
-                       if (mld_v1_input_query(ifp, ip6, mld) != 0)
-                               return (0);
+                       if (mld_v1_input_query(ifp, ip6, mld) != 0) {
+                               return 0;
+                       }
                } else if (icmp6len >= sizeof(struct mldv2_query)) {
                        if (mld_v2_input_query(ifp, ip6, m, off,
-                           icmp6len) != 0)
-                               return (0);
+                           icmp6len) != 0) {
+                               return 0;
+                       }
                }
                break;
        case MLD_LISTENER_REPORT:
                icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
-               if (mld_v1_input_report(ifp, ip6, mld) != 0)
-                       return (0);
+               if (mld_v1_input_report(ifp, m, ip6, mld) != 0) {
+                       return 0;
+               }
                break;
        case MLDV2_LISTENER_REPORT:
                icmp6_ifstat_inc(ifp, ifs6_in_mldreport);
@@ -1406,76 +1574,154 @@ mld_input(struct mbuf *m, int off, int icmp6len)
                break;
        }
 
-       return (0);
+       return 0;
 }
 
 /*
- * MLD6 slowtimo handler.
- * Combiles both the slow and fast timer into one. We loose some responsivness but
- * allows the system to avoid having a pr_fasttimo, thus allowing for power savings.
+ * Schedule MLD timer based on various parameters; caller must ensure that
+ * lock ordering is maintained as this routine acquires MLD global lock.
  */
 void
-mld_slowtimo(void)
+mld_set_timeout(struct mld_tparams *mtp)
 {
-       struct ifqueue           scq;   /* State-change packets */
-       struct ifqueue           qrq;   /* Query response packets */
-       struct ifnet            *ifp;
-       struct mld_ifinfo       *mli;
-       struct in6_multi        *inm;
-       int                      uri_fasthz = 0;
-       SLIST_HEAD(, in6_multi) in6m_dthead;
+       MLD_LOCK_ASSERT_NOTHELD();
+       VERIFY(mtp != NULL);
+
+       if (mtp->qpt != 0 || mtp->it != 0 || mtp->cst != 0 || mtp->sct != 0) {
+               MLD_LOCK();
+               if (mtp->qpt != 0) {
+                       querier_present_timers_running6 = 1;
+               }
+               if (mtp->it != 0) {
+                       interface_timers_running6 = 1;
+               }
+               if (mtp->cst != 0) {
+                       current_state_timers_running6 = 1;
+               }
+               if (mtp->sct != 0) {
+                       state_change_timers_running6 = 1;
+               }
+               mld_sched_timeout();
+               MLD_UNLOCK();
+       }
+}
+
+/*
+ * MLD6 timer handler (per 1 second).
+ */
+static void
+mld_timeout(void *arg)
+{
+#pragma unused(arg)
+       struct ifqueue           scq;   /* State-change packets */
+       struct ifqueue           qrq;   /* Query response packets */
+       struct ifnet            *ifp;
+       struct mld_ifinfo       *mli;
+       struct in6_multi        *inm;
+       int                      uri_sec = 0;
+       unsigned int genid = mld_mli_list_genid;
+
+       SLIST_HEAD(, in6_multi) in6m_dthead;
 
        SLIST_INIT(&in6m_dthead);
 
+       /*
+        * Update coarse-grained networking timestamp (in sec.); the idea
+        * is to piggy-back on the timeout callout to update the counter
+        * returnable via net_uptime().
+        */
+       net_update_uptime();
+
        MLD_LOCK();
 
-       LIST_FOREACH(mli, &mli_head, mli_link) {
-               MLI_LOCK(mli);
-               mld_v1_process_querier_timers(mli);
-               MLI_UNLOCK(mli);
-       }
+       MLD_PRINTF(("%s: qpt %d, it %d, cst %d, sct %d\n", __func__,
+           querier_present_timers_running6, interface_timers_running6,
+           current_state_timers_running6, state_change_timers_running6));
 
        /*
-        * Quick check to see if any work needs to be done, in order to
-        * minimize the overhead of fasttimo processing.
+        * MLDv1 querier present timer processing.
         */
-       if (!current_state_timers_running6 &&
-           !interface_timers_running6 &&
-           !state_change_timers_running6) {
-               MLD_UNLOCK();
-               return;
+       if (querier_present_timers_running6) {
+               querier_present_timers_running6 = 0;
+               LIST_FOREACH(mli, &mli_head, mli_link) {
+                       MLI_LOCK(mli);
+                       mld_v1_process_querier_timers(mli);
+                       if (mli->mli_v1_timer > 0) {
+                               querier_present_timers_running6 = 1;
+                       }
+                       MLI_UNLOCK(mli);
+               }
        }
 
        /*
         * MLDv2 General Query response timer processing.
         */
        if (interface_timers_running6) {
-#if 0
                MLD_PRINTF(("%s: interface timers running\n", __func__));
-#endif
                interface_timers_running6 = 0;
-               LIST_FOREACH(mli, &mli_head, mli_link) {
+               mli = LIST_FIRST(&mli_head);
+
+               while (mli != NULL) {
+                       if (mli->mli_flags & MLIF_PROCESSED) {
+                               mli = LIST_NEXT(mli, mli_link);
+                               continue;
+                       }
+
                        MLI_LOCK(mli);
+                       if (mli->mli_version != MLD_VERSION_2) {
+                               MLI_UNLOCK(mli);
+                               mli = LIST_NEXT(mli, mli_link);
+                               continue;
+                       }
+                       /*
+                        * XXX The logic below ends up calling
+                        * mld_dispatch_packet which can unlock mli
+                        * and the global MLD lock.
+                        * Therefore grab a reference on MLI and also
+                        * check for generation count to see if we should
+                        * iterate the list again.
+                        */
+                       MLI_ADDREF_LOCKED(mli);
+
                        if (mli->mli_v2_timer == 0) {
                                /* Do nothing. */
                        } else if (--mli->mli_v2_timer == 0) {
-                               mld_v2_dispatch_general_query(mli);
+                               if (mld_v2_dispatch_general_query(mli) > 0) {
+                                       interface_timers_running6 = 1;
+                               }
                        } else {
                                interface_timers_running6 = 1;
                        }
+                       mli->mli_flags |= MLIF_PROCESSED;
                        MLI_UNLOCK(mli);
+                       MLI_REMREF(mli);
+
+                       if (genid != mld_mli_list_genid) {
+                               MLD_PRINTF(("%s: MLD information list changed "
+                                   "in the middle of iteration! Restart iteration.\n",
+                                   __func__));
+                               mli = LIST_FIRST(&mli_head);
+                               genid = mld_mli_list_genid;
+                       } else {
+                               mli = LIST_NEXT(mli, mli_link);
+                       }
                }
+
+               LIST_FOREACH(mli, &mli_head, mli_link)
+               mli->mli_flags &= ~MLIF_PROCESSED;
        }
 
+
+
        if (!current_state_timers_running6 &&
-           !state_change_timers_running6)
+           !state_change_timers_running6) {
                goto out_locked;
+       }
 
        current_state_timers_running6 = 0;
        state_change_timers_running6 = 0;
-#if 0
+
        MLD_PRINTF(("%s: state change timers running\n", __func__));
-#endif
 
        memset(&qrq, 0, sizeof(struct ifqueue));
        qrq.ifq_maxlen = MLD_MAX_G_GS_PACKETS;
@@ -1487,20 +1733,28 @@ mld_slowtimo(void)
         * MLD host report and state-change timer processing.
         * Note: Processing a v2 group timer may remove a node.
         */
-       LIST_FOREACH(mli, &mli_head, mli_link) {
+       mli = LIST_FIRST(&mli_head);
+
+       while (mli != NULL) {
                struct in6_multistep step;
 
+               if (mli->mli_flags & MLIF_PROCESSED) {
+                       mli = LIST_NEXT(mli, mli_link);
+                       continue;
+               }
+
                MLI_LOCK(mli);
                ifp = mli->mli_ifp;
-               uri_fasthz = MLD_RANDOM_DELAY(mli->mli_uri * PR_SLOWHZ);
+               uri_sec = MLD_RANDOM_DELAY(mli->mli_uri);
                MLI_UNLOCK(mli);
 
                in6_multihead_lock_shared();
                IN6_FIRST_MULTI(step, inm);
                while (inm != NULL) {
                        IN6M_LOCK(inm);
-                       if (inm->in6m_ifp != ifp)
+                       if (inm->in6m_ifp != ifp) {
                                goto next;
+                       }
 
                        MLI_LOCK(mli);
                        switch (mli->mli_version) {
@@ -1510,7 +1764,7 @@ mld_slowtimo(void)
                                break;
                        case MLD_VERSION_2:
                                mld_v2_process_group_timers(mli, &qrq,
-                                   &scq, inm, uri_fasthz);
+                                   &scq, inm, uri_sec);
                                break;
                        }
                        MLI_UNLOCK(mli);
@@ -1520,13 +1774,22 @@ next:
                }
                in6_multihead_lock_done();
 
+               /*
+                * XXX The logic below ends up calling
+                * mld_dispatch_packet which can unlock mli
+                * and the global MLD lock.
+                * Therefore grab a reference on MLI and also
+                * check for generation count to see if we should
+                * iterate the list again.
+                */
                MLI_LOCK(mli);
+               MLI_ADDREF_LOCKED(mli);
                if (mli->mli_version == MLD_VERSION_1) {
-                       mld_dispatch_queue(mli, &mli->mli_v1q, 0);
+                       mld_dispatch_queue_locked(mli, &mli->mli_v1q, 0);
                } else if (mli->mli_version == MLD_VERSION_2) {
                        MLI_UNLOCK(mli);
-                       mld_dispatch_queue(NULL, &qrq, 0);
-                       mld_dispatch_queue(NULL, &scq, 0);
+                       mld_dispatch_queue_locked(NULL, &qrq, 0);
+                       mld_dispatch_queue_locked(NULL, &scq, 0);
                        VERIFY(qrq.ifq_len == 0);
                        VERIFY(scq.ifq_len == 0);
                        MLI_LOCK(mli);
@@ -1544,19 +1807,50 @@ next:
                 */
                mld_flush_relq(mli, (struct mld_in6m_relhead *)&in6m_dthead);
                VERIFY(SLIST_EMPTY(&mli->mli_relinmhead));
+               mli->mli_flags |= MLIF_PROCESSED;
                MLI_UNLOCK(mli);
+               MLI_REMREF(mli);
 
                IF_DRAIN(&qrq);
                IF_DRAIN(&scq);
+
+               if (genid != mld_mli_list_genid) {
+                       MLD_PRINTF(("%s: MLD information list changed "
+                           "in the middle of iteration! Restart iteration.\n",
+                           __func__));
+                       mli = LIST_FIRST(&mli_head);
+                       genid = mld_mli_list_genid;
+               } else {
+                       mli = LIST_NEXT(mli, mli_link);
+               }
        }
 
+       LIST_FOREACH(mli, &mli_head, mli_link)
+       mli->mli_flags &= ~MLIF_PROCESSED;
+
 out_locked:
+       /* re-arm the timer if there's work to do */
+       mld_timeout_run = 0;
+       mld_sched_timeout();
        MLD_UNLOCK();
 
        /* Now that we're dropped all locks, release detached records */
        MLD_REMOVE_DETACHED_IN6M(&in6m_dthead);
 }
 
+static void
+mld_sched_timeout(void)
+{
+       MLD_LOCK_ASSERT_HELD();
+
+       if (!mld_timeout_run &&
+           (querier_present_timers_running6 || current_state_timers_running6 ||
+           interface_timers_running6 || state_change_timers_running6)) {
+               mld_timeout_run = 1;
+               timeout(mld_timeout, NULL, hz);
+       }
+}
+
 /*
  * Free the in6_multi reference(s) for this MLD lifecycle.
  *
@@ -1614,6 +1908,7 @@ mld_v1_process_group_timer(struct in6_multi *inm, const int mld_version)
 #pragma unused(mld_version)
        int report_timer_expired;
 
+       MLD_LOCK_ASSERT_HELD();
        IN6M_LOCK_ASSERT_HELD(inm);
        MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
 
@@ -1623,6 +1918,7 @@ mld_v1_process_group_timer(struct in6_multi *inm, const int mld_version)
                report_timer_expired = 1;
        } else {
                current_state_timers_running6 = 1;
+               /* caller will schedule timer */
                return;
        }
 
@@ -1638,7 +1934,7 @@ mld_v1_process_group_timer(struct in6_multi *inm, const int mld_version)
                if (report_timer_expired) {
                        inm->in6m_state = MLD_IDLE_MEMBER;
                        (void) mld_v1_transmit_report(inm,
-                            MLD_LISTENER_REPORT);
+                           MLD_LISTENER_REPORT);
                        IN6M_LOCK_ASSERT_HELD(inm);
                        MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
                }
@@ -1658,11 +1954,12 @@ mld_v1_process_group_timer(struct in6_multi *inm, const int mld_version)
 static void
 mld_v2_process_group_timers(struct mld_ifinfo *mli,
     struct ifqueue *qrq, struct ifqueue *scq,
-    struct in6_multi *inm, const int uri_fasthz)
+    struct in6_multi *inm, const int uri_sec)
 {
        int query_response_timer_expired;
        int state_change_retransmit_timer_expired;
 
+       MLD_LOCK_ASSERT_HELD();
        IN6M_LOCK_ASSERT_HELD(inm);
        MLI_LOCK_ASSERT_HELD(mli);
        VERIFY(mli == inm->in6m_mli);
@@ -1674,7 +1971,7 @@ mld_v2_process_group_timers(struct mld_ifinfo *mli,
         * During a transition from compatibility mode back to MLDv2,
         * a group record in REPORTING state may still have its group
         * timer active. This is a no-op in this function; it is easier
-        * to deal with it here than to complicate the slow-timeout path.
+        * to deal with it here than to complicate the timeout path.
         */
        if (inm->in6m_timer == 0) {
                query_response_timer_expired = 0;
@@ -1682,6 +1979,7 @@ mld_v2_process_group_timers(struct mld_ifinfo *mli,
                query_response_timer_expired = 1;
        } else {
                current_state_timers_running6 = 1;
+               /* caller will schedule timer */
        }
 
        if (inm->in6m_sctimer == 0) {
@@ -1690,12 +1988,14 @@ mld_v2_process_group_timers(struct mld_ifinfo *mli,
                state_change_retransmit_timer_expired = 1;
        } else {
                state_change_timers_running6 = 1;
+               /* caller will schedule timer */
        }
 
-       /* We are in fasttimo, so be quick about it. */
+       /* We are in timer callback, so be quick about it. */
        if (!state_change_retransmit_timer_expired &&
-           !query_response_timer_expired)
+           !query_response_timer_expired) {
                return;
+       }
 
        switch (inm->in6m_state) {
        case MLD_NOT_MEMBER:
@@ -1724,7 +2024,7 @@ mld_v2_process_group_timers(struct mld_ifinfo *mli,
                        inm->in6m_state = MLD_REPORTING_MEMBER;
                        in6m_clear_recorded(inm);
                }
-               /* FALLTHROUGH */
+       /* FALLTHROUGH */
        case MLD_REPORTING_MEMBER:
        case MLD_LEAVING_MEMBER:
                if (state_change_retransmit_timer_expired) {
@@ -1735,8 +2035,9 @@ mld_v2_process_group_timers(struct mld_ifinfo *mli,
                         * reset the timer.
                         */
                        if (--inm->in6m_scrv > 0) {
-                               inm->in6m_sctimer = uri_fasthz;
+                               inm->in6m_sctimer = uri_sec;
                                state_change_timers_running6 = 1;
+                               /* caller will schedule timer */
                        }
                        /*
                         * Retransmit the previously computed state-change
@@ -1748,9 +2049,9 @@ mld_v2_process_group_timers(struct mld_ifinfo *mli,
                        (void) mld_v2_merge_state_changes(inm, scq);
 
                        in6m_commit(inm);
-                       MLD_PRINTF(("%s: T1 -> T0 for %s/%s%d\n", __func__,
+                       MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
                            ip6_sprintf(&inm->in6m_addr),
-                           inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
+                           if_name(inm->in6m_ifp)));
 
                        /*
                         * If we are leaving the group for good, make sure
@@ -1784,24 +2085,23 @@ mld_v2_process_group_timers(struct mld_ifinfo *mli,
  * Switch to a different version on the given interface,
  * as per Section 9.12.
  */
-static void
+static uint32_t
 mld_set_version(struct mld_ifinfo *mli, const int mld_version)
 {
        int old_version_timer;
 
        MLI_LOCK_ASSERT_HELD(mli);
 
-       MLD_PRINTF(("%s: switching to v%d on ifp %p(%s%d)\n", __func__,
-           mld_version, mli->mli_ifp, mli->mli_ifp->if_name,
-           mli->mli_ifp->if_unit));
+       MLD_PRINTF(("%s: switching to v%d on ifp 0x%llx(%s)\n", __func__,
+           mld_version, (uint64_t)VM_KERNEL_ADDRPERM(mli->mli_ifp),
+           if_name(mli->mli_ifp)));
 
        if (mld_version == MLD_VERSION_1) {
                /*
                 * Compute the "Older Version Querier Present" timer as per
-                * Section 9.12.
+                * Section 9.12, in seconds.
                 */
                old_version_timer = (mli->mli_rv * mli->mli_qi) + mli->mli_qri;
-               old_version_timer *= PR_SLOWHZ;
                mli->mli_v1_timer = old_version_timer;
        }
 
@@ -1811,34 +2111,42 @@ mld_set_version(struct mld_ifinfo *mli, const int mld_version)
        }
 
        MLI_LOCK_ASSERT_HELD(mli);
+
+       return mli->mli_v1_timer;
 }
 
 /*
  * Cancel pending MLDv2 timers for the given link and all groups
  * joined on it; state-change, general-query, and group-query timers.
+ *
+ * Only ever called on a transition from v2 to Compatibility mode. Kill
+ * the timers stone dead (this may be expensive for large N groups), they
+ * will be restarted if Compatibility Mode deems that they must be due to
+ * query processing.
  */
 static void
 mld_v2_cancel_link_timers(struct mld_ifinfo *mli)
 {
-       struct ifnet            *ifp;
-       struct in6_multi        *inm;
-       struct in6_multistep    step;
+       struct ifnet            *ifp;
+       struct in6_multi        *inm;
+       struct in6_multistep    step;
 
        MLI_LOCK_ASSERT_HELD(mli);
 
-       MLD_PRINTF(("%s: cancel v2 timers on ifp %p(%s%d)\n", __func__,
-           mli->mli_ifp, mli->mli_ifp->if_name, mli->mli_ifp->if_unit));
+       MLD_PRINTF(("%s: cancel v2 timers on ifp 0x%llx(%s)\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(mli->mli_ifp), if_name(mli->mli_ifp)));
 
        /*
-        * Fast-track this potentially expensive operation
-        * by checking all the global 'timer pending' flags.
+        * Stop the v2 General Query Response on this link stone dead.
+        * If timer is woken up due to interface_timers_running6,
+        * the flag will be cleared if there are no pending link timers.
         */
-       if (!interface_timers_running6 &&
-           !state_change_timers_running6 &&
-           !current_state_timers_running6)
-               return;
-
        mli->mli_v2_timer = 0;
+
+       /*
+        * Now clear the current-state and state-change report timers
+        * for all memberships scoped to this link.
+        */
        ifp = mli->mli_ifp;
        MLI_UNLOCK(mli);
 
@@ -1846,8 +2154,9 @@ mld_v2_cancel_link_timers(struct mld_ifinfo *mli)
        IN6_FIRST_MULTI(step, inm);
        while (inm != NULL) {
                IN6M_LOCK(inm);
-               if (inm->in6m_ifp != ifp)
+               if (inm->in6m_ifp != ifp) {
                        goto next;
+               }
 
                switch (inm->in6m_state) {
                case MLD_NOT_MEMBER:
@@ -1856,6 +2165,10 @@ mld_v2_cancel_link_timers(struct mld_ifinfo *mli)
                case MLD_LAZY_MEMBER:
                case MLD_SLEEPING_MEMBER:
                case MLD_AWAKENING_MEMBER:
+                       /*
+                        * These states are either not relevant in v2 mode,
+                        * or are unreported. Do nothing.
+                        */
                        break;
                case MLD_LEAVING_MEMBER:
                        /*
@@ -1872,21 +2185,22 @@ mld_v2_cancel_link_timers(struct mld_ifinfo *mli)
                        SLIST_INSERT_HEAD(&mli->mli_relinmhead, inm,
                            in6m_nrele);
                        MLI_UNLOCK(mli);
-                       /* FALLTHROUGH */
+               /* FALLTHROUGH */
                case MLD_G_QUERY_PENDING_MEMBER:
                case MLD_SG_QUERY_PENDING_MEMBER:
                        in6m_clear_recorded(inm);
-                       /* FALLTHROUGH */
+               /* FALLTHROUGH */
                case MLD_REPORTING_MEMBER:
-                       inm->in6m_sctimer = 0;
-                       inm->in6m_timer = 0;
                        inm->in6m_state = MLD_REPORTING_MEMBER;
-                       /*
-                        * Free any pending MLDv2 state-change records.
-                        */
-                       IF_DRAIN(&inm->in6m_scq);
                        break;
                }
+               /*
+                * Always clear state-change and group report timers.
+                * Free any pending MLDv2 state-change records.
+                */
+               inm->in6m_sctimer = 0;
+               inm->in6m_timer = 0;
+               IF_DRAIN(&inm->in6m_scq);
 next:
                IN6M_UNLOCK(inm);
                IN6_NEXT_MULTI(step, inm);
@@ -1905,13 +2219,15 @@ mld_v1_process_querier_timers(struct mld_ifinfo *mli)
 {
        MLI_LOCK_ASSERT_HELD(mli);
 
-       if (mli->mli_version != MLD_VERSION_2 && --mli->mli_v1_timer == 0) {
+       if (mld_v2enable && mli->mli_version != MLD_VERSION_2 &&
+           --mli->mli_v1_timer == 0) {
                /*
                 * MLDv1 Querier Present timer expired; revert to MLDv2.
                 */
-               MLD_PRINTF(("%s: transition from v%d -> v%d on %p(%s%d)\n",
+               MLD_PRINTF(("%s: transition from v%d -> v%d on 0x%llx(%s)\n",
                    __func__, mli->mli_version, MLD_VERSION_2,
-                   mli->mli_ifp, mli->mli_ifp->if_name, mli->mli_ifp->if_unit));
+                   (uint64_t)VM_KERNEL_ADDRPERM(mli->mli_ifp),
+                   if_name(mli->mli_ifp)));
                mli->mli_version = MLD_VERSION_2;
        }
 }
@@ -1922,32 +2238,34 @@ mld_v1_process_querier_timers(struct mld_ifinfo *mli)
 static int
 mld_v1_transmit_report(struct in6_multi *in6m, const int type)
 {
-       struct ifnet            *ifp;
-       struct in6_ifaddr       *ia;
-       struct ip6_hdr          *ip6;
-       struct mbuf             *mh, *md;
-       struct mld_hdr          *mld;
-       int                     error = 0;
+       struct ifnet            *ifp;
+       struct in6_ifaddr       *ia;
+       struct ip6_hdr          *ip6;
+       struct mbuf             *mh, *md;
+       struct mld_hdr          *mld;
+       int                     error = 0;
 
        IN6M_LOCK_ASSERT_HELD(in6m);
        MLI_LOCK_ASSERT_HELD(in6m->in6m_mli);
 
        ifp = in6m->in6m_ifp;
        /* ia may be NULL if link-local address is tentative. */
-       ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
+       ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
 
        MGETHDR(mh, M_DONTWAIT, MT_HEADER);
        if (mh == NULL) {
-               if (ia != NULL)
+               if (ia != NULL) {
                        IFA_REMREF(&ia->ia_ifa);
-               return (ENOMEM);
+               }
+               return ENOMEM;
        }
        MGET(md, M_DONTWAIT, MT_DATA);
        if (md == NULL) {
                m_free(mh);
-               if (ia != NULL)
+               if (ia != NULL) {
                        IFA_REMREF(&ia->ia_ifa);
-               return (ENOMEM);
+               }
+               return ENOMEM;
        }
        mh->m_next = md;
 
@@ -1965,8 +2283,9 @@ mld_v1_transmit_report(struct in6_multi *in6m, const int type)
        ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
        ip6->ip6_vfc |= IPV6_VERSION;
        ip6->ip6_nxt = IPPROTO_ICMPV6;
-       if (ia != NULL)
+       if (ia != NULL) {
                IFA_LOCK(&ia->ia_ifa);
+       }
        ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
        if (ia != NULL) {
                IFA_UNLOCK(&ia->ia_ifa);
@@ -1987,26 +2306,28 @@ mld_v1_transmit_report(struct in6_multi *in6m, const int type)
        mld->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6,
            sizeof(struct ip6_hdr), sizeof(struct mld_hdr));
 
+       mld_save_context(mh, ifp);
        mh->m_flags |= M_MLDV1;
 
-       
        /*
         * Due to the fact that at this point we are possibly holding
         * in6_multihead_lock in shared or exclusive mode, we can't call
         * mld_dispatch_packet() here since that will eventually call
         * ip6_output(), which will try to lock in6_multihead_lock and cause
         * a deadlock.
-        * Instead we defer the work to the mld_slowtimo() thread, thus
+        * Instead we defer the work to the mld_timeout() thread, thus
         * avoiding unlocking in_multihead_lock here.
         */
-        if (IF_QFULL(&in6m->in6m_mli->mli_v1q)) {
-                MLD_PRINTF(("%s: v1 outbound queue full\n", __func__));
-                error = ENOMEM;
-                m_freem(mh);
-        } else
-                IF_ENQUEUE(&in6m->in6m_mli->mli_v1q, mh);
-
-       return (error);
+       if (IF_QFULL(&in6m->in6m_mli->mli_v1q)) {
+               MLD_PRINTF(("%s: v1 outbound queue full\n", __func__));
+               error = ENOMEM;
+               m_freem(mh);
+       } else {
+               IF_ENQUEUE(&in6m->in6m_mli->mli_v1q, mh);
+               VERIFY(error == 0);
+       }
+
+       return error;
 }
 
 /*
@@ -2025,17 +2346,21 @@ mld_v1_transmit_report(struct in6_multi *in6m, const int type)
  *
  * If delay is non-zero, and the state change is an initial multicast
  * join, the state change report will be delayed by 'delay' ticks
- * in units of PR_FASTHZ if MLDv1 is active on the link; otherwise
+ * in units of seconds if MLDv1 is active on the link; otherwise
  * the initial MLDv2 state change report will be delayed by whichever
  * is sooner, a pending state-change timer or delay itself.
  */
 int
-mld_change_state(struct in6_multi *inm, const int delay)
+mld_change_state(struct in6_multi *inm, struct mld_tparams *mtp,
+    const int delay)
 {
        struct mld_ifinfo *mli;
        struct ifnet *ifp;
        int error = 0;
 
+       VERIFY(mtp != NULL);
+       bzero(mtp, sizeof(*mtp));
+
        IN6M_LOCK_ASSERT_HELD(inm);
        VERIFY(inm->in6m_mli != NULL);
        MLI_LOCK_ASSERT_NOTHELD(inm->in6m_mli);
@@ -2064,21 +2389,20 @@ mld_change_state(struct in6_multi *inm, const int delay)
                    inm->in6m_st[0].iss_fmode, inm->in6m_st[1].iss_fmode));
                if (inm->in6m_st[0].iss_fmode == MCAST_UNDEFINED) {
                        MLD_PRINTF(("%s: initial join\n", __func__));
-                       error = mld_initial_join(inm, mli, delay);
+                       error = mld_initial_join(inm, mli, mtp, delay);
                        goto out;
                } else if (inm->in6m_st[1].iss_fmode == MCAST_UNDEFINED) {
                        MLD_PRINTF(("%s: final leave\n", __func__));
-                       mld_final_leave(inm, mli);
+                       mld_final_leave(inm, mli, mtp);
                        goto out;
                }
        } else {
                MLD_PRINTF(("%s: filter set change\n", __func__));
        }
 
-       error = mld_handle_state_change(inm, mli);
-
+       error = mld_handle_state_change(inm, mli, mtp);
 out:
-       return (error);
+       return error;
 }
 
 /*
@@ -2091,23 +2415,25 @@ out:
  *  initial state of the membership.
  *
  * If the delay argument is non-zero, then we must delay sending the
- * initial state change for delay ticks (in units of PR_FASTHZ).
+ * initial state change for delay ticks (in units of seconds).
  */
 static int
 mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
-    const int delay)
+    struct mld_tparams *mtp, const int delay)
 {
-       struct ifnet            *ifp;
-       struct ifqueue          *ifq;
-       int                      error, retval, syncstates;
-       int                      odelay;
+       struct ifnet            *ifp;
+       struct ifqueue          *ifq;
+       int                      error, retval, syncstates;
+       int                      odelay;
 
        IN6M_LOCK_ASSERT_HELD(inm);
        MLI_LOCK_ASSERT_NOTHELD(mli);
+       VERIFY(mtp != NULL);
 
-       MLD_PRINTF(("%s: initial join %s on ifp %p(%s%d)\n",
+       MLD_PRINTF(("%s: initial join %s on ifp 0x%llx(%s)\n",
            __func__, ip6_sprintf(&inm->in6m_addr),
-           inm->in6m_ifp, inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
+           (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
+           if_name(inm->in6m_ifp)));
 
        error = 0;
        syncstates = 1;
@@ -2118,16 +2444,20 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
        VERIFY(mli->mli_ifp == ifp);
 
        /*
-        * Groups joined on loopback or marked as 'not reported',
-        * enter the MLD_SILENT_MEMBER state and
-        * are never reported in any protocol exchanges.
+        * Avoid MLD if group is :
+        * 1. Joined on loopback, OR
+        * 2. On a link that is marked MLIF_SILENT
+        * 3. rdar://problem/19227650 Is link local scoped and
+        *    on cellular interface
+        * 4. Is a type that should not be reported (node local
+        *    or all node link local multicast.
         * All other groups enter the appropriate state machine
         * for the version in use on this link.
-        * A link marked as MLIF_SILENT causes MLD to be completely
-        * disabled for the link.
         */
        if ((ifp->if_flags & IFF_LOOPBACK) ||
            (mli->mli_flags & MLIF_SILENT) ||
+           (IFNET_IS_CELLULAR(ifp) &&
+           IN6_IS_ADDR_MC_LINKLOCAL(&inm->in6m_addr)) ||
            !mld_is_addr_reported(&inm->in6m_addr)) {
                MLD_PRINTF(("%s: not kicking state machine for silent group\n",
                    __func__));
@@ -2160,21 +2490,21 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
                         * and delay sending the initial MLDv1 report
                         * by not transitioning to the IDLE state.
                         */
-                       odelay = MLD_RANDOM_DELAY(MLD_V1_MAX_RI * PR_SLOWHZ);
+                       odelay = MLD_RANDOM_DELAY(MLD_V1_MAX_RI);
                        if (delay) {
                                inm->in6m_timer = max(delay, odelay);
-                               current_state_timers_running6 = 1;
+                               mtp->cst = 1;
                        } else {
                                inm->in6m_state = MLD_IDLE_MEMBER;
                                error = mld_v1_transmit_report(inm,
-                                    MLD_LISTENER_REPORT);
+                                   MLD_LISTENER_REPORT);
 
                                IN6M_LOCK_ASSERT_HELD(inm);
                                MLI_LOCK_ASSERT_HELD(mli);
 
                                if (error == 0) {
                                        inm->in6m_timer = odelay;
-                                       current_state_timers_running6 = 1;
+                                       mtp->cst = 1;
                                }
                        }
                        break;
@@ -2196,6 +2526,7 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
                        IF_DRAIN(ifq);
                        retval = mld_v2_enqueue_group_record(ifq, inm, 1,
                            0, 0, (mli->mli_flags & MLIF_USEALLOW));
+                       mtp->cst = (ifq->ifq_len > 0);
                        MLD_PRINTF(("%s: enqueue record = %d\n",
                            __func__, retval));
                        if (retval <= 0) {
@@ -2206,7 +2537,7 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
                        /*
                         * Schedule transmission of pending state-change
                         * report up to RV times for this link. The timer
-                        * will fire at the next mld_fasttimo (~200ms),
+                        * will fire at the next mld_timeout (1 second)),
                         * giving us an opportunity to merge the reports.
                         *
                         * If a delay was provided to this function, only
@@ -2218,12 +2549,13 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
                                if (inm->in6m_sctimer > 1) {
                                        inm->in6m_sctimer =
                                            min(inm->in6m_sctimer, delay);
-                               } else
+                               } else {
                                        inm->in6m_sctimer = delay;
-                       } else
+                               }
+                       } else {
                                inm->in6m_sctimer = 1;
-                       state_change_timers_running6 = 1;
-
+                       }
+                       mtp->sct = 1;
                        error = 0;
                        break;
                }
@@ -2237,29 +2569,32 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifinfo *mli,
         */
        if (syncstates) {
                in6m_commit(inm);
-               MLD_PRINTF(("%s: T1 -> T0 for %s/%s%d\n", __func__,
+               MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
                    ip6_sprintf(&inm->in6m_addr),
-                   inm->in6m_ifp->if_name, ifp->if_unit));
+                   if_name(inm->in6m_ifp)));
        }
 
-       return (error);
+       return error;
 }
 
 /*
  * Issue an intermediate state change during the life-cycle.
  */
 static int
-mld_handle_state_change(struct in6_multi *inm, struct mld_ifinfo *mli)
+mld_handle_state_change(struct in6_multi *inm, struct mld_ifinfo *mli,
+    struct mld_tparams *mtp)
 {
-       struct ifnet            *ifp;
-       int                      retval;
+       struct ifnet            *ifp;
+       int                      retval = 0;
 
        IN6M_LOCK_ASSERT_HELD(inm);
        MLI_LOCK_ASSERT_NOTHELD(mli);
+       VERIFY(mtp != NULL);
 
-       MLD_PRINTF(("%s: state change for %s on ifp %p(%s%d)\n",
+       MLD_PRINTF(("%s: state change for %s on ifp 0x%llx(%s)\n",
            __func__, ip6_sprintf(&inm->in6m_addr),
-           inm->in6m_ifp, inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
+           (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
+           if_name(inm->in6m_ifp)));
 
        ifp = inm->in6m_ifp;
 
@@ -2277,31 +2612,37 @@ mld_handle_state_change(struct in6_multi *inm, struct mld_ifinfo *mli)
                }
                MLD_PRINTF(("%s: nothing to do\n", __func__));
                in6m_commit(inm);
-               MLD_PRINTF(("%s: T1 -> T0 for %s/%s%d\n", __func__,
+               MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
                    ip6_sprintf(&inm->in6m_addr),
-                   inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
-               return (0);
+                   if_name(inm->in6m_ifp)));
+               goto done;
        }
 
        IF_DRAIN(&inm->in6m_scq);
 
        retval = mld_v2_enqueue_group_record(&inm->in6m_scq, inm, 1, 0, 0,
            (mli->mli_flags & MLIF_USEALLOW));
+       mtp->cst = (inm->in6m_scq.ifq_len > 0);
        MLD_PRINTF(("%s: enqueue record = %d\n", __func__, retval));
        if (retval <= 0) {
                MLI_UNLOCK(mli);
-               return (-retval);
+               retval *= -1;
+               goto done;
+       } else {
+               retval = 0;
        }
+
        /*
         * If record(s) were enqueued, start the state-change
         * report timer for this group.
         */
        inm->in6m_scrv = mli->mli_rv;
        inm->in6m_sctimer = 1;
-       state_change_timers_running6 = 1;
+       mtp->sct = 1;
        MLI_UNLOCK(mli);
 
-       return (0);
+done:
+       return retval;
 }
 
 /*
@@ -2313,16 +2654,19 @@ mld_handle_state_change(struct in6_multi *inm, struct mld_ifinfo *mli)
  *  to INCLUDE {} for immediate transmission.
  */
 static void
-mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli)
+mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli,
+    struct mld_tparams *mtp)
 {
        int syncstates = 1;
 
        IN6M_LOCK_ASSERT_HELD(inm);
        MLI_LOCK_ASSERT_NOTHELD(mli);
+       VERIFY(mtp != NULL);
 
-       MLD_PRINTF(("%s: final leave %s on ifp %p(%s%d)\n",
+       MLD_PRINTF(("%s: final leave %s on ifp 0x%llx(%s)\n",
            __func__, ip6_sprintf(&inm->in6m_addr),
-           inm->in6m_ifp, inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
+           (uint64_t)VM_KERNEL_ADDRPERM(inm->in6m_ifp),
+           if_name(inm->in6m_ifp)));
 
        switch (inm->in6m_state) {
        case MLD_NOT_MEMBER:
@@ -2344,7 +2688,9 @@ mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli)
                                    "mode\n", __func__);
                                /* NOTREACHED */
                        }
-                       mld_v1_transmit_report(inm, MLD_LISTENER_DONE);
+                       /* scheduler timer if enqueue is successful */
+                       mtp->cst = (mld_v1_transmit_report(inm,
+                           MLD_LISTENER_DONE) == 0);
 
                        IN6M_LOCK_ASSERT_HELD(inm);
                        MLI_LOCK_ASSERT_HELD(mli);
@@ -2354,16 +2700,16 @@ mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli)
                        /*
                         * Stop group timer and all pending reports.
                         * Immediately enqueue a state-change report
-                        * TO_IN {} to be sent on the next fast timeout,
+                        * TO_IN {} to be sent on the next timeout,
                         * giving us an opportunity to merge reports.
                         */
                        IF_DRAIN(&inm->in6m_scq);
                        inm->in6m_timer = 0;
                        inm->in6m_scrv = mli->mli_rv;
-                       MLD_PRINTF(("%s: Leaving %s/%s%d with %d "
+                       MLD_PRINTF(("%s: Leaving %s/%s with %d "
                            "pending retransmissions.\n", __func__,
                            ip6_sprintf(&inm->in6m_addr),
-                           inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit,
+                           if_name(inm->in6m_ifp),
                            inm->in6m_scrv));
                        if (inm->in6m_scrv == 0) {
                                inm->in6m_state = MLD_NOT_MEMBER;
@@ -2384,15 +2730,16 @@ mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli)
                                VERIFY(inm->in6m_nrelecnt != 0);
 
                                retval = mld_v2_enqueue_group_record(
-                                   &inm->in6m_scq, inm, 1, 0, 0,
-                                   (mli->mli_flags & MLIF_USEALLOW));
+                                       &inm->in6m_scq, inm, 1, 0, 0,
+                                       (mli->mli_flags & MLIF_USEALLOW));
+                               mtp->cst = (inm->in6m_scq.ifq_len > 0);
                                KASSERT(retval != 0,
                                    ("%s: enqueue record = %d\n", __func__,
-                                    retval));
+                                   retval));
 
                                inm->in6m_state = MLD_LEAVING_MEMBER;
                                inm->in6m_sctimer = 1;
-                               state_change_timers_running6 = 1;
+                               mtp->sct = 1;
                                syncstates = 0;
                        }
                }
@@ -2407,13 +2754,13 @@ mld_final_leave(struct in6_multi *inm, struct mld_ifinfo *mli)
 
        if (syncstates) {
                in6m_commit(inm);
-               MLD_PRINTF(("%s: T1 -> T0 for %s/%s%d\n", __func__,
+               MLD_PRINTF(("%s: T1 -> T0 for %s/%s\n", __func__,
                    ip6_sprintf(&inm->in6m_addr),
-                   inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
+                   if_name(inm->in6m_ifp)));
                inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED;
-               MLD_PRINTF(("%s: T1 now MCAST_UNDEFINED for %p/%s%d\n",
-                   __func__, &inm->in6m_addr, inm->in6m_ifp->if_name,
-                   inm->in6m_ifp->if_unit));
+               MLD_PRINTF(("%s: T1 now MCAST_UNDEFINED for 0x%llx/%s\n",
+                   __func__, (uint64_t)VM_KERNEL_ADDRPERM(&inm->in6m_addr),
+                   if_name(inm->in6m_ifp)));
        }
 }
 
@@ -2449,17 +2796,17 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
     const int is_state_change, const int is_group_query,
     const int is_source_query, const int use_block_allow)
 {
-       struct mldv2_record      mr;
-       struct mldv2_record     *pmr;
-       struct ifnet            *ifp;
-       struct ip6_msource      *ims, *nims;
-       struct mbuf             *m0, *m, *md;
-       int                      error, is_filter_list_change;
-       int                      minrec0len, m0srcs, msrcs, nbytes, off;
-       int                      record_has_sources;
-       int                      now;
-       int                      type;
-       uint8_t                  mode;
+       struct mldv2_record      mr;
+       struct mldv2_record     *pmr;
+       struct ifnet            *ifp;
+       struct ip6_msource      *ims, *nims;
+       struct mbuf             *m0, *m, *md;
+       int                      error, is_filter_list_change;
+       int                      minrec0len, m0srcs, msrcs, nbytes, off;
+       int                      record_has_sources;
+       int                      now;
+       int                      type;
+       uint8_t                  mode;
 
        IN6M_LOCK_ASSERT_HELD(inm);
        MLI_LOCK_ASSERT_HELD(inm->in6m_mli);
@@ -2484,8 +2831,9 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
         * the generation of source records.
         */
        if (inm->in6m_st[0].iss_asm > 0 && inm->in6m_st[1].iss_asm > 0 &&
-           inm->in6m_nsrc == 0)
+           inm->in6m_nsrc == 0) {
                record_has_sources = 0;
+       }
 
        if (is_state_change) {
                /*
@@ -2529,8 +2877,9 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
                                        }
                                } else {
                                        type = MLD_CHANGE_TO_INCLUDE_MODE;
-                                       if (mode == MCAST_UNDEFINED)
+                                       if (mode == MCAST_UNDEFINED) {
                                                record_has_sources = 0;
+                                       }
                                }
                        }
                } else {
@@ -2555,14 +2904,15 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
        /*
         * Generate the filter list changes using a separate function.
         */
-       if (is_filter_list_change)
-               return (mld_v2_enqueue_filter_change(ifq, inm));
+       if (is_filter_list_change) {
+               return mld_v2_enqueue_filter_change(ifq, inm);
+       }
 
        if (type == MLD_DO_NOTHING) {
-               MLD_PRINTF(("%s: nothing to do for %s/%s%d\n",
+               MLD_PRINTF(("%s: nothing to do for %s/%s\n",
                    __func__, ip6_sprintf(&inm->in6m_addr),
-                   inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
-               return (0);
+                   if_name(inm->in6m_ifp)));
+               return 0;
        }
 
        /*
@@ -2571,12 +2921,13 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
         * ideally more.
         */
        minrec0len = sizeof(struct mldv2_record);
-       if (record_has_sources)
+       if (record_has_sources) {
                minrec0len += sizeof(struct in6_addr);
-       MLD_PRINTF(("%s: queueing %s for %s/%s%d\n", __func__,
+       }
+       MLD_PRINTF(("%s: queueing %s for %s/%s\n", __func__,
            mld_rec_type_to_str(type),
            ip6_sprintf(&inm->in6m_addr),
-           inm->in6m_ifp->if_name, inm->in6m_ifp->if_unit));
+           if_name(inm->in6m_ifp)));
 
        /*
         * Check if we have a packet in the tail of the queue for this
@@ -2591,26 +2942,31 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
            m0 != NULL &&
            (m0->m_pkthdr.vt_nrecs + 1 <= MLD_V2_REPORT_MAXRECS) &&
            (m0->m_pkthdr.len + minrec0len) <
-            (ifp->if_mtu - MLD_MTUSPACE)) {
+           (ifp->if_mtu - MLD_MTUSPACE)) {
                m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
-                           sizeof(struct mldv2_record)) /
-                           sizeof(struct in6_addr);
+                   sizeof(struct mldv2_record)) /
+                   sizeof(struct in6_addr);
                m = m0;
                MLD_PRINTF(("%s: use existing packet\n", __func__));
        } else {
                if (IF_QFULL(ifq)) {
                        MLD_PRINTF(("%s: outbound queue full\n", __func__));
-                       return (-ENOMEM);
+                       return -ENOMEM;
                }
                m = NULL;
                m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
                    sizeof(struct mldv2_record)) / sizeof(struct in6_addr);
-               if (!is_state_change && !is_group_query)
+               if (!is_state_change && !is_group_query) {
                        m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
-               if (m == NULL)
+               }
+               if (m == NULL) {
                        m = m_gethdr(M_DONTWAIT, MT_DATA);
-               if (m == NULL)
-                       return (-ENOMEM);
+               }
+               if (m == NULL) {
+                       return -ENOMEM;
+               }
+
+               mld_save_context(m, ifp);
 
                MLD_PRINTF(("%s: allocated first packet\n", __func__));
        }
@@ -2625,10 +2981,11 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
        mr.mr_addr = inm->in6m_addr;
        in6_clearscope(&mr.mr_addr);
        if (!m_append(m, sizeof(struct mldv2_record), (void *)&mr)) {
-               if (m != m0)
+               if (m != m0) {
                        m_freem(m);
+               }
                MLD_PRINTF(("%s: m_append() failed.\n", __func__));
-               return (-ENOMEM);
+               return -ENOMEM;
        }
        nbytes += sizeof(struct mldv2_record);
 
@@ -2667,7 +3024,7 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
                        MLD_PRINTF(("%s: node is %d\n", __func__, now));
                        if ((now != mode) ||
                            (now == mode &&
-                            (!use_block_allow && mode == MCAST_UNDEFINED))) {
+                           (!use_block_allow && mode == MCAST_UNDEFINED))) {
                                MLD_PRINTF(("%s: skip node\n", __func__));
                                continue;
                        }
@@ -2679,16 +3036,18 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
                        MLD_PRINTF(("%s: append node\n", __func__));
                        if (!m_append(m, sizeof(struct in6_addr),
                            (void *)&ims->im6s_addr)) {
-                               if (m != m0)
+                               if (m != m0) {
                                        m_freem(m);
+                               }
                                MLD_PRINTF(("%s: m_append() failed.\n",
                                    __func__));
-                               return (-ENOMEM);
+                               return -ENOMEM;
                        }
                        nbytes += sizeof(struct in6_addr);
                        ++msrcs;
-                       if (msrcs == m0srcs)
+                       if (msrcs == m0srcs) {
                                break;
+                       }
                }
                MLD_PRINTF(("%s: msrcs is %d this packet\n", __func__,
                    msrcs));
@@ -2698,9 +3057,10 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
 
        if (is_source_query && msrcs == 0) {
                MLD_PRINTF(("%s: no recorded sources to report\n", __func__));
-               if (m != m0)
+               if (m != m0) {
                        m_freem(m);
-               return (0);
+               }
+               return 0;
        }
 
        /*
@@ -2709,7 +3069,6 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
        if (m != m0) {
                MLD_PRINTF(("%s: enqueueing first packet\n", __func__));
                m->m_pkthdr.vt_nrecs = 1;
-               m->m_pkthdr.rcvif = ifp;
                IF_ENQUEUE(ifq, m);
        } else {
                m->m_pkthdr.vt_nrecs++;
@@ -2717,8 +3076,9 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
        /*
         * No further work needed if no source list in packet(s).
         */
-       if (!record_has_sources)
-               return (nbytes);
+       if (!record_has_sources) {
+               return nbytes;
+       }
 
        /*
         * Whilst sources remain to be announced, we need to allocate
@@ -2728,22 +3088,26 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
        while (nims != NULL) {
                if (IF_QFULL(ifq)) {
                        MLD_PRINTF(("%s: outbound queue full\n", __func__));
-                       return (-ENOMEM);
+                       return -ENOMEM;
                }
                m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
-               if (m == NULL)
+               if (m == NULL) {
                        m = m_gethdr(M_DONTWAIT, MT_DATA);
-               if (m == NULL)
-                       return (-ENOMEM);
+               }
+               if (m == NULL) {
+                       return -ENOMEM;
+               }
+               mld_save_context(m, ifp);
                md = m_getptr(m, 0, &off);
                pmr = (struct mldv2_record *)(mtod(md, uint8_t *) + off);
                MLD_PRINTF(("%s: allocated next packet\n", __func__));
 
                if (!m_append(m, sizeof(struct mldv2_record), (void *)&mr)) {
-                       if (m != m0)
+                       if (m != m0) {
                                m_freem(m);
+                       }
                        MLD_PRINTF(("%s: m_append() failed.\n", __func__));
-                       return (-ENOMEM);
+                       return -ENOMEM;
                }
                m->m_pkthdr.vt_nrecs = 1;
                nbytes += sizeof(struct mldv2_record);
@@ -2758,7 +3122,7 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
                        now = im6s_get_mode(inm, ims, 1);
                        if ((now != mode) ||
                            (now == mode &&
-                            (!use_block_allow && mode == MCAST_UNDEFINED))) {
+                           (!use_block_allow && mode == MCAST_UNDEFINED))) {
                                MLD_PRINTF(("%s: skip node\n", __func__));
                                continue;
                        }
@@ -2770,25 +3134,26 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
                        MLD_PRINTF(("%s: append node\n", __func__));
                        if (!m_append(m, sizeof(struct in6_addr),
                            (void *)&ims->im6s_addr)) {
-                               if (m != m0)
+                               if (m != m0) {
                                        m_freem(m);
+                               }
                                MLD_PRINTF(("%s: m_append() failed.\n",
                                    __func__));
-                               return (-ENOMEM);
+                               return -ENOMEM;
                        }
                        ++msrcs;
-                       if (msrcs == m0srcs)
+                       if (msrcs == m0srcs) {
                                break;
+                       }
                }
                pmr->mr_numsrc = htons(msrcs);
                nbytes += (msrcs * sizeof(struct in6_addr));
 
                MLD_PRINTF(("%s: enqueueing next packet\n", __func__));
-               m->m_pkthdr.rcvif = ifp;
                IF_ENQUEUE(ifq, m);
        }
 
-       return (nbytes);
+       return nbytes;
 }
 
 /*
@@ -2797,9 +3162,9 @@ mld_v2_enqueue_group_record(struct ifqueue *ifq, struct in6_multi *inm,
  * current filter modes on each ip_msource node.
  */
 typedef enum {
-       REC_NONE = 0x00,        /* MCAST_UNDEFINED */
-       REC_ALLOW = 0x01,       /* MCAST_INCLUDE */
-       REC_BLOCK = 0x02,       /* MCAST_EXCLUDE */
+       REC_NONE = 0x00,        /* MCAST_UNDEFINED */
+       REC_ALLOW = 0x01,       /* MCAST_INCLUDE */
+       REC_BLOCK = 0x02,       /* MCAST_EXCLUDE */
        REC_FULL = REC_ALLOW | REC_BLOCK
 } rectype_t;
 
@@ -2829,35 +3194,36 @@ mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
 {
        static const int MINRECLEN =
            sizeof(struct mldv2_record) + sizeof(struct in6_addr);
-       struct ifnet            *ifp;
-       struct mldv2_record      mr;
-       struct mldv2_record     *pmr;
-       struct ip6_msource      *ims, *nims;
-       struct mbuf             *m, *m0, *md;
-       int                      m0srcs, nbytes, npbytes, off, rsrcs, schanged;
-       int                      nallow, nblock;
-       uint8_t                  mode, now, then;
-       rectype_t                crt, drt, nrt;
+       struct ifnet            *ifp;
+       struct mldv2_record      mr;
+       struct mldv2_record     *pmr;
+       struct ip6_msource      *ims, *nims;
+       struct mbuf             *m, *m0, *md;
+       int                      m0srcs, nbytes, npbytes, off, rsrcs, schanged;
+       int                      nallow, nblock;
+       uint8_t                  mode, now, then;
+       rectype_t                crt, drt, nrt;
 
        IN6M_LOCK_ASSERT_HELD(inm);
 
        if (inm->in6m_nsrc == 0 ||
-           (inm->in6m_st[0].iss_asm > 0 && inm->in6m_st[1].iss_asm > 0))
-               return (0);
-
-       ifp = inm->in6m_ifp;                    /* interface */
-       mode = inm->in6m_st[1].iss_fmode;       /* filter mode at t1 */
-       crt = REC_NONE; /* current group record type */
-       drt = REC_NONE; /* mask of completed group record types */
-       nrt = REC_NONE; /* record type for current node */
-       m0srcs = 0;     /* # source which will fit in current mbuf chain */
-       npbytes = 0;    /* # of bytes appended this packet */
-       nbytes = 0;     /* # of bytes appended to group's state-change queue */
-       rsrcs = 0;      /* # sources encoded in current record */
-       schanged = 0;   /* # nodes encoded in overall filter change */
-       nallow = 0;     /* # of source entries in ALLOW_NEW */
-       nblock = 0;     /* # of source entries in BLOCK_OLD */
-       nims = NULL;    /* next tree node pointer */
+           (inm->in6m_st[0].iss_asm > 0 && inm->in6m_st[1].iss_asm > 0)) {
+               return 0;
+       }
+
+       ifp = inm->in6m_ifp;                    /* interface */
+       mode = inm->in6m_st[1].iss_fmode;       /* filter mode at t1 */
+       crt = REC_NONE; /* current group record type */
+       drt = REC_NONE; /* mask of completed group record types */
+       nrt = REC_NONE; /* record type for current node */
+       m0srcs = 0;     /* # source which will fit in current mbuf chain */
+       npbytes = 0;    /* # of bytes appended this packet */
+       nbytes = 0;     /* # of bytes appended to group's state-change queue */
+       rsrcs = 0;      /* # sources encoded in current record */
+       schanged = 0;   /* # nodes encoded in overall filter change */
+       nallow = 0;     /* # of source entries in ALLOW_NEW */
+       nblock = 0;     /* # of source entries in BLOCK_OLD */
+       nims = NULL;    /* next tree node pointer */
 
        /*
         * For each possible filter record mode.
@@ -2871,25 +3237,27 @@ mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
                        m0 = ifq->ifq_tail;
                        if (m0 != NULL &&
                            (m0->m_pkthdr.vt_nrecs + 1 <=
-                            MLD_V2_REPORT_MAXRECS) &&
+                           MLD_V2_REPORT_MAXRECS) &&
                            (m0->m_pkthdr.len + MINRECLEN) <
-                            (ifp->if_mtu - MLD_MTUSPACE)) {
+                           (ifp->if_mtu - MLD_MTUSPACE)) {
                                m = m0;
                                m0srcs = (ifp->if_mtu - m0->m_pkthdr.len -
-                                           sizeof(struct mldv2_record)) /
-                                           sizeof(struct in6_addr);
+                                   sizeof(struct mldv2_record)) /
+                                   sizeof(struct in6_addr);
                                MLD_PRINTF(("%s: use previous packet\n",
                                    __func__));
                        } else {
                                m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
-                               if (m == NULL)
+                               if (m == NULL) {
                                        m = m_gethdr(M_DONTWAIT, MT_DATA);
+                               }
                                if (m == NULL) {
                                        MLD_PRINTF(("%s: m_get*() failed\n",
                                            __func__));
-                                       return (-ENOMEM);
+                                       return -ENOMEM;
                                }
                                m->m_pkthdr.vt_nrecs = 0;
+                               mld_save_context(m, ifp);
                                m0srcs = (ifp->if_mtu - MLD_MTUSPACE -
                                    sizeof(struct mldv2_record)) /
                                    sizeof(struct in6_addr);
@@ -2908,11 +3276,12 @@ mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
                        mr.mr_addr = inm->in6m_addr;
                        in6_clearscope(&mr.mr_addr);
                        if (!m_append(m, sizeof(mr), (void *)&mr)) {
-                               if (m != m0)
+                               if (m != m0) {
                                        m_freem(m);
+                               }
                                MLD_PRINTF(("%s: m_append() failed\n",
                                    __func__));
-                               return (-ENOMEM);
+                               return -ENOMEM;
                        }
                        npbytes += sizeof(struct mldv2_record);
                        if (m != m0) {
@@ -2961,24 +3330,28 @@ mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
                                        continue;
                                }
                                nrt = (rectype_t)now;
-                               if (nrt == REC_NONE)
+                               if (nrt == REC_NONE) {
                                        nrt = (rectype_t)(~mode & REC_FULL);
+                               }
                                if (schanged++ == 0) {
                                        crt = nrt;
-                               } else if (crt != nrt)
+                               } else if (crt != nrt) {
                                        continue;
+                               }
                                if (!m_append(m, sizeof(struct in6_addr),
                                    (void *)&ims->im6s_addr)) {
-                                       if (m != m0)
+                                       if (m != m0) {
                                                m_freem(m);
+                                       }
                                        MLD_PRINTF(("%s: m_append() failed\n",
                                            __func__));
-                                       return (-ENOMEM);
+                                       return -ENOMEM;
                                }
                                nallow += !!(crt == REC_ALLOW);
                                nblock += !!(crt == REC_BLOCK);
-                               if (++rsrcs == m0srcs)
+                               if (++rsrcs == m0srcs) {
                                        break;
+                               }
                        }
                        /*
                         * If we did not append any tree nodes on this
@@ -2994,24 +3367,25 @@ mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
                                        MLD_PRINTF(("%s: m_adj(m, -mr)\n",
                                            __func__));
                                        m_adj(m, -((int)sizeof(
-                                           struct mldv2_record)));
+                                                   struct mldv2_record)));
                                }
                                continue;
                        }
                        npbytes += (rsrcs * sizeof(struct in6_addr));
-                       if (crt == REC_ALLOW)
+                       if (crt == REC_ALLOW) {
                                pmr->mr_type = MLD_ALLOW_NEW_SOURCES;
-                       else if (crt == REC_BLOCK)
+                       } else if (crt == REC_BLOCK) {
                                pmr->mr_type = MLD_BLOCK_OLD_SOURCES;
+                       }
                        pmr->mr_numsrc = htons(rsrcs);
                        /*
                         * Count the new group record, and enqueue this
                         * packet if it wasn't already queued.
                         */
                        m->m_pkthdr.vt_nrecs++;
-                       m->m_pkthdr.rcvif = ifp;
-                       if (m != m0)
+                       if (m != m0) {
                                IF_ENQUEUE(ifq, m);
+                       }
                        nbytes += npbytes;
                } while (nims != NULL);
                drt |= crt;
@@ -3021,19 +3395,19 @@ mld_v2_enqueue_filter_change(struct ifqueue *ifq, struct in6_multi *inm)
        MLD_PRINTF(("%s: queued %d ALLOW_NEW, %d BLOCK_OLD\n", __func__,
            nallow, nblock));
 
-       return (nbytes);
+       return nbytes;
 }
 
 static int
 mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq)
 {
-       struct ifqueue  *gq;
-       struct mbuf     *m;             /* pending state-change */
-       struct mbuf     *m0;            /* copy of pending state-change */
-       struct mbuf     *mt;            /* last state-change in packet */
-       struct mbuf     *n;
-       int              docopy, domerge;
-       u_int            recslen;
+       struct ifqueue  *gq;
+       struct mbuf     *m;             /* pending state-change */
+       struct mbuf     *m0;            /* copy of pending state-change */
+       struct mbuf     *mt;            /* last state-change in packet */
+       struct mbuf     *n;
+       int              docopy, domerge;
+       u_int            recslen;
 
        IN6M_LOCK_ASSERT_HELD(inm);
 
@@ -3045,14 +3419,15 @@ mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq)
         * If there are further pending retransmissions, make a writable
         * copy of each queued state-change message before merging.
         */
-       if (inm->in6m_scrv > 0)
+       if (inm->in6m_scrv > 0) {
                docopy = 1;
+       }
 
        gq = &inm->in6m_scq;
 #ifdef MLD_DEBUG
        if (gq->ifq_head == NULL) {
-               MLD_PRINTF(("%s: WARNING: queue for inm %p is empty\n",
-                   __func__, inm));
+               MLD_PRINTF(("%s: WARNING: queue for inm 0x%llx is empty\n",
+                   __func__, (uint64_t)VM_KERNEL_ADDRPERM(inm)));
        }
 #endif
 
@@ -3079,13 +3454,15 @@ mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq)
                            m->m_pkthdr.vt_nrecs <=
                            MLD_V2_REPORT_MAXRECS) &&
                            (mt->m_pkthdr.len + recslen <=
-                           (inm->in6m_ifp->if_mtu - MLD_MTUSPACE)))
+                           (inm->in6m_ifp->if_mtu - MLD_MTUSPACE))) {
                                domerge = 1;
+                       }
                }
 
                if (!domerge && IF_QFULL(gq)) {
                        MLD_PRINTF(("%s: outbound queue full, skipping whole "
-                           "packet %p\n", __func__, m));
+                           "packet 0x%llx\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(m)));
                        n = m->m_nextpkt;
                        if (!docopy) {
                                IF_REMQUEUE(gq, m);
@@ -3096,30 +3473,35 @@ mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq)
                }
 
                if (!docopy) {
-                       MLD_PRINTF(("%s: dequeueing %p\n", __func__, m));
+                       MLD_PRINTF(("%s: dequeueing 0x%llx\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(m)));
                        n = m->m_nextpkt;
                        IF_REMQUEUE(gq, m);
                        m0 = m;
                        m = n;
                } else {
-                       MLD_PRINTF(("%s: copying %p\n", __func__, m));
+                       MLD_PRINTF(("%s: copying 0x%llx\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(m)));
                        m0 = m_dup(m, M_NOWAIT);
-                       if (m0 == NULL)
-                               return (ENOMEM);
+                       if (m0 == NULL) {
+                               return ENOMEM;
+                       }
                        m0->m_nextpkt = NULL;
                        m = m->m_nextpkt;
                }
 
                if (!domerge) {
-                       MLD_PRINTF(("%s: queueing %p to ifscq %p)\n",
-                           __func__, m0, ifscq));
-                       m0->m_pkthdr.rcvif = inm->in6m_ifp;
+                       MLD_PRINTF(("%s: queueing 0x%llx to ifscq 0x%llx)\n",
+                           __func__, (uint64_t)VM_KERNEL_ADDRPERM(m0),
+                           (uint64_t)VM_KERNEL_ADDRPERM(ifscq)));
                        IF_ENQUEUE(ifscq, m0);
                } else {
-                       struct mbuf *mtl;       /* last mbuf of packet mt */
+                       struct mbuf *mtl;       /* last mbuf of packet mt */
 
-                       MLD_PRINTF(("%s: merging %p with ifscq tail %p)\n",
-                           __func__, m0, mt));
+                       MLD_PRINTF(("%s: merging 0x%llx with ifscq tail "
+                           "0x%llx)\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(m0),
+                           (uint64_t)VM_KERNEL_ADDRPERM(mt)));
 
                        mtl = m_last(mt);
                        m0->m_flags &= ~M_PKTHDR;
@@ -3131,19 +3513,19 @@ mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq)
                }
        }
 
-       return (0);
+       return 0;
 }
 
 /*
  * Respond to a pending MLDv2 General Query.
  */
-static void
+static uint32_t
 mld_v2_dispatch_general_query(struct mld_ifinfo *mli)
 {
-       struct ifnet            *ifp;
-       struct in6_multi        *inm;
-       struct in6_multistep    step;
-       int                      retval;
+       struct ifnet            *ifp;
+       struct in6_multi        *inm;
+       struct in6_multistep    step;
+       int                      retval;
 
        MLI_LOCK_ASSERT_HELD(mli);
 
@@ -3156,8 +3538,9 @@ mld_v2_dispatch_general_query(struct mld_ifinfo *mli)
        IN6_FIRST_MULTI(step, inm);
        while (inm != NULL) {
                IN6M_LOCK(inm);
-               if (inm->in6m_ifp != ifp)
+               if (inm->in6m_ifp != ifp) {
                        goto next;
+               }
 
                switch (inm->in6m_state) {
                case MLD_NOT_MEMBER:
@@ -3188,17 +3571,18 @@ next:
        in6_multihead_lock_done();
 
        MLI_LOCK(mli);
-       mld_dispatch_queue(mli, &mli->mli_gq, MLD_MAX_RESPONSE_BURST);
+       mld_dispatch_queue_locked(mli, &mli->mli_gq, MLD_MAX_RESPONSE_BURST);
        MLI_LOCK_ASSERT_HELD(mli);
 
        /*
-        * Slew transmission of bursts over 500ms intervals.
+        * Slew transmission of bursts over 1 second intervals.
         */
        if (mli->mli_gq.ifq_head != NULL) {
                mli->mli_v2_timer = 1 + MLD_RANDOM_DELAY(
-                   MLD_RESPONSE_BURST_INTERVAL);
-               interface_timers_running6 = 1;
+                       MLD_RESPONSE_BURST_INTERVAL);
        }
+
+       return mli->mli_v2_timer;
 }
 
 /*
@@ -3209,26 +3593,28 @@ next:
 static void
 mld_dispatch_packet(struct mbuf *m)
 {
-       struct ip6_moptions     *im6o;
-       struct ifnet            *ifp;
-       struct ifnet            *oifp = NULL;
-       struct mbuf             *m0;
-       struct mbuf             *md;
-       struct ip6_hdr          *ip6;
-       struct mld_hdr          *mld;
-       int                      error;
-       int                      off;
-       int                      type;
-
-       MLD_PRINTF(("%s: transmit %p\n", __func__, m));
+       struct ip6_moptions     *im6o;
+       struct ifnet            *ifp;
+       struct ifnet            *oifp = NULL;
+       struct mbuf             *m0;
+       struct mbuf             *md;
+       struct ip6_hdr          *ip6;
+       struct mld_hdr          *mld;
+       int                      error;
+       int                      off;
+       int                      type;
+
+       MLD_PRINTF(("%s: transmit 0x%llx\n", __func__,
+           (uint64_t)VM_KERNEL_ADDRPERM(m)));
 
        /*
         * Check if the ifnet is still attached.
         */
-       ifp = m->m_pkthdr.rcvif;
+       ifp = mld_restore_context(m);
        if (ifp == NULL || !ifnet_is_attached(ifp, 0)) {
-               MLD_PRINTF(("%s: dropped %p as ifindex %u went away.\n",
-                   __func__, m, (u_int)if_index));
+               MLD_PRINTF(("%s: dropped 0x%llx as ifindex %u went away.\n",
+                   __func__, (uint64_t)VM_KERNEL_ADDRPERM(m),
+                   (u_int)if_index));
                m_freem(m);
                ip6stat.ip6s_noroute++;
                return;
@@ -3241,11 +3627,7 @@ mld_dispatch_packet(struct mbuf *m)
        }
 
        im6o->im6o_multicast_hlim  = 1;
-#if MROUTING
-       im6o->im6o_multicast_loop = (ip6_mrouter != NULL);
-#else
        im6o->im6o_multicast_loop = 0;
-#endif
        im6o->im6o_multicast_ifp = ifp;
 
        if (m->m_flags & M_MLDV1) {
@@ -3253,7 +3635,8 @@ mld_dispatch_packet(struct mbuf *m)
        } else {
                m0 = mld_v2_encap_report(ifp, m);
                if (m0 == NULL) {
-                       MLD_PRINTF(("%s: dropped %p\n", __func__, m));
+                       MLD_PRINTF(("%s: dropped 0x%llx\n", __func__,
+                           (uint64_t)VM_KERNEL_ADDRPERM(m)));
                        /*
                         * mld_v2_encap_report() has already freed our mbuf.
                         */
@@ -3263,20 +3646,12 @@ mld_dispatch_packet(struct mbuf *m)
                }
        }
 
+       mld_scrub_context(m0);
        m->m_flags &= ~(M_PROTOFLAGS);
        m0->m_pkthdr.rcvif = lo_ifp;
 
        ip6 = mtod(m0, struct ip6_hdr *);
-#if 0
-       (void) in6_setscope(&ip6->ip6_dst, ifp, NULL);  /* XXX LOR */
-#else
-       /*
-        * XXX XXX Break some KPI rules to prevent an LOR which would
-        * occur if we called in6_setscope() at transmission.
-        * See comments at top of file.
-        */
-       MLD_EMBEDSCOPE(&ip6->ip6_dst, ifp->if_index);
-#endif
+       (void) in6_setscope(&ip6->ip6_dst, ifp, NULL);
 
        /*
         * Retrieve the ICMPv6 type before handoff to ip6_output(),
@@ -3286,15 +3661,25 @@ mld_dispatch_packet(struct mbuf *m)
        mld = (struct mld_hdr *)(mtod(md, uint8_t *) + off);
        type = mld->mld_type;
 
+       if (ifp->if_eflags & IFEF_TXSTART) {
+               /*
+                * Use control service class if the outgoing
+                * interface supports transmit-start model.
+                */
+               (void) m_set_service_class(m0, MBUF_SC_CTL);
+       }
+
        error = ip6_output(m0, &mld_po, NULL, IPV6_UNSPECSRC, im6o,
            &oifp, NULL);
 
        IM6O_REMREF(im6o);
 
        if (error) {
-               MLD_PRINTF(("%s: ip6_output(%p) = %d\n", __func__, m0, error));
-               if (oifp != NULL)
+               MLD_PRINTF(("%s: ip6_output(0x%llx) = %d\n", __func__,
+                   (uint64_t)VM_KERNEL_ADDRPERM(m0), error));
+               if (oifp != NULL) {
                        ifnet_release(oifp);
+               }
                return;
        }
 
@@ -3326,27 +3711,29 @@ mld_dispatch_packet(struct mbuf *m)
 static struct mbuf *
 mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m)
 {
-       struct mbuf             *mh;
-       struct mldv2_report     *mld;
-       struct ip6_hdr          *ip6;
-       struct in6_ifaddr       *ia;
-       int                      mldreclen;
+       struct mbuf             *mh;
+       struct mldv2_report     *mld;
+       struct ip6_hdr          *ip6;
+       struct in6_ifaddr       *ia;
+       int                      mldreclen;
 
        VERIFY(m->m_flags & M_PKTHDR);
 
        /*
         * RFC3590: OK to send as :: or tentative during DAD.
         */
-       ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST);
-       if (ia == NULL)
+       ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
+       if (ia == NULL) {
                MLD_PRINTF(("%s: warning: ia is NULL\n", __func__));
+       }
 
        MGETHDR(mh, M_DONTWAIT, MT_HEADER);
        if (mh == NULL) {
-               if (ia != NULL)
+               if (ia != NULL) {
                        IFA_REMREF(&ia->ia_ifa);
+               }
                m_freem(m);
-               return (NULL);
+               return NULL;
        }
        MH_ALIGN(mh, sizeof(struct ip6_hdr) + sizeof(struct mldv2_report));
 
@@ -3362,8 +3749,9 @@ mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m)
        ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
        ip6->ip6_vfc |= IPV6_VERSION;
        ip6->ip6_nxt = IPPROTO_ICMPV6;
-       if (ia != NULL)
+       if (ia != NULL) {
                IFA_LOCK(&ia->ia_ifa);
+       }
        ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
        if (ia != NULL) {
                IFA_UNLOCK(&ia->ia_ifa);
@@ -3385,7 +3773,7 @@ mld_v2_encap_report(struct ifnet *ifp, struct mbuf *m)
        mh->m_next = m;
        mld->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6,
            sizeof(struct ip6_hdr), sizeof(struct mldv2_report) + mldreclen);
-       return (mh);
+       return mh;
 }
 
 #ifdef MLD_DEBUG
@@ -3393,26 +3781,20 @@ static const char *
 mld_rec_type_to_str(const int type)
 {
        switch (type) {
-               case MLD_CHANGE_TO_EXCLUDE_MODE:
-                       return "TO_EX";
-                       break;
-               case MLD_CHANGE_TO_INCLUDE_MODE:
-                       return "TO_IN";
-                       break;
-               case MLD_MODE_IS_EXCLUDE:
-                       return "MODE_EX";
-                       break;
-               case MLD_MODE_IS_INCLUDE:
-                       return "MODE_IN";
-                       break;
-               case MLD_ALLOW_NEW_SOURCES:
-                       return "ALLOW_NEW";
-                       break;
-               case MLD_BLOCK_OLD_SOURCES:
-                       return "BLOCK_OLD";
-                       break;
-               default:
-                       break;
+       case MLD_CHANGE_TO_EXCLUDE_MODE:
+               return "TO_EX";
+       case MLD_CHANGE_TO_INCLUDE_MODE:
+               return "TO_IN";
+       case MLD_MODE_IS_EXCLUDE:
+               return "MODE_EX";
+       case MLD_MODE_IS_INCLUDE:
+               return "MODE_IN";
+       case MLD_ALLOW_NEW_SOURCES:
+               return "ALLOW_NEW";
+       case MLD_BLOCK_OLD_SOURCES:
+               return "BLOCK_OLD";
+       default:
+               break;
        }
        return "unknown";
 }
@@ -3421,14 +3803,13 @@ mld_rec_type_to_str(const int type)
 void
 mld_init(void)
 {
-
        MLD_PRINTF(("%s: initializing\n", __func__));
 
-        /* Setup lock group and attribute for mld6_mtx */
-        mld_mtx_grp_attr = lck_grp_attr_alloc_init();
-        mld_mtx_grp = lck_grp_alloc_init("mld_mtx\n", mld_mtx_grp_attr);
-        mld_mtx_attr = lck_attr_alloc_init();
-        lck_mtx_init(&mld_mtx, mld_mtx_grp, mld_mtx_attr);
+       /* Setup lock group and attribute for mld_mtx */
+       mld_mtx_grp_attr = lck_grp_attr_alloc_init();
+       mld_mtx_grp = lck_grp_alloc_init("mld_mtx\n", mld_mtx_grp_attr);
+       mld_mtx_attr = lck_attr_alloc_init();
+       lck_mtx_init(&mld_mtx, mld_mtx_grp, mld_mtx_attr);
 
        ip6_initpktopts(&mld_po);
        mld_po.ip6po_hlim = 1;
@@ -3437,7 +3818,7 @@ mld_init(void)
        mld_po.ip6po_flags = IP6PO_DONTFRAG;
        LIST_INIT(&mli_head);
 
-       mli_size = sizeof (struct mld_ifinfo);
+       mli_size = sizeof(struct mld_ifinfo);
        mli_zone = zinit(mli_size, MLI_ZONE_MAX * mli_size,
            0, MLI_ZONE_NAME);
        if (mli_zone == NULL) {