r333175 introduced deferred deletion of multicast addresses in order to permit the driver ioctl
to sleep on commands to the NIC when updating multicast filters. More generally this permitted driver's to use an sx as a softc lock. Unfortunately this change introduced a race whereby a a multicast update would still be queued for deletion when ifconfig deleted the interface thus calling down in to _purgemaddrs and synchronously deleting _all_ of the multicast addresses on the interface. Synchronously remove all external references to a multicast address before enqueueing for delete. Reported by: lwhsu Approved by: sbruno
This commit is contained in:
parent
7edd877a1e
commit
b6f6f88018
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=333309
36
sys/net/if.c
36
sys/net/if.c
@ -254,7 +254,6 @@ struct mbuf *(*tbr_dequeue_ptr)(struct ifaltq *, int) = NULL;
|
||||
static void if_attachdomain(void *);
|
||||
static void if_attachdomain1(struct ifnet *);
|
||||
static int ifconf(u_long, caddr_t);
|
||||
static void if_freemulti(struct ifmultiaddr *);
|
||||
static void if_grow(void);
|
||||
static void if_input_default(struct ifnet *, struct mbuf *);
|
||||
static int if_requestencap_default(struct ifnet *, struct if_encap_req *);
|
||||
@ -3395,7 +3394,10 @@ if_allocmulti(struct ifnet *ifp, struct sockaddr *sa, struct sockaddr *llsa,
|
||||
* counting, notifying the driver, handling routing messages, and releasing
|
||||
* any dependent link layer state.
|
||||
*/
|
||||
static void
|
||||
#ifdef MCAST_VERBOSE
|
||||
extern void kdb_backtrace(void);
|
||||
#endif
|
||||
void
|
||||
if_freemulti(struct ifmultiaddr *ifma)
|
||||
{
|
||||
|
||||
@ -3404,6 +3406,10 @@ if_freemulti(struct ifmultiaddr *ifma)
|
||||
|
||||
if (ifma->ifma_lladdr != NULL)
|
||||
free(ifma->ifma_lladdr, M_IFMADDR);
|
||||
#ifdef MCAST_VERBOSE
|
||||
kdb_backtrace();
|
||||
printf("%s freeing ifma: %p\n", __func__, ifma);
|
||||
#endif
|
||||
free(ifma->ifma_addr, M_IFMADDR);
|
||||
free(ifma, M_IFMADDR);
|
||||
}
|
||||
@ -3610,6 +3616,12 @@ if_delallmulti(struct ifnet *ifp)
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
}
|
||||
|
||||
void
|
||||
if_delmulti_ifma(struct ifmultiaddr *ifma)
|
||||
{
|
||||
if_delmulti_ifma_flags(ifma, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a multicast group membership by group membership pointer.
|
||||
* Network-layer protocol domains must use this routine.
|
||||
@ -3617,11 +3629,11 @@ if_delallmulti(struct ifnet *ifp)
|
||||
* It is safe to call this routine if the ifp disappeared.
|
||||
*/
|
||||
void
|
||||
if_delmulti_ifma(struct ifmultiaddr *ifma)
|
||||
if_delmulti_ifma_flags(struct ifmultiaddr *ifma, int flags)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
int lastref;
|
||||
|
||||
MCDPRINTF("%s freeing ifma: %p\n", __func__, ifma);
|
||||
#ifdef INET
|
||||
IN_MULTI_LIST_UNLOCK_ASSERT();
|
||||
#endif
|
||||
@ -3649,7 +3661,7 @@ if_delmulti_ifma(struct ifmultiaddr *ifma)
|
||||
if (ifp != NULL)
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
|
||||
lastref = if_delmulti_locked(ifp, ifma, 0);
|
||||
lastref = if_delmulti_locked(ifp, ifma, flags);
|
||||
|
||||
if (ifp != NULL) {
|
||||
/*
|
||||
@ -3683,6 +3695,7 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching)
|
||||
}
|
||||
|
||||
ifp = ifma->ifma_ifp;
|
||||
MCDPRINTF("%s freeing %p from %s \n", __func__, ifma, ifp ? ifp->if_xname : "");
|
||||
|
||||
/*
|
||||
* If the ifnet is detaching, null out references to ifnet,
|
||||
@ -3708,6 +3721,9 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching)
|
||||
if (--ifma->ifma_refcount > 0)
|
||||
return 0;
|
||||
|
||||
if (ifp != NULL && detaching == 0)
|
||||
TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
|
||||
|
||||
/*
|
||||
* If this ifma is a network-layer ifma, a link-layer ifma may
|
||||
* have been associated with it. Release it first if so.
|
||||
@ -3726,11 +3742,15 @@ if_delmulti_locked(struct ifnet *ifp, struct ifmultiaddr *ifma, int detaching)
|
||||
if_freemulti(ll_ifma);
|
||||
}
|
||||
}
|
||||
if (ifp != NULL && detaching == 0)
|
||||
TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
|
||||
#ifdef INVARIANTS
|
||||
if (ifp) {
|
||||
struct ifmultiaddr *ifmatmp;
|
||||
|
||||
TAILQ_FOREACH(ifmatmp, &ifp->if_multiaddrs, ifma_link)
|
||||
MPASS(ifma != ifmatmp);
|
||||
}
|
||||
#endif
|
||||
if_freemulti(ifma);
|
||||
|
||||
/*
|
||||
* The last reference to this instance of struct ifmultiaddr
|
||||
* was released; the hardware should be notified of this change.
|
||||
|
@ -596,6 +596,12 @@ VNET_DECLARE(struct ifnet *, loif); /* first loopback interface */
|
||||
#define V_if_index VNET(if_index)
|
||||
#define V_loif VNET(loif)
|
||||
|
||||
#ifdef MCAST_VERBOSE
|
||||
#define MCDPRINTF printf
|
||||
#else
|
||||
#define MCDPRINTF(...)
|
||||
#endif
|
||||
|
||||
int if_addgroup(struct ifnet *, const char *);
|
||||
int if_delgroup(struct ifnet *, const char *);
|
||||
int if_addmulti(struct ifnet *, struct sockaddr *, struct ifmultiaddr **);
|
||||
@ -605,12 +611,14 @@ void if_attach(struct ifnet *);
|
||||
void if_dead(struct ifnet *);
|
||||
int if_delmulti(struct ifnet *, struct sockaddr *);
|
||||
void if_delmulti_ifma(struct ifmultiaddr *);
|
||||
void if_delmulti_ifma_flags(struct ifmultiaddr *, int flags);
|
||||
void if_detach(struct ifnet *);
|
||||
void if_purgeaddrs(struct ifnet *);
|
||||
void if_delallmulti(struct ifnet *);
|
||||
void if_down(struct ifnet *);
|
||||
struct ifmultiaddr *
|
||||
if_findmulti(struct ifnet *, const struct sockaddr *);
|
||||
void if_freemulti(struct ifmultiaddr *ifma);
|
||||
void if_free(struct ifnet *);
|
||||
void if_initname(struct ifnet *, const char *, int);
|
||||
void if_link_state_change(struct ifnet *, int);
|
||||
|
@ -610,7 +610,7 @@ void
|
||||
igmp_ifdetach(struct ifnet *ifp)
|
||||
{
|
||||
struct igmp_ifsoftc *igi;
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifmultiaddr *ifma, *next;
|
||||
struct in_multi *inm;
|
||||
struct in_multi_head inm_free_tmp;
|
||||
CTR3(KTR_IGMPV3, "%s: called for ifp %p(%s)", __func__, ifp,
|
||||
@ -621,21 +621,22 @@ igmp_ifdetach(struct ifnet *ifp)
|
||||
|
||||
igi = ((struct in_ifinfo *)ifp->if_afdata[AF_INET])->ii_igmp;
|
||||
if (igi->igi_version == IGMP_VERSION_3) {
|
||||
IF_ADDR_RLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
restart:
|
||||
TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
|
||||
if (ifma->ifma_addr->sa_family != AF_INET ||
|
||||
ifma->ifma_protospec == NULL)
|
||||
continue;
|
||||
#if 0
|
||||
KASSERT(ifma->ifma_protospec != NULL,
|
||||
("%s: ifma_protospec is NULL", __func__));
|
||||
#endif
|
||||
inm = (struct in_multi *)ifma->ifma_protospec;
|
||||
if (inm->inm_state == IGMP_LEAVING_MEMBER)
|
||||
inm_rele_locked(&inm_free_tmp, inm);
|
||||
inm_clear_recorded(inm);
|
||||
if (__predict_false(ifma_restart)) {
|
||||
ifma_restart = false;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
inm_release_list_deferred(&inm_free_tmp);
|
||||
}
|
||||
IGMP_UNLOCK();
|
||||
@ -1631,7 +1632,7 @@ igmp_fasttimo_vnet(void)
|
||||
struct mbufq qrq; /* Query response packets */
|
||||
struct ifnet *ifp;
|
||||
struct igmp_ifsoftc *igi;
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifmultiaddr *ifma, *next;
|
||||
struct in_multi *inm;
|
||||
struct in_multi_head inm_free_tmp;
|
||||
int loop, uri_fasthz;
|
||||
@ -1695,8 +1696,9 @@ igmp_fasttimo_vnet(void)
|
||||
mbufq_init(&scq, IGMP_MAX_STATE_CHANGE_PACKETS);
|
||||
}
|
||||
|
||||
IF_ADDR_RLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
restart:
|
||||
TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
|
||||
if (ifma->ifma_addr->sa_family != AF_INET ||
|
||||
ifma->ifma_protospec == NULL)
|
||||
continue;
|
||||
@ -1712,8 +1714,12 @@ igmp_fasttimo_vnet(void)
|
||||
&scq, inm, uri_fasthz);
|
||||
break;
|
||||
}
|
||||
if (__predict_false(ifma_restart)) {
|
||||
ifma_restart = false;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
|
||||
if (igi->igi_version == IGMP_VERSION_3) {
|
||||
igmp_dispatch_queue(&qrq, 0, loop);
|
||||
|
@ -1012,7 +1012,7 @@ in_purgemaddrs(struct ifnet *ifp)
|
||||
{
|
||||
struct in_multi_head purgeinms;
|
||||
struct in_multi *inm;
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifmultiaddr *ifma, *next;
|
||||
|
||||
SLIST_INIT(&purgeinms);
|
||||
IN_MULTI_LIST_LOCK();
|
||||
@ -1023,19 +1023,20 @@ in_purgemaddrs(struct ifnet *ifp)
|
||||
* We need to do this as IF_ADDR_LOCK() may be re-acquired
|
||||
* by code further down.
|
||||
*/
|
||||
IF_ADDR_RLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
restart:
|
||||
TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
|
||||
if (ifma->ifma_addr->sa_family != AF_INET ||
|
||||
ifma->ifma_protospec == NULL)
|
||||
continue;
|
||||
#if 0
|
||||
KASSERT(ifma->ifma_protospec != NULL,
|
||||
("%s: ifma_protospec is NULL", __func__));
|
||||
#endif
|
||||
inm = (struct in_multi *)ifma->ifma_protospec;
|
||||
inm_rele_locked(&purgeinms, inm);
|
||||
if (__predict_false(ifma_restart)) {
|
||||
ifma_restart = true;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
|
||||
inm_release_list_deferred(&purgeinms);
|
||||
igmp_ifdetach(ifp);
|
||||
|
@ -112,6 +112,8 @@ MTX_SYSINIT(in_multi_free_mtx, &in_multi_free_mtx, "in_multi_free_mtx", MTX_DEF)
|
||||
struct sx in_multi_sx;
|
||||
SX_SYSINIT(in_multi_sx, &in_multi_sx, "in_multi_sx");
|
||||
|
||||
int ifma_restart;
|
||||
|
||||
/*
|
||||
* Functions with non-static linkage defined in this file should be
|
||||
* declared in in_var.h:
|
||||
@ -251,6 +253,33 @@ inm_release_list_deferred(struct in_multi_head *inmh)
|
||||
GROUPTASK_ENQUEUE(&free_gtask);
|
||||
}
|
||||
|
||||
void
|
||||
inm_disconnect(struct in_multi *inm)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
struct ifmultiaddr *ifma, *ll_ifma;
|
||||
|
||||
ifp = inm->inm_ifp;
|
||||
IF_ADDR_WLOCK_ASSERT(ifp);
|
||||
ifma = inm->inm_ifma;
|
||||
|
||||
if_ref(ifp);
|
||||
TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
|
||||
MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname);
|
||||
if ((ll_ifma = ifma->ifma_llifma) != NULL) {
|
||||
MPASS(ifma != ll_ifma);
|
||||
ifma->ifma_llifma = NULL;
|
||||
MPASS(ll_ifma->ifma_llifma == NULL);
|
||||
MPASS(ll_ifma->ifma_ifp == ifp);
|
||||
if (--ll_ifma->ifma_refcount == 0) {
|
||||
TAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifma_link);
|
||||
MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname);
|
||||
if_freemulti(ll_ifma);
|
||||
ifma_restart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
inm_release_deferred(struct in_multi *inm)
|
||||
{
|
||||
@ -260,6 +289,7 @@ inm_release_deferred(struct in_multi *inm)
|
||||
MPASS(inm->inm_refcount > 0);
|
||||
if (--inm->inm_refcount == 0) {
|
||||
SLIST_INIT(&tmp);
|
||||
inm_disconnect(inm);
|
||||
inm->inm_ifma->ifma_protospec = NULL;
|
||||
SLIST_INSERT_HEAD(&tmp, inm, inm_nrele);
|
||||
inm_release_list_deferred(&tmp);
|
||||
@ -643,9 +673,11 @@ inm_release(struct in_multi *inm)
|
||||
inm_purge(inm);
|
||||
free(inm, M_IPMADDR);
|
||||
|
||||
if_delmulti_ifma(ifma);
|
||||
if (ifp)
|
||||
if_delmulti_ifma_flags(ifma, 1);
|
||||
if (ifp) {
|
||||
CURVNET_RESTORE();
|
||||
if_rele(ifp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1270,7 +1302,6 @@ in_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina,
|
||||
}
|
||||
|
||||
out_inm_release:
|
||||
IN_MULTI_LIST_UNLOCK();
|
||||
if (error) {
|
||||
|
||||
CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm);
|
||||
@ -1278,6 +1309,7 @@ in_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina,
|
||||
} else {
|
||||
*pinm = inm;
|
||||
}
|
||||
IN_MULTI_LIST_UNLOCK();
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -1350,7 +1382,9 @@ in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
|
||||
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
|
||||
CURVNET_SET(inm->inm_ifp->if_vnet);
|
||||
error = igmp_change_state(inm);
|
||||
IF_ADDR_WLOCK(inm->inm_ifp);
|
||||
inm_release_deferred(inm);
|
||||
IF_ADDR_WUNLOCK(inm->inm_ifp);
|
||||
IN_MULTI_LIST_UNLOCK();
|
||||
CURVNET_RESTORE();
|
||||
if (error)
|
||||
|
@ -343,6 +343,9 @@ extern struct sx in_multi_sx;
|
||||
#define IN_MULTI_LOCK_ASSERT() sx_assert(&in_multi_sx, SA_XLOCKED)
|
||||
#define IN_MULTI_UNLOCK_ASSERT() sx_assert(&in_multi_sx, SA_XUNLOCKED)
|
||||
|
||||
void inm_disconnect(struct in_multi *inm);
|
||||
extern int ifma_restart;
|
||||
|
||||
/* Acquire an in_multi record. */
|
||||
static __inline void
|
||||
inm_acquire_locked(struct in_multi *inm)
|
||||
@ -368,6 +371,7 @@ inm_rele_locked(struct in_multi_head *inmh, struct in_multi *inm)
|
||||
|
||||
if (--inm->inm_refcount == 0) {
|
||||
MPASS(inmh != NULL);
|
||||
inm_disconnect(inm);
|
||||
inm->inm_ifma->ifma_protospec = NULL;
|
||||
SLIST_INSERT_HEAD(inmh, inm, inm_nrele);
|
||||
}
|
||||
|
@ -848,26 +848,29 @@ in6_purgemaddrs(struct ifnet *ifp)
|
||||
{
|
||||
struct in6_multi_head purgeinms;
|
||||
struct in6_multi *inm;
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifmultiaddr *ifma, *next;
|
||||
|
||||
SLIST_INIT(&purgeinms);
|
||||
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.
|
||||
* We need to do this as IF_ADDR_LOCK() may be re-acquired
|
||||
* by code further down.
|
||||
*/
|
||||
IF_ADDR_RLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
restart:
|
||||
TAILQ_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_rele_locked(&purgeinms, inm);
|
||||
if (__predict_false(ifma6_restart)) {
|
||||
ifma6_restart = false;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
mld_ifdetach(ifp);
|
||||
IN6_MULTI_LIST_UNLOCK();
|
||||
IN6_MULTI_UNLOCK();
|
||||
|
@ -190,6 +190,7 @@ 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.
|
||||
@ -531,6 +532,7 @@ in6m_release(struct in6_multi *inm)
|
||||
|
||||
ifma = inm->in6m_ifma;
|
||||
ifp = inm->in6m_ifp;
|
||||
MPASS(ifma->ifma_llifma == NULL);
|
||||
|
||||
/* XXX this access is not covered by IF_ADDR_LOCK */
|
||||
CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma);
|
||||
@ -542,9 +544,11 @@ in6m_release(struct in6_multi *inm)
|
||||
in6m_purge(inm);
|
||||
free(inm, M_IP6MADDR);
|
||||
|
||||
if_delmulti_ifma(ifma);
|
||||
if (ifp)
|
||||
if_delmulti_ifma_flags(ifma, 1);
|
||||
if (ifp) {
|
||||
CURVNET_RESTORE();
|
||||
if_rele(ifp);
|
||||
}
|
||||
}
|
||||
|
||||
static struct grouptask free_gtask;
|
||||
@ -572,30 +576,58 @@ in6m_release_list_deferred(struct in6_multi_head *inmh)
|
||||
}
|
||||
|
||||
void
|
||||
in6m_release_deferred(struct in6_multi *inm)
|
||||
in6m_disconnect(struct in6_multi *inm)
|
||||
{
|
||||
struct in6_multi_head tmp;
|
||||
struct ifnet *ifp;
|
||||
struct ifaddr *ifa;
|
||||
struct in6_ifaddr *ifa6;
|
||||
struct in6_multi_mship *imm;
|
||||
struct ifmultiaddr *ifma, *ll_ifma;
|
||||
|
||||
ifp = inm->in6m_ifp;
|
||||
IF_ADDR_WLOCK_ASSERT(ifp);
|
||||
ifma = inm->in6m_ifma;
|
||||
|
||||
if_ref(ifp);
|
||||
TAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifma_link);
|
||||
MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname);
|
||||
if ((ll_ifma = ifma->ifma_llifma) != NULL) {
|
||||
MPASS(ifma != ll_ifma);
|
||||
ifma->ifma_llifma = NULL;
|
||||
MPASS(ll_ifma->ifma_llifma == NULL);
|
||||
MPASS(ll_ifma->ifma_ifp == ifp);
|
||||
if (--ll_ifma->ifma_refcount == 0) {
|
||||
ifma6_restart = true;
|
||||
TAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifma_link);
|
||||
MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname);
|
||||
if_freemulti(ll_ifma);
|
||||
}
|
||||
}
|
||||
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
|
||||
if (ifa->ifa_addr->sa_family != AF_INET6)
|
||||
continue;
|
||||
ifa6 = (void *)ifa;
|
||||
LIST_FOREACH(imm, &ifa6->ia6_memberships, i6mm_chain) {
|
||||
if (inm == imm->i6mm_maddr) {
|
||||
LIST_REMOVE(imm, i6mm_chain);
|
||||
free(imm, M_IP6MADDR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
ifp = inm->in6m_ifp;
|
||||
IF_ADDR_LOCK_ASSERT(ifp);
|
||||
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
|
||||
if (ifa->ifa_addr->sa_family != AF_INET6)
|
||||
continue;
|
||||
ifa6 = (void *)ifa;
|
||||
LIST_FOREACH(imm, &ifa6->ia6_memberships, i6mm_chain) {
|
||||
if (inm == imm->i6mm_maddr)
|
||||
imm->i6mm_maddr = NULL;
|
||||
}
|
||||
}
|
||||
in6m_disconnect(inm);
|
||||
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);
|
||||
}
|
||||
@ -1355,10 +1387,10 @@ 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_RLOCK(ifp);
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
in6m_release_deferred(inm);
|
||||
if (ifp)
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
IN6_MULTI_LIST_UNLOCK();
|
||||
|
||||
return (error);
|
||||
|
@ -670,6 +670,8 @@ struct in6_multi {
|
||||
} in6m_st[2]; /* state at t0, t1 */
|
||||
};
|
||||
|
||||
void in6m_disconnect(struct in6_multi *inm);
|
||||
extern int ifma6_restart;
|
||||
/*
|
||||
* Helper function to derive the filter mode on a source entry
|
||||
* from its internal counters. Predicates are:
|
||||
@ -711,6 +713,7 @@ 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)
|
||||
|
||||
|
||||
/*
|
||||
* Look up an in6_multi record for an IPv6 multicast address
|
||||
* on the interface ifp.
|
||||
@ -779,28 +782,13 @@ in6m_acquire(struct in6_multi *inm)
|
||||
static __inline void
|
||||
in6m_rele_locked(struct in6_multi_head *inmh, struct in6_multi *inm)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
struct ifaddr *ifa;
|
||||
struct in6_ifaddr *ifa6;
|
||||
struct in6_multi_mship *imm;
|
||||
|
||||
KASSERT(inm->in6m_refcount > 0, ("refcount == %d inm: %p", inm->in6m_refcount, inm));
|
||||
IN6_MULTI_LIST_LOCK_ASSERT();
|
||||
|
||||
if (--inm->in6m_refcount == 0) {
|
||||
ifp = inm->in6m_ifp;
|
||||
IF_ADDR_LOCK_ASSERT(ifp);
|
||||
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
|
||||
if (ifa->ifa_addr->sa_family != AF_INET6)
|
||||
continue;
|
||||
|
||||
ifa6 = (void *)ifa;
|
||||
LIST_FOREACH(imm, &ifa6->ia6_memberships, i6mm_chain) {
|
||||
if (inm == imm->i6mm_maddr)
|
||||
imm->i6mm_maddr = NULL;
|
||||
}
|
||||
}
|
||||
in6m_disconnect(inm);
|
||||
inm->in6m_ifma->ifma_protospec = NULL;
|
||||
MPASS(inm->in6m_ifma->ifma_llifma == NULL);
|
||||
SLIST_INSERT_HEAD(inmh, inm, in6m_nrele);
|
||||
}
|
||||
}
|
||||
|
@ -536,7 +536,7 @@ void
|
||||
mld_ifdetach(struct ifnet *ifp)
|
||||
{
|
||||
struct mld_ifsoftc *mli;
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifmultiaddr *ifma, *next;
|
||||
struct in6_multi *inm;
|
||||
struct in6_multi_head inmh;
|
||||
|
||||
@ -549,8 +549,9 @@ mld_ifdetach(struct ifnet *ifp)
|
||||
|
||||
mli = MLD_IFINFO(ifp);
|
||||
if (mli->mli_version == MLD_VERSION_2) {
|
||||
IF_ADDR_RLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
restart:
|
||||
TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
|
||||
if (ifma->ifma_addr->sa_family != AF_INET6 ||
|
||||
ifma->ifma_protospec == NULL)
|
||||
continue;
|
||||
@ -560,8 +561,12 @@ mld_ifdetach(struct ifnet *ifp)
|
||||
ifma->ifma_protospec = NULL;
|
||||
}
|
||||
in6m_clear_recorded(inm);
|
||||
if (__predict_false(ifma6_restart)) {
|
||||
ifma6_restart = false;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
}
|
||||
|
||||
MLD_UNLOCK();
|
||||
@ -696,7 +701,7 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6,
|
||||
* interface, kick the report timer.
|
||||
*/
|
||||
CTR2(KTR_MLD, "process v1 general query on ifp %p(%s)",
|
||||
ifp, if_name(ifp));
|
||||
ifp, if_name(ifp));
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
if (ifma->ifma_addr->sa_family != AF_INET6 ||
|
||||
ifma->ifma_protospec == NULL)
|
||||
@ -1326,7 +1331,7 @@ mld_fasttimo_vnet(void)
|
||||
struct mbufq qrq; /* Query response packets */
|
||||
struct ifnet *ifp;
|
||||
struct mld_ifsoftc *mli;
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifmultiaddr *ifma, *next;
|
||||
struct in6_multi *inm, *tinm;
|
||||
struct in6_multi_head inmh;
|
||||
int uri_fasthz;
|
||||
@ -1388,8 +1393,9 @@ mld_fasttimo_vnet(void)
|
||||
mbufq_init(&scq, MLD_MAX_STATE_CHANGE_PACKETS);
|
||||
}
|
||||
|
||||
IF_ADDR_RLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
restart:
|
||||
TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
|
||||
if (ifma->ifma_addr->sa_family != AF_INET6 ||
|
||||
ifma->ifma_protospec == NULL)
|
||||
continue;
|
||||
@ -1403,8 +1409,12 @@ mld_fasttimo_vnet(void)
|
||||
&scq, inm, uri_fasthz);
|
||||
break;
|
||||
}
|
||||
if (__predict_false(ifma6_restart)) {
|
||||
ifma6_restart = false;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
|
||||
switch (mli->mli_version) {
|
||||
case MLD_VERSION_1:
|
||||
@ -1641,7 +1651,7 @@ mld_set_version(struct mld_ifsoftc *mli, const int version)
|
||||
static void
|
||||
mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
|
||||
{
|
||||
struct ifmultiaddr *ifma;
|
||||
struct ifmultiaddr *ifma, *next;
|
||||
struct ifnet *ifp;
|
||||
struct in6_multi *inm;
|
||||
struct in6_multi_head inmh;
|
||||
@ -1666,8 +1676,9 @@ mld_v2_cancel_link_timers(struct mld_ifsoftc *mli)
|
||||
|
||||
ifp = mli->mli_ifp;
|
||||
|
||||
IF_ADDR_RLOCK(ifp);
|
||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
||||
IF_ADDR_WLOCK(ifp);
|
||||
restart:
|
||||
TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, next) {
|
||||
if (ifma->ifma_addr->sa_family != AF_INET6)
|
||||
continue;
|
||||
inm = (struct in6_multi *)ifma->ifma_protospec;
|
||||
@ -1702,8 +1713,12 @@ 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;
|
||||
}
|
||||
}
|
||||
IF_ADDR_RUNLOCK(ifp);
|
||||
IF_ADDR_WUNLOCK(ifp);
|
||||
in6m_release_list_deferred(&inmh);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user