aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet6
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/in6_ifattach.c29
-rw-r--r--sys/netinet6/in6_mcast.c60
-rw-r--r--sys/netinet6/in6_var.h40
-rw-r--r--sys/netinet6/mld6.c141
-rw-r--r--sys/netinet6/mld6_var.h3
5 files changed, 126 insertions, 147 deletions
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index 14df5fff6450..03285b60d2d4 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -854,36 +854,15 @@ in6_tmpaddrtimer(void *arg)
static void
in6_purgemaddrs(struct ifnet *ifp)
{
- struct in6_multi_head purgeinms;
- struct in6_multi *inm;
- struct ifmultiaddr *ifma, *next;
+ struct in6_multi_head inmh;
- SLIST_INIT(&purgeinms);
+ SLIST_INIT(&inmh);
IN6_MULTI_LOCK();
IN6_MULTI_LIST_LOCK();
- IF_ADDR_WLOCK(ifp);
- /*
- * Extract list of in6_multi associated with the detaching ifp
- * which the PF_INET6 layer is about to release.
- */
- restart:
- CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
- continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
- in6m_disconnect(inm);
- in6m_rele_locked(&purgeinms, inm);
- if (__predict_false(ifma6_restart)) {
- ifma6_restart = false;
- goto restart;
- }
- }
- IF_ADDR_WUNLOCK(ifp);
- mld_ifdetach(ifp);
+ mld_ifdetach(ifp, &inmh);
IN6_MULTI_LIST_UNLOCK();
IN6_MULTI_UNLOCK();
- in6m_release_list_deferred(&purgeinms);
+ in6m_release_list_deferred(&inmh);
/*
* Make sure all multicast deletions invoking if_ioctl() are
diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c
index 693345b1c5ee..3b60d26c84ee 100644
--- a/sys/netinet6/in6_mcast.c
+++ b/sys/netinet6/in6_mcast.c
@@ -190,7 +190,6 @@ static SYSCTL_NODE(_net_inet6_ip6_mcast, OID_AUTO, filters,
CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip6_mcast_filters,
"Per-interface stack-wide source filters");
-int ifma6_restart = 0;
#ifdef KTR
/*
* Inline function which wraps assertions for a valid ifp.
@@ -405,6 +404,7 @@ static int
in6_getmulti(struct ifnet *ifp, const struct in6_addr *group,
struct in6_multi **pinm)
{
+ struct epoch_tracker et;
struct sockaddr_in6 gsin6;
struct ifmultiaddr *ifma;
struct in6_multi *inm;
@@ -420,7 +420,10 @@ in6_getmulti(struct ifnet *ifp, const struct in6_addr *group,
IN6_MULTI_LOCK_ASSERT();
IN6_MULTI_LIST_LOCK();
IF_ADDR_WLOCK(ifp);
+ NET_EPOCH_ENTER(et);
inm = in6m_lookup_locked(ifp, group);
+ NET_EPOCH_EXIT(et);
+
if (inm != NULL) {
/*
* If we already joined this group, just bump the
@@ -593,7 +596,7 @@ in6m_release_wait(void)
}
void
-in6m_disconnect(struct in6_multi *inm)
+in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm)
{
struct ifnet *ifp;
struct ifaddr *ifa;
@@ -601,10 +604,12 @@ in6m_disconnect(struct in6_multi *inm)
struct in6_multi_mship *imm, *imm_tmp;
struct ifmultiaddr *ifma, *ll_ifma;
- ifp = inm->in6m_ifp;
+ IN6_MULTI_LIST_LOCK_ASSERT();
+ ifp = inm->in6m_ifp;
if (ifp == NULL)
- return;
+ return; /* already called */
+
inm->in6m_ifp = NULL;
IF_ADDR_WLOCK_ASSERT(ifp);
ifma = inm->in6m_ifma;
@@ -623,7 +628,6 @@ in6m_disconnect(struct in6_multi *inm)
MPASS(ll_ifma->ifma_llifma == NULL);
MPASS(ll_ifma->ifma_ifp == ifp);
if (--ll_ifma->ifma_refcount == 0) {
- ifma6_restart = true;
if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) {
CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link);
ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
@@ -641,28 +645,12 @@ in6m_disconnect(struct in6_multi *inm)
if (inm == imm->i6mm_maddr) {
LIST_REMOVE(imm, i6mm_chain);
free(imm, M_IP6MADDR);
+ in6m_rele_locked(inmh, inm);
}
}
}
}
-void
-in6m_release_deferred(struct in6_multi *inm)
-{
- struct in6_multi_head tmp;
-
- IN6_MULTI_LIST_LOCK_ASSERT();
- KASSERT(inm->in6m_refcount > 0, ("refcount == %d inm: %p", inm->in6m_refcount, inm));
- if (--inm->in6m_refcount == 0) {
- MPASS(inm->in6m_ifp == NULL);
- SLIST_INIT(&tmp);
- inm->in6m_ifma->ifma_protospec = NULL;
- MPASS(inm->in6m_ifma->ifma_llifma == NULL);
- SLIST_INSERT_HEAD(&tmp, inm, in6m_nrele);
- in6m_release_list_deferred(&tmp);
- }
-}
-
static void
in6m_release_task(void *arg __unused)
{
@@ -1262,6 +1250,7 @@ in6_joingroup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr,
/*const*/ struct in6_mfilter *imf, struct in6_multi **pinm,
const int delay)
{
+ struct in6_multi_head inmh;
struct in6_mfilter timf;
struct in6_multi *inm;
struct ifmultiaddr *ifma;
@@ -1321,6 +1310,7 @@ in6_joingroup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr,
}
out_in6m_release:
+ SLIST_INIT(&inmh);
if (error) {
struct epoch_tracker et;
@@ -1332,13 +1322,14 @@ out_in6m_release:
break;
}
}
- in6m_disconnect(inm);
- in6m_release_deferred(inm);
+ in6m_disconnect_locked(&inmh, inm);
+ in6m_rele_locked(&inmh, inm);
NET_EPOCH_EXIT(et);
} else {
*pinm = inm;
}
IN6_MULTI_LIST_UNLOCK();
+ in6m_release_list_deferred(&inmh);
return (error);
}
@@ -1372,6 +1363,7 @@ in6_leavegroup(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
int
in6_leavegroup_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
{
+ struct in6_multi_head inmh;
struct in6_mfilter timf;
struct ifnet *ifp;
int error;
@@ -1421,13 +1413,15 @@ in6_leavegroup_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm);
if (ifp)
IF_ADDR_WLOCK(ifp);
- if (inm->in6m_refcount == 1 && inm->in6m_ifp != NULL)
- in6m_disconnect(inm);
- in6m_release_deferred(inm);
+
+ SLIST_INIT(&inmh);
+ if (inm->in6m_refcount == 1)
+ in6m_disconnect_locked(&inmh, inm);
+ in6m_rele_locked(&inmh, inm);
if (ifp)
IF_ADDR_WUNLOCK(ifp);
IN6_MULTI_LIST_UNLOCK();
-
+ in6m_release_list_deferred(&inmh);
return (error);
}
@@ -1941,6 +1935,7 @@ in6p_lookup_mcast_ifp(const struct inpcb *in6p,
static int
in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
{
+ struct in6_multi_head inmh;
struct group_source_req gsr;
sockunion_t *gsa, *ssa;
struct ifnet *ifp;
@@ -1951,6 +1946,7 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
size_t idx;
int error, is_new;
+ SLIST_INIT(&inmh);
ifp = NULL;
imf = NULL;
lims = NULL;
@@ -2227,7 +2223,7 @@ out_im6o_free:
inm = imo->im6o_membership[idx];
if (inm != NULL) {
IN6_MULTI_LIST_LOCK();
- in6m_release_deferred(inm);
+ in6m_rele_locked(&inmh, inm);
IN6_MULTI_LIST_UNLOCK();
}
imo->im6o_membership[idx] = NULL;
@@ -2236,6 +2232,7 @@ out_im6o_free:
out_in6p_locked:
INP_WUNLOCK(inp);
+ in6m_release_list_deferred(&inmh);
return (error);
}
@@ -2880,10 +2877,9 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
IN6_MULTI_LIST_LOCK();
NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr))
continue;
fmode = inm->in6m_st[1].iss_fmode;
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index a19747d97918..3e965c2d4b32 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -645,6 +645,7 @@ struct in6_multi {
/* New fields for MLDv2 follow. */
struct mld_ifsoftc *in6m_mli; /* MLD info */
SLIST_ENTRY(in6_multi) in6m_nrele; /* to-be-released by MLD */
+ SLIST_ENTRY(in6_multi) in6m_defer; /* deferred MLDv1 */
struct ip6_msource_tree in6m_srcs; /* tree of sources */
u_long in6m_nsrc; /* # of tree entries */
@@ -670,8 +671,8 @@ struct in6_multi {
} in6m_st[2]; /* state at t0, t1 */
};
-void in6m_disconnect(struct in6_multi *inm);
-extern int ifma6_restart;
+void in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm);
+
/*
* Helper function to derive the filter mode on a source entry
* from its internal counters. Predicates are:
@@ -713,13 +714,25 @@ extern struct sx in6_multi_sx;
#define IN6_MULTI_LOCK_ASSERT() sx_assert(&in6_multi_sx, SA_XLOCKED)
#define IN6_MULTI_UNLOCK_ASSERT() sx_assert(&in6_multi_sx, SA_XUNLOCKED)
+/*
+ * Get the in6_multi pointer from a ifmultiaddr.
+ * Returns NULL if ifmultiaddr is no longer valid.
+ */
+static __inline struct in6_multi *
+in6m_ifmultiaddr_get_inm(struct ifmultiaddr *ifma)
+{
+
+ NET_EPOCH_ASSERT();
+
+ return ((ifma->ifma_addr->sa_family != AF_INET6 ||
+ (ifma->ifma_flags & IFMA_F_ENQUEUED) == 0) ? NULL :
+ ifma->ifma_protospec);
+}
/*
* Look up an in6_multi record for an IPv6 multicast address
* on the interface ifp.
* If no record found, return NULL.
- *
- * SMPng: The IN6_MULTI_LOCK and IF_ADDR_LOCK on ifp must be held.
*/
static __inline struct in6_multi *
in6m_lookup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr)
@@ -727,18 +740,14 @@ in6m_lookup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr)
struct ifmultiaddr *ifma;
struct in6_multi *inm;
- inm = NULL;
- CK_STAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) {
- if (ifma->ifma_addr->sa_family == AF_INET6) {
- inm = (struct in6_multi *)ifma->ifma_protospec;
- if (inm == NULL)
- continue;
- if (IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, mcaddr))
- break;
- inm = NULL;
- }
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
+ continue;
+ if (IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, mcaddr))
+ return (inm);
}
- return (inm);
+ return (NULL);
}
/*
@@ -809,7 +818,6 @@ void in6m_clear_recorded(struct in6_multi *);
void in6m_commit(struct in6_multi *);
void in6m_print(const struct in6_multi *);
int in6m_record_source(struct in6_multi *, const struct in6_addr *);
-void in6m_release_deferred(struct in6_multi *);
void in6m_release_list_deferred(struct in6_multi_head *);
void in6m_release_wait(void);
void ip6_freemoptions(struct ip6_moptions *);
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index 9536f4591b1c..62632e989c88 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -110,7 +110,7 @@ static void mli_delete_locked(const struct ifnet *);
static void mld_dispatch_packet(struct mbuf *);
static void mld_dispatch_queue(struct mbufq *, int);
static void mld_final_leave(struct in6_multi *, struct mld_ifsoftc *);
-static void mld_fasttimo_vnet(void);
+static void mld_fasttimo_vnet(struct in6_multi_head *inmh);
static int mld_handle_state_change(struct in6_multi *,
struct mld_ifsoftc *);
static int mld_initial_join(struct in6_multi *, struct mld_ifsoftc *,
@@ -537,45 +537,48 @@ out:
* XXX This routine is also bitten by unlocked ifma_protospec access.
*/
void
-mld_ifdetach(struct ifnet *ifp)
+mld_ifdetach(struct ifnet *ifp, struct in6_multi_head *inmh)
{
+ struct epoch_tracker et;
struct mld_ifsoftc *mli;
- struct ifmultiaddr *ifma, *next;
+ struct ifmultiaddr *ifma;
struct in6_multi *inm;
- struct in6_multi_head inmh;
CTR3(KTR_MLD, "%s: called for ifp %p(%s)", __func__, ifp,
if_name(ifp));
- SLIST_INIT(&inmh);
IN6_MULTI_LIST_LOCK_ASSERT();
MLD_LOCK();
mli = MLD_IFINFO(ifp);
- if (mli->mli_version == MLD_VERSION_2) {
- IF_ADDR_WLOCK(ifp);
- restart:
- CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
- continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
- if (inm->in6m_state == MLD_LEAVING_MEMBER) {
- in6m_disconnect(inm);
- in6m_rele_locked(&inmh, inm);
- ifma->ifma_protospec = NULL;
- }
+ IF_ADDR_WLOCK(ifp);
+ /*
+ * Extract list of in6_multi associated with the detaching ifp
+ * which the PF_INET6 layer is about to release.
+ */
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
+ continue;
+ in6m_disconnect_locked(inmh, inm);
+
+ if (mli->mli_version == MLD_VERSION_2) {
in6m_clear_recorded(inm);
- if (__predict_false(ifma6_restart)) {
- ifma6_restart = false;
- goto restart;
+
+ /*
+ * We need to release the final reference held
+ * for issuing the INCLUDE {}.
+ */
+ if (inm->in6m_state == MLD_LEAVING_MEMBER) {
+ inm->in6m_state = MLD_NOT_MEMBER;
+ in6m_rele_locked(inmh, inm);
}
}
- IF_ADDR_WUNLOCK(ifp);
}
-
+ NET_EPOCH_EXIT(et);
+ IF_ADDR_WUNLOCK(ifp);
MLD_UNLOCK();
- in6m_release_list_deferred(&inmh);
}
/*
@@ -709,10 +712,9 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
CTR2(KTR_MLD, "process v1 general query on ifp %p(%s)",
ifp, if_name(ifp));
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
mld_v1_update_group(inm, timer);
}
} else {
@@ -1324,15 +1326,19 @@ mld_input(struct mbuf *m, int off, int icmp6len)
void
mld_fasttimo(void)
{
+ struct in6_multi_head inmh;
VNET_ITERATOR_DECL(vnet_iter);
+ SLIST_INIT(&inmh);
+
VNET_LIST_RLOCK_NOSLEEP();
VNET_FOREACH(vnet_iter) {
CURVNET_SET(vnet_iter);
- mld_fasttimo_vnet();
+ mld_fasttimo_vnet(&inmh);
CURVNET_RESTORE();
}
VNET_LIST_RUNLOCK_NOSLEEP();
+ in6m_release_list_deferred(&inmh);
}
/*
@@ -1341,15 +1347,15 @@ mld_fasttimo(void)
* VIMAGE: Assume caller has set up our curvnet.
*/
static void
-mld_fasttimo_vnet(void)
+mld_fasttimo_vnet(struct in6_multi_head *inmh)
{
+ struct epoch_tracker et;
struct mbufq scq; /* State-change packets */
struct mbufq qrq; /* Query response packets */
struct ifnet *ifp;
struct mld_ifsoftc *mli;
- struct ifmultiaddr *ifma, *next;
- struct in6_multi *inm, *tinm;
- struct in6_multi_head inmh;
+ struct ifmultiaddr *ifma;
+ struct in6_multi *inm;
int uri_fasthz;
uri_fasthz = 0;
@@ -1364,7 +1370,6 @@ mld_fasttimo_vnet(void)
!V_state_change_timers_running6)
return;
- SLIST_INIT(&inmh);
IN6_MULTI_LIST_LOCK();
MLD_LOCK();
@@ -1410,25 +1415,20 @@ mld_fasttimo_vnet(void)
}
IF_ADDR_WLOCK(ifp);
- restart:
- CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
switch (mli->mli_version) {
case MLD_VERSION_1:
- mld_v1_process_group_timer(&inmh, inm);
+ mld_v1_process_group_timer(inmh, inm);
break;
case MLD_VERSION_2:
- mld_v2_process_group_timers(&inmh, &qrq,
+ mld_v2_process_group_timers(inmh, &qrq,
&scq, inm, uri_fasthz);
break;
}
- if (__predict_false(ifma6_restart)) {
- ifma6_restart = false;
- goto restart;
- }
}
IF_ADDR_WUNLOCK(ifp);
@@ -1442,9 +1442,8 @@ mld_fasttimo_vnet(void)
* IF_ADDR_LOCK internally as well as
* ip6_output() to transmit a packet.
*/
- SLIST_FOREACH_SAFE(inm, &inmh, in6m_nrele, tinm) {
- SLIST_REMOVE_HEAD(&inmh,
- in6m_nrele);
+ while ((inm = SLIST_FIRST(inmh)) != NULL) {
+ SLIST_REMOVE_HEAD(inmh, in6m_defer);
(void)mld_v1_transmit_report(inm,
MLD_LISTENER_REPORT);
}
@@ -1452,14 +1451,9 @@ mld_fasttimo_vnet(void)
case MLD_VERSION_2:
mld_dispatch_queue(&qrq, 0);
mld_dispatch_queue(&scq, 0);
-
- /*
- * Free the in_multi reference(s) for
- * this lifecycle.
- */
- in6m_release_list_deferred(&inmh);
break;
}
+ NET_EPOCH_EXIT(et);
}
out_locked:
@@ -1499,8 +1493,7 @@ mld_v1_process_group_timer(struct in6_multi_head *inmh, struct in6_multi *inm)
case MLD_REPORTING_MEMBER:
if (report_timer_expired) {
inm->in6m_state = MLD_IDLE_MEMBER;
- in6m_disconnect(inm);
- in6m_rele_locked(inmh, inm);
+ SLIST_INSERT_HEAD(inmh, inm, in6m_defer);
}
break;
case MLD_G_QUERY_PENDING_MEMBER:
@@ -1624,7 +1617,7 @@ mld_v2_process_group_timers(struct in6_multi_head *inmh,
if (inm->in6m_state == MLD_LEAVING_MEMBER &&
inm->in6m_scrv == 0) {
inm->in6m_state = MLD_NOT_MEMBER;
- in6m_disconnect(inm);
+ in6m_disconnect_locked(inmh, inm);
in6m_rele_locked(inmh, inm);
}
}
@@ -1669,10 +1662,11 @@ mld_set_version(struct mld_ifsoftc *mli, const int version)
static void
mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
{
- struct ifmultiaddr *ifma, *next;
+ struct epoch_tracker et;
+ struct in6_multi_head inmh;
+ struct ifmultiaddr *ifma;
struct ifnet *ifp;
struct in6_multi *inm;
- struct in6_multi_head inmh;
CTR3(KTR_MLD, "%s: cancel v2 timers on ifp %p(%s)", __func__,
mli->mli_ifp, if_name(mli->mli_ifp));
@@ -1695,12 +1689,11 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
ifp = mli->mli_ifp;
IF_ADDR_WLOCK(ifp);
- restart:
- CK_STAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
- inm = (struct in6_multi *)ifma->ifma_protospec;
switch (inm->in6m_state) {
case MLD_NOT_MEMBER:
case MLD_SILENT_MEMBER:
@@ -1715,9 +1708,7 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
* version, we need to release the final
* reference held for issuing the INCLUDE {}.
*/
- in6m_disconnect(inm);
in6m_rele_locked(&inmh, inm);
- ifma->ifma_protospec = NULL;
/* FALLTHROUGH */
case MLD_G_QUERY_PENDING_MEMBER:
case MLD_SG_QUERY_PENDING_MEMBER:
@@ -1733,11 +1724,8 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
mbufq_drain(&inm->in6m_scq);
break;
}
- if (__predict_false(ifma6_restart)) {
- ifma6_restart = false;
- goto restart;
- }
}
+ NET_EPOCH_EXIT(et);
IF_ADDR_WUNLOCK(ifp);
in6m_release_list_deferred(&inmh);
}
@@ -1910,6 +1898,14 @@ mld_change_state(struct in6_multi *inm, const int delay)
error = 0;
/*
+ * Check if the in6_multi has already been disconnected.
+ */
+ if (inm->in6m_ifp == NULL) {
+ CTR1(KTR_MLD, "%s: inm is disconnected", __func__);
+ return (0);
+ }
+
+ /*
* Try to detect if the upper layer just asked us to change state
* for an interface which has now gone away.
*/
@@ -2019,6 +2015,7 @@ mld_initial_join(struct in6_multi *inm, struct mld_ifsoftc *mli,
if (mli->mli_version == MLD_VERSION_2 &&
inm->in6m_state == MLD_LEAVING_MEMBER) {
inm->in6m_refcount--;
+ MPASS(inm->in6m_refcount > 0);
}
inm->in6m_state = MLD_REPORTING_MEMBER;
@@ -3023,11 +3020,9 @@ mld_v2_dispatch_general_query(struct mld_ifsoftc *mli)
NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
- if (ifma->ifma_addr->sa_family != AF_INET6 ||
- ifma->ifma_protospec == NULL)
+ inm = in6m_ifmultiaddr_get_inm(ifma);
+ if (inm == NULL)
continue;
-
- inm = (struct in6_multi *)ifma->ifma_protospec;
KASSERT(ifp == inm->in6m_ifp,
("%s: inconsistent ifp", __func__));
diff --git a/sys/netinet6/mld6_var.h b/sys/netinet6/mld6_var.h
index 166c20555b96..8dc2ffa4cd7c 100644
--- a/sys/netinet6/mld6_var.h
+++ b/sys/netinet6/mld6_var.h
@@ -160,12 +160,13 @@ struct mld_ifsoftc {
#define MLD_IFINFO(ifp) \
(((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->mld_ifinfo)
+struct in6_multi_head;
int mld_change_state(struct in6_multi *, const int);
struct mld_ifsoftc *
mld_domifattach(struct ifnet *);
void mld_domifdetach(struct ifnet *);
void mld_fasttimo(void);
-void mld_ifdetach(struct ifnet *);
+void mld_ifdetach(struct ifnet *, struct in6_multi_head *);
int mld_input(struct mbuf *, int, int);
void mld_slowtimo(void);