Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet/in_mcast.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
#include <sys/protosw.h> | #include <sys/protosw.h> | ||||
#include <sys/rmlock.h> | #include <sys/rmlock.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/socketvar.h> | #include <sys/socketvar.h> | ||||
#include <sys/protosw.h> | #include <sys/protosw.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/ktr.h> | #include <sys/ktr.h> | ||||
#include <sys/taskqueue.h> | #include <sys/taskqueue.h> | ||||
#include <sys/gtaskqueue.h> | |||||
#include <sys/tree.h> | #include <sys/tree.h> | ||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_dl.h> | #include <net/if_dl.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#include <net/ethernet.h> | |||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_systm.h> | #include <netinet/in_systm.h> | ||||
#include <netinet/in_fib.h> | #include <netinet/in_fib.h> | ||||
#include <netinet/in_pcb.h> | #include <netinet/in_pcb.h> | ||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <netinet/ip_var.h> | #include <netinet/ip_var.h> | ||||
#include <netinet/igmp_var.h> | #include <netinet/igmp_var.h> | ||||
Show All 16 Lines | static MALLOC_DEFINE(M_INMFILTER, "in_mfilter", | ||||
"IPv4 multicast PCB-layer source filter"); | "IPv4 multicast PCB-layer source filter"); | ||||
static MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group"); | static MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group"); | ||||
static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options"); | static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options"); | ||||
static MALLOC_DEFINE(M_IPMSOURCE, "ip_msource", | static MALLOC_DEFINE(M_IPMSOURCE, "ip_msource", | ||||
"IPv4 multicast IGMP-layer source filter"); | "IPv4 multicast IGMP-layer source filter"); | ||||
/* | /* | ||||
* Locking: | * Locking: | ||||
* - Lock order is: Giant, INP_WLOCK, IN_MULTI_LOCK, IGMP_LOCK, IF_ADDR_LOCK. | * - Lock order is: Giant, INP_WLOCK, IN_MULTI_LIST_LOCK, IGMP_LOCK, IF_ADDR_LOCK. | ||||
* - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however | * - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however | ||||
* it can be taken by code in net/if.c also. | * it can be taken by code in net/if.c also. | ||||
* - ip_moptions and in_mfilter are covered by the INP_WLOCK. | * - ip_moptions and in_mfilter are covered by the INP_WLOCK. | ||||
* | * | ||||
* struct in_multi is covered by IN_MULTI_LOCK. There isn't strictly | * struct in_multi is covered by IN_MULTI_LIST_LOCK. There isn't strictly | ||||
* any need for in_multi itself to be virtualized -- it is bound to an ifp | * any need for in_multi itself to be virtualized -- it is bound to an ifp | ||||
* anyway no matter what happens. | * anyway no matter what happens. | ||||
*/ | */ | ||||
struct mtx in_multi_mtx; | struct mtx in_multi_list_mtx; | ||||
MTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF); | MTX_SYSINIT(in_multi_mtx, &in_multi_list_mtx, "in_multi_list_mtx", MTX_DEF); | ||||
struct mtx in_multi_free_mtx; | |||||
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"); | |||||
/* | /* | ||||
* Functions with non-static linkage defined in this file should be | * Functions with non-static linkage defined in this file should be | ||||
* declared in in_var.h: | * declared in in_var.h: | ||||
* imo_multi_filter() | * imo_multi_filter() | ||||
* in_addmulti() | * in_addmulti() | ||||
* in_delmulti() | * in_delmulti() | ||||
* in_joingroup() | * in_joingroup() | ||||
* in_joingroup_locked() | * in_joingroup_locked() | ||||
Show All 32 Lines | |||||
static int inm_get_source(struct in_multi *inm, const in_addr_t haddr, | static int inm_get_source(struct in_multi *inm, const in_addr_t haddr, | ||||
const int noalloc, struct ip_msource **pims); | const int noalloc, struct ip_msource **pims); | ||||
#ifdef KTR | #ifdef KTR | ||||
static int inm_is_ifp_detached(const struct in_multi *); | static int inm_is_ifp_detached(const struct in_multi *); | ||||
#endif | #endif | ||||
static int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *); | static int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *); | ||||
static void inm_purge(struct in_multi *); | static void inm_purge(struct in_multi *); | ||||
static void inm_reap(struct in_multi *); | static void inm_reap(struct in_multi *); | ||||
static void inm_release(struct in_multi *); | |||||
static struct ip_moptions * | static struct ip_moptions * | ||||
inp_findmoptions(struct inpcb *); | inp_findmoptions(struct inpcb *); | ||||
static void inp_freemoptions_internal(struct ip_moptions *); | static void inp_freemoptions_internal(struct ip_moptions *); | ||||
static void inp_gcmoptions(void *, int); | static void inp_gcmoptions(void *, int); | ||||
static int inp_get_source_filters(struct inpcb *, struct sockopt *); | static int inp_get_source_filters(struct inpcb *, struct sockopt *); | ||||
static int inp_join_group(struct inpcb *, struct sockopt *); | static int inp_join_group(struct inpcb *, struct sockopt *); | ||||
static int inp_leave_group(struct inpcb *, struct sockopt *); | static int inp_leave_group(struct inpcb *, struct sockopt *); | ||||
static struct ifnet * | static struct ifnet * | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if (ifp != NULL) { | ||||
*/ | */ | ||||
KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__)); | KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__)); | ||||
} | } | ||||
return (ifp == NULL); | return (ifp == NULL); | ||||
} | } | ||||
#endif | #endif | ||||
static struct grouptask free_gtask; | |||||
static struct in_multi_head inm_free_list; | |||||
static void inm_release_task(void *arg __unused); | |||||
static void inm_init(void) | |||||
{ | |||||
SLIST_INIT(&inm_free_list); | |||||
taskqgroup_config_gtask_init(NULL, &free_gtask, inm_release_task, "inm release task"); | |||||
} | |||||
SYSINIT(inm_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, | |||||
inm_init, NULL); | |||||
void | |||||
inm_release_list_deferred(struct in_multi_head *inmh) | |||||
{ | |||||
if (SLIST_EMPTY(inmh)) | |||||
return; | |||||
mtx_lock(&in_multi_free_mtx); | |||||
SLIST_CONCAT(&inm_free_list, inmh, in_multi, inm_nrele); | |||||
mtx_unlock(&in_multi_free_mtx); | |||||
GROUPTASK_ENQUEUE(&free_gtask); | |||||
} | |||||
void | |||||
inm_release_deferred(struct in_multi *inm) | |||||
{ | |||||
struct in_multi_head tmp; | |||||
IN_MULTI_LIST_LOCK_ASSERT(); | |||||
MPASS(inm->inm_refcount > 0); | |||||
if (--inm->inm_refcount == 0) { | |||||
SLIST_INIT(&tmp); | |||||
inm->inm_ifma->ifma_protospec = NULL; | |||||
SLIST_INSERT_HEAD(&tmp, inm, inm_nrele); | |||||
inm_release_list_deferred(&tmp); | |||||
} | |||||
} | |||||
static void | |||||
inm_release_task(void *arg __unused) | |||||
{ | |||||
struct in_multi_head inm_free_tmp; | |||||
struct in_multi *inm, *tinm; | |||||
SLIST_INIT(&inm_free_tmp); | |||||
mtx_lock(&in_multi_free_mtx); | |||||
SLIST_CONCAT(&inm_free_tmp, &inm_free_list, in_multi, inm_nrele); | |||||
mtx_unlock(&in_multi_free_mtx); | |||||
IN_MULTI_LOCK(); | |||||
SLIST_FOREACH_SAFE(inm, &inm_free_tmp, inm_nrele, tinm) { | |||||
SLIST_REMOVE_HEAD(&inm_free_tmp, inm_nrele); | |||||
MPASS(inm); | |||||
inm_release(inm); | |||||
} | |||||
IN_MULTI_UNLOCK(); | |||||
} | |||||
/* | /* | ||||
* Initialize an in_mfilter structure to a known state at t0, t1 | * Initialize an in_mfilter structure to a known state at t0, t1 | ||||
* with an empty source filter list. | * with an empty source filter list. | ||||
*/ | */ | ||||
static __inline void | static __inline void | ||||
imf_init(struct in_mfilter *imf, const int st0, const int st1) | imf_init(struct in_mfilter *imf, const int st0, const int st1) | ||||
{ | { | ||||
memset(imf, 0, sizeof(struct in_mfilter)); | memset(imf, 0, sizeof(struct in_mfilter)); | ||||
RB_INIT(&imf->imf_sources); | RB_INIT(&imf->imf_sources); | ||||
imf->imf_st[0] = st0; | imf->imf_st[0] = st0; | ||||
imf->imf_st[1] = st1; | imf->imf_st[1] = st1; | ||||
} | } | ||||
/* | /* | ||||
* Function for looking up an in_multi record for an IPv4 multicast address | * Function for looking up an in_multi record for an IPv4 multicast address | ||||
* on a given interface. ifp must be valid. If no record found, return NULL. | * on a given interface. ifp must be valid. If no record found, return NULL. | ||||
* The IN_MULTI_LOCK and IF_ADDR_LOCK on ifp must be held. | * The IN_MULTI_LIST_LOCK and IF_ADDR_LOCK on ifp must be held. | ||||
*/ | */ | ||||
struct in_multi * | struct in_multi * | ||||
inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina) | inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina) | ||||
{ | { | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct in_multi *inm; | struct in_multi *inm; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IF_ADDR_LOCK_ASSERT(ifp); | IF_ADDR_LOCK_ASSERT(ifp); | ||||
inm = NULL; | inm = NULL; | ||||
TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { | TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { | ||||
if (ifma->ifma_addr->sa_family == AF_INET) { | if (ifma->ifma_addr->sa_family == AF_INET) { | ||||
inm = (struct in_multi *)ifma->ifma_protospec; | inm = (struct in_multi *)ifma->ifma_protospec; | ||||
if (inm->inm_addr.s_addr == ina.s_addr) | if (inm->inm_addr.s_addr == ina.s_addr) | ||||
break; | break; | ||||
inm = NULL; | inm = NULL; | ||||
} | } | ||||
} | } | ||||
return (inm); | return (inm); | ||||
} | } | ||||
/* | /* | ||||
* Wrapper for inm_lookup_locked(). | * Wrapper for inm_lookup_locked(). | ||||
* The IF_ADDR_LOCK will be taken on ifp and released on return. | * The IF_ADDR_LOCK will be taken on ifp and released on return. | ||||
*/ | */ | ||||
struct in_multi * | struct in_multi * | ||||
inm_lookup(struct ifnet *ifp, const struct in_addr ina) | inm_lookup(struct ifnet *ifp, const struct in_addr ina) | ||||
{ | { | ||||
struct in_multi *inm; | struct in_multi *inm; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
IF_ADDR_RLOCK(ifp); | IF_ADDR_RLOCK(ifp); | ||||
inm = inm_lookup_locked(ifp, ina); | inm = inm_lookup_locked(ifp, ina); | ||||
IF_ADDR_RUNLOCK(ifp); | IF_ADDR_RUNLOCK(ifp); | ||||
return (inm); | return (inm); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Lines | in_getmulti(struct ifnet *ifp, const struct in_addr *group, | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct in_ifinfo *ii; | struct in_ifinfo *ii; | ||||
struct in_multi *inm; | struct in_multi *inm; | ||||
int error; | int error; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LOCK_ASSERT(); | ||||
ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET]; | ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET]; | ||||
IN_MULTI_LIST_LOCK(); | |||||
inm = inm_lookup(ifp, *group); | inm = inm_lookup(ifp, *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->inm_refcount >= 1, | KASSERT(inm->inm_refcount >= 1, | ||||
("%s: bad refcount %d", __func__, inm->inm_refcount)); | ("%s: bad refcount %d", __func__, inm->inm_refcount)); | ||||
++inm->inm_refcount; | inm_acquire_locked(inm); | ||||
*pinm = inm; | *pinm = inm; | ||||
return (0); | |||||
} | } | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
if (inm != NULL) | |||||
return (0); | |||||
memset(&gsin, 0, sizeof(gsin)); | memset(&gsin, 0, sizeof(gsin)); | ||||
gsin.sin_family = AF_INET; | gsin.sin_family = AF_INET; | ||||
gsin.sin_len = sizeof(struct sockaddr_in); | gsin.sin_len = sizeof(struct sockaddr_in); | ||||
gsin.sin_addr = *group; | gsin.sin_addr = *group; | ||||
/* | /* | ||||
* Check if a link-layer group is already associated | * Check if a link-layer group is already associated | ||||
* with this network-layer group on the given ifnet. | * with this network-layer group on the given ifnet. | ||||
*/ | */ | ||||
error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma); | error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
/* XXX ifma_protospec must be covered by IF_ADDR_LOCK */ | /* XXX ifma_protospec must be covered by IF_ADDR_LOCK */ | ||||
IN_MULTI_LIST_LOCK(); | |||||
IF_ADDR_WLOCK(ifp); | IF_ADDR_WLOCK(ifp); | ||||
/* | /* | ||||
* If something other than netinet is occupying the link-layer | * If something other than netinet is occupying the link-layer | ||||
* group, print a meaningful error message and back out of | * group, print a meaningful error message and back out of | ||||
* the allocation. | * the allocation. | ||||
* Otherwise, bump the refcount on the existing network-layer | * Otherwise, bump the refcount on the existing network-layer | ||||
* group association and return it. | * group association and return it. | ||||
Show All 9 Lines | #ifdef INVARIANTS | ||||
if (inm->inm_ifma != ifma || inm->inm_ifp != ifp || | if (inm->inm_ifma != ifma || inm->inm_ifp != ifp || | ||||
!in_hosteq(inm->inm_addr, *group)) { | !in_hosteq(inm->inm_addr, *group)) { | ||||
char addrbuf[INET_ADDRSTRLEN]; | char addrbuf[INET_ADDRSTRLEN]; | ||||
panic("%s: ifma %p is inconsistent with %p (%s)", | panic("%s: ifma %p is inconsistent with %p (%s)", | ||||
__func__, ifma, inm, inet_ntoa_r(*group, addrbuf)); | __func__, ifma, inm, inet_ntoa_r(*group, addrbuf)); | ||||
} | } | ||||
#endif | #endif | ||||
++inm->inm_refcount; | inm_acquire_locked(inm); | ||||
*pinm = inm; | *pinm = inm; | ||||
IF_ADDR_WUNLOCK(ifp); | goto out_locked; | ||||
return (0); | |||||
} | } | ||||
IF_ADDR_WLOCK_ASSERT(ifp); | IF_ADDR_WLOCK_ASSERT(ifp); | ||||
/* | /* | ||||
* A new in_multi record is needed; allocate and initialize it. | * A new in_multi record is needed; allocate and initialize it. | ||||
* We DO NOT perform an IGMP join as the in_ layer may need to | * We DO NOT perform an IGMP join as the in_ layer may need to | ||||
* push an initial source list down to IGMP to support SSM. | * push an initial source list down to IGMP to support SSM. | ||||
* | * | ||||
* The initial source filter state is INCLUDE, {} as per the RFC. | * The initial source filter state is INCLUDE, {} as per the RFC. | ||||
*/ | */ | ||||
inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO); | inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO); | ||||
if (inm == NULL) { | if (inm == NULL) { | ||||
IF_ADDR_WUNLOCK(ifp); | IF_ADDR_WUNLOCK(ifp); | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
if_delmulti_ifma(ifma); | if_delmulti_ifma(ifma); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
inm->inm_addr = *group; | inm->inm_addr = *group; | ||||
inm->inm_ifp = ifp; | inm->inm_ifp = ifp; | ||||
inm->inm_igi = ii->ii_igmp; | inm->inm_igi = ii->ii_igmp; | ||||
inm->inm_ifma = ifma; | inm->inm_ifma = ifma; | ||||
inm->inm_refcount = 1; | inm->inm_refcount = 1; | ||||
inm->inm_state = IGMP_NOT_MEMBER; | inm->inm_state = IGMP_NOT_MEMBER; | ||||
mbufq_init(&inm->inm_scq, IGMP_MAX_STATE_CHANGES); | mbufq_init(&inm->inm_scq, IGMP_MAX_STATE_CHANGES); | ||||
inm->inm_st[0].iss_fmode = MCAST_UNDEFINED; | inm->inm_st[0].iss_fmode = MCAST_UNDEFINED; | ||||
inm->inm_st[1].iss_fmode = MCAST_UNDEFINED; | inm->inm_st[1].iss_fmode = MCAST_UNDEFINED; | ||||
RB_INIT(&inm->inm_srcs); | RB_INIT(&inm->inm_srcs); | ||||
ifma->ifma_protospec = inm; | ifma->ifma_protospec = inm; | ||||
*pinm = inm; | *pinm = inm; | ||||
out_locked: | |||||
IF_ADDR_WUNLOCK(ifp); | IF_ADDR_WUNLOCK(ifp); | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Drop a reference to an in_multi record. | * Drop a reference to an in_multi record. | ||||
* | * | ||||
* If the refcount drops to 0, free the in_multi record and | * If the refcount drops to 0, free the in_multi record and | ||||
* delete the underlying link-layer membership. | * delete the underlying link-layer membership. | ||||
*/ | */ | ||||
void | static void | ||||
inm_release_locked(struct in_multi *inm) | inm_release(struct in_multi *inm) | ||||
{ | { | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct ifnet *ifp; | |||||
IN_MULTI_LOCK_ASSERT(); | |||||
CTR2(KTR_IGMPV3, "%s: refcount is %d", __func__, inm->inm_refcount); | CTR2(KTR_IGMPV3, "%s: refcount is %d", __func__, inm->inm_refcount); | ||||
MPASS(inm->inm_refcount == 0); | |||||
if (--inm->inm_refcount > 0) { | |||||
CTR2(KTR_IGMPV3, "%s: refcount is now %d", __func__, | |||||
inm->inm_refcount); | |||||
return; | |||||
} | |||||
CTR2(KTR_IGMPV3, "%s: freeing inm %p", __func__, inm); | CTR2(KTR_IGMPV3, "%s: freeing inm %p", __func__, inm); | ||||
ifma = inm->inm_ifma; | ifma = inm->inm_ifma; | ||||
ifp = inm->inm_ifp; | |||||
/* XXX this access is not covered by IF_ADDR_LOCK */ | /* XXX this access is not covered by IF_ADDR_LOCK */ | ||||
CTR2(KTR_IGMPV3, "%s: purging ifma %p", __func__, ifma); | CTR2(KTR_IGMPV3, "%s: purging ifma %p", __func__, ifma); | ||||
KASSERT(ifma->ifma_protospec == inm, | if (ifp) | ||||
("%s: ifma_protospec != inm", __func__)); | CURVNET_SET(ifp->if_vnet); | ||||
ifma->ifma_protospec = NULL; | |||||
inm_purge(inm); | inm_purge(inm); | ||||
free(inm, M_IPMADDR); | free(inm, M_IPMADDR); | ||||
if_delmulti_ifma(ifma); | if_delmulti_ifma(ifma); | ||||
if (ifp) | |||||
CURVNET_RESTORE(); | |||||
} | } | ||||
/* | /* | ||||
* Clear recorded source entries for a group. | * Clear recorded source entries for a group. | ||||
* Used by the IGMP code. Caller must hold the IN_MULTI lock. | * Used by the IGMP code. Caller must hold the IN_MULTI lock. | ||||
* FIXME: Should reap. | * FIXME: Should reap. | ||||
*/ | */ | ||||
void | void | ||||
inm_clear_recorded(struct in_multi *inm) | inm_clear_recorded(struct in_multi *inm) | ||||
{ | { | ||||
struct ip_msource *ims; | struct ip_msource *ims; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { | RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { | ||||
if (ims->ims_stp) { | if (ims->ims_stp) { | ||||
ims->ims_stp = 0; | ims->ims_stp = 0; | ||||
--inm->inm_st[1].iss_rec; | --inm->inm_st[1].iss_rec; | ||||
} | } | ||||
} | } | ||||
KASSERT(inm->inm_st[1].iss_rec == 0, | KASSERT(inm->inm_st[1].iss_rec == 0, | ||||
Show All 23 Lines | |||||
* Return <0 if any error occurred (negated errno code). | * Return <0 if any error occurred (negated errno code). | ||||
*/ | */ | ||||
int | int | ||||
inm_record_source(struct in_multi *inm, const in_addr_t naddr) | inm_record_source(struct in_multi *inm, const in_addr_t naddr) | ||||
{ | { | ||||
struct ip_msource find; | struct ip_msource find; | ||||
struct ip_msource *ims, *nims; | struct ip_msource *ims, *nims; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LIST_LOCK_ASSERT(); | ||||
find.ims_haddr = ntohl(naddr); | find.ims_haddr = ntohl(naddr); | ||||
ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); | ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); | ||||
if (ims && ims->ims_stp) | if (ims && ims->ims_stp) | ||||
return (0); | return (0); | ||||
if (ims == NULL) { | if (ims == NULL) { | ||||
if (inm->inm_nsrc == in_mcast_maxgrpsrc) | if (inm->inm_nsrc == in_mcast_maxgrpsrc) | ||||
return (-ENOSPC); | return (-ENOSPC); | ||||
▲ Show 20 Lines • Show All 310 Lines • ▼ Show 20 Lines | inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) | ||||
struct ip_msource *ims, *nims; | struct ip_msource *ims, *nims; | ||||
struct in_msource *lims; | struct in_msource *lims; | ||||
int schanged, error; | int schanged, error; | ||||
int nsrc0, nsrc1; | int nsrc0, nsrc1; | ||||
schanged = 0; | schanged = 0; | ||||
error = 0; | error = 0; | ||||
nsrc1 = nsrc0 = 0; | nsrc1 = nsrc0 = 0; | ||||
IN_MULTI_LIST_LOCK_ASSERT(); | |||||
/* | /* | ||||
* Update the source filters first, as this may fail. | * Update the source filters first, as this may fail. | ||||
* Maintain count of in-mode filters at t0, t1. These are | * Maintain count of in-mode filters at t0, t1. These are | ||||
* used to work out if we transition into ASM mode or not. | * used to work out if we transition into ASM mode or not. | ||||
* Maintain a count of source filters whose state was | * Maintain a count of source filters whose state was | ||||
* actually modified by this operation. | * actually modified by this operation. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 190 Lines • ▼ Show 20 Lines | |||||
in_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina, | in_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina, | ||||
/*const*/ struct in_mfilter *imf, struct in_multi **pinm) | /*const*/ struct in_mfilter *imf, struct in_multi **pinm) | ||||
{ | { | ||||
struct in_mfilter timf; | struct in_mfilter timf; | ||||
struct in_multi *inm; | struct in_multi *inm; | ||||
int error; | int error; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LOCK_ASSERT(); | ||||
IN_MULTI_LIST_UNLOCK_ASSERT(); | |||||
CTR4(KTR_IGMPV3, "%s: join 0x%08x on %p(%s))", __func__, | CTR4(KTR_IGMPV3, "%s: join 0x%08x on %p(%s))", __func__, | ||||
ntohl(gina->s_addr), ifp, ifp->if_xname); | ntohl(gina->s_addr), ifp, ifp->if_xname); | ||||
error = 0; | error = 0; | ||||
inm = NULL; | inm = NULL; | ||||
/* | /* | ||||
* If no imf was specified (i.e. kernel consumer), | * If no imf was specified (i.e. kernel consumer), | ||||
* fake one up and assume it is an ASM join. | * fake one up and assume it is an ASM join. | ||||
*/ | */ | ||||
if (imf == NULL) { | if (imf == NULL) { | ||||
imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); | imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); | ||||
imf = &timf; | imf = &timf; | ||||
} | } | ||||
error = in_getmulti(ifp, gina, &inm); | error = in_getmulti(ifp, gina, &inm); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: in_getmulti() failure", __func__); | CTR1(KTR_IGMPV3, "%s: in_getmulti() failure", __func__); | ||||
return (error); | return (error); | ||||
} | } | ||||
IN_MULTI_LIST_LOCK(); | |||||
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | ||||
error = inm_merge(inm, imf); | error = inm_merge(inm, imf); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); | ||||
goto out_inm_release; | goto out_inm_release; | ||||
} | } | ||||
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | ||||
error = igmp_change_state(inm); | error = igmp_change_state(inm); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed to update source", __func__); | CTR1(KTR_IGMPV3, "%s: failed to update source", __func__); | ||||
goto out_inm_release; | goto out_inm_release; | ||||
} | } | ||||
out_inm_release: | out_inm_release: | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
if (error) { | if (error) { | ||||
CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); | CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); | ||||
inm_release_locked(inm); | inm_release_deferred(inm); | ||||
} else { | } else { | ||||
*pinm = inm; | *pinm = inm; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
Show All 28 Lines | |||||
in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf) | in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf) | ||||
{ | { | ||||
struct in_mfilter timf; | struct in_mfilter timf; | ||||
int error; | int error; | ||||
error = 0; | error = 0; | ||||
IN_MULTI_LOCK_ASSERT(); | IN_MULTI_LOCK_ASSERT(); | ||||
IN_MULTI_LIST_UNLOCK_ASSERT(); | |||||
CTR5(KTR_IGMPV3, "%s: leave inm %p, 0x%08x/%s, imf %p", __func__, | CTR5(KTR_IGMPV3, "%s: leave inm %p, 0x%08x/%s, imf %p", __func__, | ||||
inm, ntohl(inm->inm_addr.s_addr), | inm, ntohl(inm->inm_addr.s_addr), | ||||
(inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_xname), | (inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_xname), | ||||
imf); | imf); | ||||
/* | /* | ||||
* If no imf was specified (i.e. kernel consumer), | * If no imf was specified (i.e. kernel consumer), | ||||
* fake one up and assume it is an ASM join. | * fake one up and assume it is an ASM join. | ||||
*/ | */ | ||||
if (imf == NULL) { | if (imf == NULL) { | ||||
imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED); | imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED); | ||||
imf = &timf; | imf = &timf; | ||||
} | } | ||||
/* | /* | ||||
* Begin state merge transaction at IGMP layer. | * Begin state merge transaction at IGMP layer. | ||||
* | * | ||||
* As this particular invocation should not cause any memory | * As this particular invocation should not cause any memory | ||||
* to be allocated, and there is no opportunity to roll back | * to be allocated, and there is no opportunity to roll back | ||||
* the transaction, it MUST NOT fail. | * the transaction, it MUST NOT fail. | ||||
*/ | */ | ||||
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | ||||
IN_MULTI_LIST_LOCK(); | |||||
error = inm_merge(inm, imf); | error = inm_merge(inm, imf); | ||||
KASSERT(error == 0, ("%s: failed to merge inm state", __func__)); | KASSERT(error == 0, ("%s: failed to merge inm state", __func__)); | ||||
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | ||||
CURVNET_SET(inm->inm_ifp->if_vnet); | CURVNET_SET(inm->inm_ifp->if_vnet); | ||||
error = igmp_change_state(inm); | error = igmp_change_state(inm); | ||||
inm_release_deferred(inm); | |||||
IN_MULTI_LIST_UNLOCK(); | |||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
if (error) | if (error) | ||||
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); | ||||
CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); | CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); | ||||
inm_release_locked(inm); | |||||
return (error); | return (error); | ||||
} | } | ||||
/*#ifndef BURN_BRIDGES*/ | /*#ifndef BURN_BRIDGES*/ | ||||
/* | /* | ||||
* Join an IPv4 multicast group in (*,G) exclusive mode. | * Join an IPv4 multicast group in (*,G) exclusive mode. | ||||
* The group must be a 224.0.0.0/24 link-scope group. | * The group must be a 224.0.0.0/24 link-scope group. | ||||
Show All 15 Lines | #endif | ||||
error = in_joingroup(ifp, ap, NULL, &pinm); | error = in_joingroup(ifp, ap, NULL, &pinm); | ||||
if (error != 0) | if (error != 0) | ||||
pinm = NULL; | pinm = NULL; | ||||
return (pinm); | return (pinm); | ||||
} | } | ||||
/* | /* | ||||
* Leave an IPv4 multicast group, assumed to be in exclusive (*,G) mode. | |||||
* This KPI is for legacy kernel consumers only. | |||||
*/ | |||||
void | |||||
in_delmulti(struct in_multi *inm) | |||||
{ | |||||
(void)in_leavegroup(inm, NULL); | |||||
} | |||||
/*#endif*/ | |||||
/* | |||||
* Block or unblock an ASM multicast source on an inpcb. | * Block or unblock an ASM multicast source on an inpcb. | ||||
* This implements the delta-based API described in RFC 3678. | * This implements the delta-based API described in RFC 3678. | ||||
* | * | ||||
* The delta-based API applies only to exclusive-mode memberships. | * The delta-based API applies only to exclusive-mode memberships. | ||||
* An IGMP downcall will be performed. | * An IGMP downcall will be performed. | ||||
* | * | ||||
* SMPng: NOTE: Must take Giant as a join may create a new ifma. | * SMPng: NOTE: Must take Giant as a join may create a new ifma. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: merge imf state failed", __func__); | CTR1(KTR_IGMPV3, "%s: merge imf state failed", __func__); | ||||
goto out_imf_rollback; | goto out_imf_rollback; | ||||
} | } | ||||
/* | /* | ||||
* Begin state merge transaction at IGMP layer. | * Begin state merge transaction at IGMP layer. | ||||
*/ | */ | ||||
IN_MULTI_LOCK(); | IN_MULTI_LOCK(); | ||||
IN_MULTI_LIST_LOCK(); | |||||
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | ||||
error = inm_merge(inm, imf); | error = inm_merge(inm, imf); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); | ||||
goto out_in_multi_locked; | goto out_in_multi_locked; | ||||
} | } | ||||
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | ||||
error = igmp_change_state(inm); | error = igmp_change_state(inm); | ||||
if (error) | if (error) | ||||
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); | ||||
out_in_multi_locked: | out_in_multi_locked: | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_UNLOCK(); | ||||
IN_MULTI_UNLOCK(); | |||||
out_imf_rollback: | out_imf_rollback: | ||||
if (error) | if (error) | ||||
imf_rollback(imf); | imf_rollback(imf); | ||||
else | else | ||||
imf_commit(imf); | imf_commit(imf); | ||||
imf_reap(imf); | imf_reap(imf); | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | |||||
* operation is performed asynchronously in a separate task. | * operation is performed asynchronously in a separate task. | ||||
* | * | ||||
* SMPng: NOTE: assumes INP write lock is held. | * SMPng: NOTE: assumes INP write lock is held. | ||||
*/ | */ | ||||
void | void | ||||
inp_freemoptions(struct ip_moptions *imo) | inp_freemoptions(struct ip_moptions *imo) | ||||
{ | { | ||||
if (imo == NULL) | |||||
return; | |||||
KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__)); | KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__)); | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
STAILQ_INSERT_TAIL(&imo_gc_list, imo, imo_link); | STAILQ_INSERT_TAIL(&imo_gc_list, imo, imo_link); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
taskqueue_enqueue(taskqueue_thread, &imo_gc_task); | taskqueue_enqueue(taskqueue_thread, &imo_gc_task); | ||||
} | } | ||||
static void | static void | ||||
inp_freemoptions_internal(struct ip_moptions *imo) | inp_freemoptions_internal(struct ip_moptions *imo) | ||||
{ | { | ||||
struct in_mfilter *imf; | struct in_mfilter *imf; | ||||
size_t idx, nmships; | size_t idx, nmships; | ||||
Show All 14 Lines | inp_freemoptions_internal(struct ip_moptions *imo) | ||||
free(imo, M_IPMOPTS); | free(imo, M_IPMOPTS); | ||||
} | } | ||||
static void | static void | ||||
inp_gcmoptions(void *context, int pending) | inp_gcmoptions(void *context, int pending) | ||||
{ | { | ||||
struct ip_moptions *imo; | struct ip_moptions *imo; | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
while (!STAILQ_EMPTY(&imo_gc_list)) { | while (!STAILQ_EMPTY(&imo_gc_list)) { | ||||
imo = STAILQ_FIRST(&imo_gc_list); | imo = STAILQ_FIRST(&imo_gc_list); | ||||
STAILQ_REMOVE_HEAD(&imo_gc_list, imo_link); | STAILQ_REMOVE_HEAD(&imo_gc_list, imo_link); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
inp_freemoptions_internal(imo); | inp_freemoptions_internal(imo); | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
} | } | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
} | } | ||||
/* | /* | ||||
* Atomically get source filters on a socket for an IPv4 multicast group. | * Atomically get source filters on a socket for an IPv4 multicast group. | ||||
* Called with INP lock held; returns with lock released. | * Called with INP lock held; returns with lock released. | ||||
*/ | */ | ||||
static int | static int | ||||
inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) | inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) | ||||
▲ Show 20 Lines • Show All 523 Lines • ▼ Show 20 Lines | if (is_new) { | ||||
CTR1(KTR_IGMPV3, "%s: new join w/o source", __func__); | CTR1(KTR_IGMPV3, "%s: new join w/o source", __func__); | ||||
imf_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE); | imf_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Begin state merge transaction at IGMP layer. | * Begin state merge transaction at IGMP layer. | ||||
*/ | */ | ||||
in_pcbref(inp); | |||||
INP_WUNLOCK(inp); | |||||
IN_MULTI_LOCK(); | IN_MULTI_LOCK(); | ||||
if (is_new) { | if (is_new) { | ||||
error = in_joingroup_locked(ifp, &gsa->sin.sin_addr, imf, | error = in_joingroup_locked(ifp, &gsa->sin.sin_addr, imf, | ||||
&inm); | &inm); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: in_joingroup_locked failed", | CTR1(KTR_IGMPV3, "%s: in_joingroup_locked failed", | ||||
__func__); | __func__); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
goto out_imo_free; | goto out_imo_free; | ||||
} | } | ||||
imo->imo_membership[idx] = inm; | imo->imo_membership[idx] = inm; | ||||
} else { | } else { | ||||
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | ||||
IN_MULTI_LIST_LOCK(); | |||||
error = inm_merge(inm, imf); | error = inm_merge(inm, imf); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", | CTR1(KTR_IGMPV3, "%s: failed to merge inm state", | ||||
__func__); | __func__); | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
goto out_in_multi_locked; | goto out_in_multi_locked; | ||||
} | } | ||||
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | ||||
error = igmp_change_state(inm); | error = igmp_change_state(inm); | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", | CTR1(KTR_IGMPV3, "%s: failed igmp downcall", | ||||
__func__); | __func__); | ||||
goto out_in_multi_locked; | goto out_in_multi_locked; | ||||
} | } | ||||
} | } | ||||
out_in_multi_locked: | out_in_multi_locked: | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_UNLOCK(); | ||||
INP_WLOCK(inp); | |||||
INP_WLOCK_ASSERT(inp); | if (in_pcbrele_wlocked(inp)) | ||||
return (ENXIO); | |||||
if (error) { | if (error) { | ||||
imf_rollback(imf); | imf_rollback(imf); | ||||
if (is_new) | if (is_new) | ||||
imf_purge(imf); | imf_purge(imf); | ||||
else | else | ||||
imf_reap(imf); | imf_reap(imf); | ||||
} else { | } else { | ||||
imf_commit(imf); | imf_commit(imf); | ||||
▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | inp_leave_group(struct inpcb *inp, struct sockopt *sopt) | ||||
if (is_final) { | if (is_final) { | ||||
/* | /* | ||||
* Give up the multicast address record to which | * Give up the multicast address record to which | ||||
* the membership points. | * the membership points. | ||||
*/ | */ | ||||
(void)in_leavegroup_locked(inm, imf); | (void)in_leavegroup_locked(inm, imf); | ||||
} else { | } else { | ||||
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | ||||
IN_MULTI_LIST_LOCK(); | |||||
error = inm_merge(inm, imf); | error = inm_merge(inm, imf); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", | CTR1(KTR_IGMPV3, "%s: failed to merge inm state", | ||||
__func__); | __func__); | ||||
goto out_in_multi_locked; | goto out_in_multi_locked; | ||||
} | } | ||||
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | ||||
error = igmp_change_state(inm); | error = igmp_change_state(inm); | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", | CTR1(KTR_IGMPV3, "%s: failed igmp downcall", | ||||
__func__); | __func__); | ||||
} | } | ||||
} | } | ||||
out_in_multi_locked: | out_in_multi_locked: | ||||
▲ Show 20 Lines • Show All 219 Lines • ▼ Show 20 Lines | if (msfr.msfr_nsrcs > 0) { | ||||
free(kss, M_TEMP); | free(kss, M_TEMP); | ||||
} | } | ||||
if (error) | if (error) | ||||
goto out_imf_rollback; | goto out_imf_rollback; | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
IN_MULTI_LOCK(); | IN_MULTI_LOCK(); | ||||
IN_MULTI_LIST_LOCK(); | |||||
/* | /* | ||||
* Begin state merge transaction at IGMP layer. | * Begin state merge transaction at IGMP layer. | ||||
*/ | */ | ||||
CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); | ||||
error = inm_merge(inm, imf); | error = inm_merge(inm, imf); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); | CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
goto out_in_multi_locked; | goto out_in_multi_locked; | ||||
} | } | ||||
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); | ||||
error = igmp_change_state(inm); | error = igmp_change_state(inm); | ||||
IN_MULTI_LIST_UNLOCK(); | |||||
if (error) | if (error) | ||||
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); | CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); | ||||
out_in_multi_locked: | out_in_multi_locked: | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_UNLOCK(); | ||||
out_imf_rollback: | out_imf_rollback: | ||||
▲ Show 20 Lines • Show All 215 Lines • ▼ Show 20 Lines | if (ifp == NULL) { | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
retval = sysctl_wire_old_buffer(req, | retval = sysctl_wire_old_buffer(req, | ||||
sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr))); | sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr))); | ||||
if (retval) | if (retval) | ||||
return (retval); | return (retval); | ||||
IN_MULTI_LOCK(); | IN_MULTI_LIST_LOCK(); | ||||
IF_ADDR_RLOCK(ifp); | IF_ADDR_RLOCK(ifp); | ||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||
if (ifma->ifma_addr->sa_family != AF_INET || | if (ifma->ifma_addr->sa_family != AF_INET || | ||||
ifma->ifma_protospec == NULL) | ifma->ifma_protospec == NULL) | ||||
continue; | continue; | ||||
inm = (struct in_multi *)ifma->ifma_protospec; | inm = (struct in_multi *)ifma->ifma_protospec; | ||||
if (!in_hosteq(inm->inm_addr, group)) | if (!in_hosteq(inm->inm_addr, group)) | ||||
Show All 16 Lines | RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { | ||||
src.s_addr = htonl(ims->ims_haddr); | src.s_addr = htonl(ims->ims_haddr); | ||||
retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr)); | retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr)); | ||||
if (retval != 0) | if (retval != 0) | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
IF_ADDR_RUNLOCK(ifp); | IF_ADDR_RUNLOCK(ifp); | ||||
IN_MULTI_UNLOCK(); | IN_MULTI_LIST_UNLOCK(); | ||||
return (retval); | return (retval); | ||||
} | } | ||||
#if defined(KTR) && (KTR_COMPILE & KTR_IGMPV3) | #if defined(KTR) && (KTR_COMPILE & KTR_IGMPV3) | ||||
static const char *inm_modestrs[] = { "un", "in", "ex" }; | static const char *inm_modestrs[] = { "un", "in", "ex" }; | ||||
▲ Show 20 Lines • Show All 80 Lines • Show Last 20 Lines |