Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/in6_mcast.c
Show First 20 Lines • Show All 425 Lines • ▼ Show 20 Lines | in6_getmulti(struct ifnet *ifp, const struct in6_addr *group, | ||||
if (inm != NULL) { | if (inm != NULL) { | ||||
/* | /* | ||||
* If we already joined this group, just bump the | * If we already joined this group, just bump the | ||||
* refcount and return it. | * refcount and return it. | ||||
*/ | */ | ||||
KASSERT(inm->in6m_refcount >= 1, | KASSERT(inm->in6m_refcount >= 1, | ||||
("%s: bad refcount %d", __func__, inm->in6m_refcount)); | ("%s: bad refcount %d", __func__, inm->in6m_refcount)); | ||||
in6m_acquire_locked(inm); | /* reference acquired by lookup */ | ||||
*pinm = inm; | *pinm = inm; | ||||
goto out_locked; | goto out_locked; | ||||
} | } | ||||
memset(&gsin6, 0, sizeof(gsin6)); | memset(&gsin6, 0, sizeof(gsin6)); | ||||
gsin6.sin6_family = AF_INET6; | gsin6.sin6_family = AF_INET6; | ||||
gsin6.sin6_len = sizeof(struct sockaddr_in6); | gsin6.sin6_len = sizeof(struct sockaddr_in6); | ||||
gsin6.sin6_addr = *group; | gsin6.sin6_addr = *group; | ||||
Show All 25 Lines | #ifdef INVARIANTS | ||||
KASSERT(ifma->ifma_addr->sa_family == AF_INET6, | KASSERT(ifma->ifma_addr->sa_family == AF_INET6, | ||||
("%s: ifma not AF_INET6", __func__)); | ("%s: ifma not AF_INET6", __func__)); | ||||
KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__)); | KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__)); | ||||
if (inm->in6m_ifma != ifma || inm->in6m_ifp != ifp || | if (inm->in6m_ifma != ifma || inm->in6m_ifp != ifp || | ||||
!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, group)) | !IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, group)) | ||||
panic("%s: ifma %p is inconsistent with %p (%p)", | panic("%s: ifma %p is inconsistent with %p (%p)", | ||||
__func__, ifma, inm, group); | __func__, ifma, inm, group); | ||||
#endif | #endif | ||||
/* add caller reference */ | |||||
in6m_acquire_locked(inm); | in6m_acquire_locked(inm); | ||||
*pinm = inm; | *pinm = inm; | ||||
goto out_locked; | goto out_locked; | ||||
} | } | ||||
IF_ADDR_WLOCK_ASSERT(ifp); | IF_ADDR_WLOCK_ASSERT(ifp); | ||||
/* | /* | ||||
Show All 10 Lines | if (inm == NULL) { | ||||
IF_ADDR_WUNLOCK(ifp); | IF_ADDR_WUNLOCK(ifp); | ||||
if_delmulti_ifma(ifma); | if_delmulti_ifma(ifma); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
inm->in6m_addr = *group; | inm->in6m_addr = *group; | ||||
inm->in6m_ifp = ifp; | inm->in6m_ifp = ifp; | ||||
inm->in6m_mli = MLD_IFINFO(ifp); | inm->in6m_mli = MLD_IFINFO(ifp); | ||||
inm->in6m_ifma = ifma; | inm->in6m_ifma = ifma; | ||||
inm->in6m_refcount = 1; | ifma->ifma_refcount++; | ||||
/* one reference for the ifma and one for the caller */ | |||||
inm->in6m_refcount = 2; | |||||
inm->in6m_state = MLD_NOT_MEMBER; | inm->in6m_state = MLD_NOT_MEMBER; | ||||
mbufq_init(&inm->in6m_scq, MLD_MAX_STATE_CHANGES); | mbufq_init(&inm->in6m_scq, MLD_MAX_STATE_CHANGES); | ||||
inm->in6m_st[0].iss_fmode = MCAST_UNDEFINED; | inm->in6m_st[0].iss_fmode = MCAST_UNDEFINED; | ||||
inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; | inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; | ||||
RB_INIT(&inm->in6m_srcs); | RB_INIT(&inm->in6m_srcs); | ||||
ifma->ifma_protospec = inm; | ifma->ifma_protospec = inm; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | if (SLIST_EMPTY(inmh)) | ||||
return; | return; | ||||
mtx_lock(&in6_multi_free_mtx); | mtx_lock(&in6_multi_free_mtx); | ||||
SLIST_CONCAT(&in6m_free_list, inmh, in6_multi, in6m_nrele); | SLIST_CONCAT(&in6m_free_list, inmh, in6_multi, in6m_nrele); | ||||
mtx_unlock(&in6_multi_free_mtx); | mtx_unlock(&in6_multi_free_mtx); | ||||
GROUPTASK_ENQUEUE(&free_gtask); | GROUPTASK_ENQUEUE(&free_gtask); | ||||
} | } | ||||
void | void | ||||
in6m_release_deferred(struct in6_multi *inm) | |||||
{ | |||||
IN6_MULTI_LIST_LOCK_ASSERT(); | |||||
if (--inm->in6m_refcount == 0) { | |||||
mtx_lock(&in6_multi_free_mtx); | |||||
SLIST_INSERT_HEAD(&in6m_free_list, inm, in6m_nrele); | |||||
mtx_unlock(&in6_multi_free_mtx); | |||||
GROUPTASK_ENQUEUE(&free_gtask); | |||||
} | |||||
} | |||||
void | |||||
in6m_release_wait(void) | in6m_release_wait(void) | ||||
{ | { | ||||
/* Wait for all jobs to complete. */ | /* Wait for all jobs to complete. */ | ||||
gtaskqueue_drain_all(free_gtask.gt_taskqueue); | gtaskqueue_drain_all(free_gtask.gt_taskqueue); | ||||
} | } | ||||
void | void | ||||
in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm) | in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct ifaddr *ifa; | |||||
struct in6_ifaddr *ifa6; | |||||
struct in6_multi_mship *imm, *imm_tmp; | |||||
struct ifmultiaddr *ifma, *ll_ifma; | struct ifmultiaddr *ifma, *ll_ifma; | ||||
IN6_MULTI_LIST_LOCK_ASSERT(); | IN6_MULTI_LIST_LOCK_ASSERT(); | ||||
ifp = inm->in6m_ifp; | ifp = inm->in6m_ifp; | ||||
if (ifp == NULL) | if (ifp == NULL) | ||||
return; /* already called */ | return; /* already called */ | ||||
inm->in6m_ifp = NULL; | inm->in6m_ifp = NULL; | ||||
IF_ADDR_WLOCK_ASSERT(ifp); | IF_ADDR_WLOCK_ASSERT(ifp); | ||||
ifma = inm->in6m_ifma; | ifma = inm->in6m_ifma; | ||||
if (ifma == NULL) | if (ifma == NULL) | ||||
return; | return; | ||||
if_ref(ifp); | if_ref(ifp); | ||||
if (ifma->ifma_flags & IFMA_F_ENQUEUED) { | if (ifma->ifma_flags & IFMA_F_ENQUEUED) { | ||||
CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link); | CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link); | ||||
ifma->ifma_flags &= ~IFMA_F_ENQUEUED; | ifma->ifma_flags &= ~IFMA_F_ENQUEUED; | ||||
} | } | ||||
MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname); | MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname); | ||||
if ((ll_ifma = ifma->ifma_llifma) != NULL) { | if (ifma->ifma_ifp != NULL && (ll_ifma = ifma->ifma_llifma) != NULL) { | ||||
MPASS(ifma != ll_ifma); | MPASS(ifma != ll_ifma); | ||||
ifma->ifma_llifma = NULL; | ifma->ifma_llifma = NULL; | ||||
MPASS(ll_ifma->ifma_llifma == NULL); | MPASS(ll_ifma->ifma_llifma == NULL); | ||||
MPASS(ll_ifma->ifma_ifp == ifp); | MPASS(ll_ifma->ifma_ifp == ifp); | ||||
if (--ll_ifma->ifma_refcount == 0) { | if (--ll_ifma->ifma_refcount == 0) { | ||||
if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) { | if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) { | ||||
CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link); | CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link); | ||||
ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED; | ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED; | ||||
MCDPRINTF("removed ll_ifma: %p from %s -- from in6m -- \n", ll_ifma, ifp->if_xname); | |||||
} else { | |||||
MCDPRINTF("did not remove ll_ifma: %p from %s - not queued\n", ll_ifma, ifp->if_xname); | |||||
} | } | ||||
MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname); | |||||
if_freemulti(ll_ifma); | if_freemulti(ll_ifma); | ||||
} | } | ||||
} | } | ||||
} | |||||
int | |||||
in6m_remove_members(struct in6_multi_head *inmh, struct in6_multi *inm) | |||||
{ | |||||
struct in6_ifaddr *ifa6; | |||||
struct ifaddr *ifa; | |||||
struct in6_multi_mship *imm, *imm_tmp; | |||||
struct ifnet *ifp; | |||||
int released = 0; | |||||
ifp = inm->in6m_ifp; | |||||
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
if (ifa->ifa_addr->sa_family != AF_INET6) | if (ifa->ifa_addr->sa_family != AF_INET6) | ||||
continue; | continue; | ||||
ifa6 = (void *)ifa; | ifa6 = (void *)ifa; | ||||
LIST_FOREACH_SAFE(imm, &ifa6->ia6_memberships, | LIST_FOREACH_SAFE(imm, &ifa6->ia6_memberships, | ||||
i6mm_chain, imm_tmp) { | i6mm_chain, imm_tmp) { | ||||
if (inm == imm->i6mm_maddr) { | if (inm == imm->i6mm_maddr) { | ||||
LIST_REMOVE(imm, i6mm_chain); | LIST_REMOVE(imm, i6mm_chain); | ||||
free(imm, M_IP6MADDR); | |||||
in6m_rele_locked(inmh, inm); | in6m_rele_locked(inmh, inm); | ||||
released = (inm->in6m_refcount == 0); | |||||
free(imm, M_IP6MADDR); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return (released); | |||||
} | } | ||||
static void | static void | ||||
in6m_release_task(void *arg __unused) | in6m_release_task(void *arg __unused) | ||||
{ | { | ||||
struct in6_multi_head in6m_free_tmp; | struct in6_multi_head in6m_free_tmp; | ||||
struct in6_multi *inm, *tinm; | struct in6_multi *inm, *tinm; | ||||
SLIST_INIT(&in6m_free_tmp); | SLIST_INIT(&in6m_free_tmp); | ||||
mtx_lock(&in6_multi_free_mtx); | mtx_lock(&in6_multi_free_mtx); | ||||
▲ Show 20 Lines • Show All 655 Lines • ▼ Show 20 Lines | if (error) { | ||||
CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); | CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||
if (ifma->ifma_protospec == inm) { | if (ifma->ifma_protospec == inm) { | ||||
ifma->ifma_protospec = NULL; | ifma->ifma_protospec = NULL; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
in6m_disconnect_locked(&inmh, inm); | if (in6m_remove_members(&inmh, inm) == 0) | ||||
in6m_rele_locked(&inmh, inm); | in6m_rele_locked(&inmh, inm); | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
} else { | } else { | ||||
*pinm = inm; | *pinm = inm; | ||||
} | } | ||||
IN6_MULTI_LIST_UNLOCK(); | IN6_MULTI_LIST_UNLOCK(); | ||||
in6m_release_list_deferred(&inmh); | in6m_release_list_deferred(&inmh); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | #endif | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed mld downcall", __func__); | CTR1(KTR_MLD, "%s: failed mld downcall", __func__); | ||||
CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); | CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); | ||||
if (ifp) | if (ifp) | ||||
IF_ADDR_WLOCK(ifp); | IF_ADDR_WLOCK(ifp); | ||||
SLIST_INIT(&inmh); | SLIST_INIT(&inmh); | ||||
if (inm->in6m_refcount == 1) | if (in6m_remove_members(&inmh, inm) == 0) | ||||
in6m_disconnect_locked(&inmh, inm); | |||||
in6m_rele_locked(&inmh, inm); | in6m_rele_locked(&inmh, inm); | ||||
if (ifp) | if (ifp) | ||||
IF_ADDR_WUNLOCK(ifp); | IF_ADDR_WUNLOCK(ifp); | ||||
IN6_MULTI_LIST_UNLOCK(); | IN6_MULTI_LIST_UNLOCK(); | ||||
in6m_release_list_deferred(&inmh); | in6m_release_list_deferred(&inmh); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 692 Lines • ▼ Show 20 Lines | if (is_new) { | ||||
} | } | ||||
/* | /* | ||||
* Allocate the new slot upfront so we can deal with | * Allocate the new slot upfront so we can deal with | ||||
* grafting the new source filter in same code path | * grafting the new source filter in same code path | ||||
* as for join-source on existing membership. | * as for join-source on existing membership. | ||||
*/ | */ | ||||
idx = imo->im6o_num_memberships; | idx = imo->im6o_num_memberships; | ||||
imo->im6o_membership[idx] = NULL; | imo->im6o_membership[idx] = NULL; | ||||
imo->im6o_num_memberships++; | |||||
KASSERT(imo->im6o_mfilters != NULL, | KASSERT(imo->im6o_mfilters != NULL, | ||||
("%s: im6f_mfilters vector was not allocated", __func__)); | ("%s: im6f_mfilters vector was not allocated", __func__)); | ||||
imf = &imo->im6o_mfilters[idx]; | imf = &imo->im6o_mfilters[idx]; | ||||
KASSERT(RB_EMPTY(&imf->im6f_sources), | KASSERT(RB_EMPTY(&imf->im6f_sources), | ||||
("%s: im6f_sources not empty", __func__)); | ("%s: im6f_sources not empty", __func__)); | ||||
} | } | ||||
/* | /* | ||||
Show All 37 Lines | in6p_join_group(struct inpcb *inp, struct sockopt *sopt) | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
IN6_MULTI_LOCK(); | IN6_MULTI_LOCK(); | ||||
if (is_new) { | if (is_new) { | ||||
error = in6_joingroup_locked(ifp, &gsa->sin6.sin6_addr, imf, | error = in6_joingroup_locked(ifp, &gsa->sin6.sin6_addr, imf, | ||||
&inm, 0); | &inm, 0); | ||||
if (error) { | if (error) { | ||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_UNLOCK(); | ||||
INP_WLOCK(inp); | |||||
goto out_im6o_free; | goto out_im6o_free; | ||||
} | } | ||||
/* | /* | ||||
* NOTE: Refcount from in6_joingroup_locked() | * NOTE: Refcount from in6_joingroup_locked() | ||||
* is protecting membership. | * is protecting membership. | ||||
*/ | */ | ||||
MPASS(idx == imo->im6o_num_memberships); | |||||
imo->im6o_membership[idx] = inm; | imo->im6o_membership[idx] = inm; | ||||
imo->im6o_num_memberships++; | |||||
} else { | } else { | ||||
CTR1(KTR_MLD, "%s: merge inm state", __func__); | CTR1(KTR_MLD, "%s: merge inm state", __func__); | ||||
IN6_MULTI_LIST_LOCK(); | IN6_MULTI_LIST_LOCK(); | ||||
error = in6m_merge(inm, imf); | error = in6m_merge(inm, imf); | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed to merge inm state", | CTR1(KTR_MLD, "%s: failed to merge inm state", | ||||
__func__); | __func__); | ||||
else { | else { | ||||
▲ Show 20 Lines • Show All 802 Lines • Show Last 20 Lines |