Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet6/in6_mcast.c
Show All 35 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_inet6.h" | #include "opt_inet6.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/gtaskqueue.h> | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/protosw.h> | #include <sys/protosw.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/socketvar.h> | #include <sys/socketvar.h> | ||||
#include <sys/protosw.h> | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/priv.h> | #include <sys/priv.h> | ||||
#include <sys/ktr.h> | #include <sys/ktr.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 <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/udp.h> | |||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <netinet/ip_var.h> | |||||
#include <netinet/udp_var.h> | |||||
#include <netinet6/in6_fib.h> | #include <netinet6/in6_fib.h> | ||||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet/icmp6.h> | #include <netinet/icmp6.h> | ||||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
#include <netinet/in_pcb.h> | #include <netinet/in_pcb.h> | ||||
#include <netinet/tcp_var.h> | #include <netinet/tcp_var.h> | ||||
#include <netinet6/nd6.h> | #include <netinet6/nd6.h> | ||||
Show All 12 Lines | union sockunion { | ||||
struct sockaddr_in6 sin6; | struct sockaddr_in6 sin6; | ||||
}; | }; | ||||
typedef union sockunion sockunion_t; | typedef union sockunion sockunion_t; | ||||
#define __SOCKUNION_DECLARED | #define __SOCKUNION_DECLARED | ||||
#endif /* __SOCKUNION_DECLARED */ | #endif /* __SOCKUNION_DECLARED */ | ||||
static MALLOC_DEFINE(M_IN6MFILTER, "in6_mfilter", | static MALLOC_DEFINE(M_IN6MFILTER, "in6_mfilter", | ||||
"IPv6 multicast PCB-layer source filter"); | "IPv6 multicast PCB-layer source filter"); | ||||
static MALLOC_DEFINE(M_IP6MADDR, "in6_multi", "IPv6 multicast group"); | MALLOC_DEFINE(M_IP6MADDR, "in6_multi", "IPv6 multicast group"); | ||||
static MALLOC_DEFINE(M_IP6MOPTS, "ip6_moptions", "IPv6 multicast options"); | static MALLOC_DEFINE(M_IP6MOPTS, "ip6_moptions", "IPv6 multicast options"); | ||||
static MALLOC_DEFINE(M_IP6MSOURCE, "ip6_msource", | static MALLOC_DEFINE(M_IP6MSOURCE, "ip6_msource", | ||||
"IPv6 multicast MLD-layer source filter"); | "IPv6 multicast MLD-layer source filter"); | ||||
RB_GENERATE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp); | RB_GENERATE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp); | ||||
/* | /* | ||||
* Locking: | * Locking: | ||||
* - Lock order is: Giant, INP_WLOCK, IN6_MULTI_LOCK, MLD_LOCK, IF_ADDR_LOCK. | * - Lock order is: Giant, INP_WLOCK, IN6_MULTI_LOCK, MLD_LOCK, IF_ADDR_LOCK. | ||||
* - The IF_ADDR_LOCK is implicitly taken by in6m_lookup() earlier, however | * - The IF_ADDR_LOCK is implicitly taken by in6m_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. | ||||
* - ip6_moptions and in6_mfilter are covered by the INP_WLOCK. | * - ip6_moptions and in6_mfilter are covered by the INP_WLOCK. | ||||
* | * | ||||
* struct in6_multi is covered by IN6_MULTI_LOCK. There isn't strictly | * struct in6_multi is covered by IN6_MULTI_LOCK. There isn't strictly | ||||
* any need for in6_multi itself to be virtualized -- it is bound to an ifp | * any need for in6_multi itself to be virtualized -- it is bound to an ifp | ||||
* anyway no matter what happens. | * anyway no matter what happens. | ||||
*/ | */ | ||||
struct mtx in6_multi_mtx; | struct mtx in6_multi_list_mtx; | ||||
MTX_SYSINIT(in6_multi_mtx, &in6_multi_mtx, "in6_multi_mtx", MTX_DEF); | MTX_SYSINIT(in6_multi_mtx, &in6_multi_list_mtx, "in6_multi_list_mtx", MTX_DEF); | ||||
struct mtx in6_multi_free_mtx; | |||||
MTX_SYSINIT(in6_multi_free_mtx, &in6_multi_free_mtx, "in6_multi_free_mtx", MTX_DEF); | |||||
struct sx in6_multi_sx; | |||||
SX_SYSINIT(in6_multi_sx, &in6_multi_sx, "in6_multi_sx"); | |||||
static void im6f_commit(struct in6_mfilter *); | static void im6f_commit(struct in6_mfilter *); | ||||
static int im6f_get_source(struct in6_mfilter *imf, | static int im6f_get_source(struct in6_mfilter *imf, | ||||
const struct sockaddr_in6 *psin, | const struct sockaddr_in6 *psin, | ||||
struct in6_msource **); | struct in6_msource **); | ||||
static struct in6_msource * | static struct in6_msource * | ||||
im6f_graft(struct in6_mfilter *, const uint8_t, | im6f_graft(struct in6_mfilter *, const uint8_t, | ||||
const struct sockaddr_in6 *); | const struct sockaddr_in6 *); | ||||
static void im6f_leave(struct in6_mfilter *); | static void im6f_leave(struct in6_mfilter *); | ||||
static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *); | static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *); | ||||
static void im6f_purge(struct in6_mfilter *); | static void im6f_purge(struct in6_mfilter *); | ||||
static void im6f_rollback(struct in6_mfilter *); | static void im6f_rollback(struct in6_mfilter *); | ||||
static void im6f_reap(struct in6_mfilter *); | static void im6f_reap(struct in6_mfilter *); | ||||
static int im6o_grow(struct ip6_moptions *); | static int im6o_grow(struct ip6_moptions *); | ||||
static size_t im6o_match_group(const struct ip6_moptions *, | static size_t im6o_match_group(const struct ip6_moptions *, | ||||
const struct ifnet *, const struct sockaddr *); | const struct ifnet *, const struct sockaddr *); | ||||
static struct in6_msource * | static struct in6_msource * | ||||
im6o_match_source(const struct ip6_moptions *, const size_t, | im6o_match_source(const struct ip6_moptions *, const size_t, | ||||
const struct sockaddr *); | const struct sockaddr *); | ||||
static void im6s_merge(struct ip6_msource *ims, | static void im6s_merge(struct ip6_msource *ims, | ||||
const struct in6_msource *lims, const int rollback); | const struct in6_msource *lims, const int rollback); | ||||
static int in6_mc_get(struct ifnet *, const struct in6_addr *, | static int in6_getmulti(struct ifnet *, const struct in6_addr *, | ||||
struct in6_multi **); | struct in6_multi **); | ||||
static int in6m_get_source(struct in6_multi *inm, | static int in6m_get_source(struct in6_multi *inm, | ||||
const struct in6_addr *addr, const int noalloc, | const struct in6_addr *addr, const int noalloc, | ||||
struct ip6_msource **pims); | struct ip6_msource **pims); | ||||
#ifdef KTR | #ifdef KTR | ||||
static int in6m_is_ifp_detached(const struct in6_multi *); | static int in6m_is_ifp_detached(const struct in6_multi *); | ||||
#endif | #endif | ||||
static int in6m_merge(struct in6_multi *, /*const*/ struct in6_mfilter *); | static int in6m_merge(struct in6_multi *, /*const*/ struct in6_mfilter *); | ||||
▲ Show 20 Lines • Show All 242 Lines • ▼ Show 20 Lines | |||||
* Find and return a reference to an in6_multi record for (ifp, group), | * Find and return a reference to an in6_multi record for (ifp, group), | ||||
* and bump its reference count. | * and bump its reference count. | ||||
* If one does not exist, try to allocate it, and update link-layer multicast | * If one does not exist, try to allocate it, and update link-layer multicast | ||||
* filters on ifp to listen for group. | * filters on ifp to listen for group. | ||||
* Assumes the IN6_MULTI lock is held across the call. | * Assumes the IN6_MULTI lock is held across the call. | ||||
* Return 0 if successful, otherwise return an appropriate error code. | * Return 0 if successful, otherwise return an appropriate error code. | ||||
*/ | */ | ||||
static int | static int | ||||
in6_mc_get(struct ifnet *ifp, const struct in6_addr *group, | in6_getmulti(struct ifnet *ifp, const struct in6_addr *group, | ||||
struct in6_multi **pinm) | struct in6_multi **pinm) | ||||
{ | { | ||||
struct sockaddr_in6 gsin6; | struct sockaddr_in6 gsin6; | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct in6_multi *inm; | struct in6_multi *inm; | ||||
int error; | int error; | ||||
error = 0; | error = 0; | ||||
/* | /* | ||||
* XXX: Accesses to ifma_protospec must be covered by IF_ADDR_LOCK; | * XXX: Accesses to ifma_protospec must be covered by IF_ADDR_LOCK; | ||||
* if_addmulti() takes this mutex itself, so we must drop and | * if_addmulti() takes this mutex itself, so we must drop and | ||||
* re-acquire around the call. | * re-acquire around the call. | ||||
*/ | */ | ||||
IN6_MULTI_LOCK_ASSERT(); | IN6_MULTI_LOCK_ASSERT(); | ||||
IN6_MULTI_LIST_LOCK(); | |||||
IF_ADDR_WLOCK(ifp); | IF_ADDR_WLOCK(ifp); | ||||
inm = in6m_lookup_locked(ifp, group); | inm = in6m_lookup_locked(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->in6m_refcount >= 1, | KASSERT(inm->in6m_refcount >= 1, | ||||
("%s: bad refcount %d", __func__, inm->in6m_refcount)); | ("%s: bad refcount %d", __func__, inm->in6m_refcount)); | ||||
++inm->in6m_refcount; | in6m_acquire_locked(inm); | ||||
*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; | ||||
/* | /* | ||||
* 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. | ||||
*/ | */ | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
IF_ADDR_WUNLOCK(ifp); | IF_ADDR_WUNLOCK(ifp); | ||||
error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma); | error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
IN6_MULTI_LIST_LOCK(); | |||||
IF_ADDR_WLOCK(ifp); | IF_ADDR_WLOCK(ifp); | ||||
/* | /* | ||||
* If something other than netinet6 is occupying the link-layer | * If something other than netinet6 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. | ||||
*/ | */ | ||||
if (ifma->ifma_protospec != NULL) { | if (ifma->ifma_protospec != NULL) { | ||||
inm = (struct in6_multi *)ifma->ifma_protospec; | inm = (struct in6_multi *)ifma->ifma_protospec; | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr", | KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr", | ||||
__func__)); | __func__)); | ||||
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 | ||||
++inm->in6m_refcount; | in6m_acquire_locked(inm); | ||||
*pinm = inm; | *pinm = inm; | ||||
goto out_locked; | goto out_locked; | ||||
} | } | ||||
IF_ADDR_WLOCK_ASSERT(ifp); | IF_ADDR_WLOCK_ASSERT(ifp); | ||||
/* | /* | ||||
* A new in6_multi record is needed; allocate and initialize it. | * A new in6_multi record is needed; allocate and initialize it. | ||||
* We DO NOT perform an MLD join as the in6_ layer may need to | * We DO NOT perform an MLD join as the in6_ layer may need to | ||||
* push an initial source list down to MLD to support SSM. | * push an initial source list down to MLD 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. | ||||
* Pending state-changes per group are subject to a bounds check. | * Pending state-changes per group are subject to a bounds check. | ||||
*/ | */ | ||||
inm = malloc(sizeof(*inm), M_IP6MADDR, M_NOWAIT | M_ZERO); | inm = malloc(sizeof(*inm), M_IP6MADDR, M_NOWAIT | M_ZERO); | ||||
if (inm == NULL) { | if (inm == NULL) { | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
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; | inm->in6m_refcount = 1; | ||||
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; | ||||
*pinm = inm; | *pinm = inm; | ||||
out_locked: | out_locked: | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
IF_ADDR_WUNLOCK(ifp); | IF_ADDR_WUNLOCK(ifp); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Drop a reference to an in6_multi record. | * Drop a reference to an in6_multi record. | ||||
* | * | ||||
* If the refcount drops to 0, free the in6_multi record and | * If the refcount drops to 0, free the in6_multi record and | ||||
* delete the underlying link-layer membership. | * delete the underlying link-layer membership. | ||||
*/ | */ | ||||
void | static void | ||||
in6m_release_locked(struct in6_multi *inm) | in6m_release(struct in6_multi *inm) | ||||
{ | { | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct ifnet *ifp; | |||||
IN6_MULTI_LOCK_ASSERT(); | |||||
CTR2(KTR_MLD, "%s: refcount is %d", __func__, inm->in6m_refcount); | CTR2(KTR_MLD, "%s: refcount is %d", __func__, inm->in6m_refcount); | ||||
if (--inm->in6m_refcount > 0) { | MPASS(inm->in6m_refcount == 0); | ||||
CTR2(KTR_MLD, "%s: refcount is now %d", __func__, | |||||
inm->in6m_refcount); | |||||
return; | |||||
} | |||||
CTR2(KTR_MLD, "%s: freeing inm %p", __func__, inm); | CTR2(KTR_MLD, "%s: freeing inm %p", __func__, inm); | ||||
ifma = inm->in6m_ifma; | ifma = inm->in6m_ifma; | ||||
ifp = inm->in6m_ifp; | |||||
/* XXX this access is not covered by IF_ADDR_LOCK */ | /* XXX this access is not covered by IF_ADDR_LOCK */ | ||||
CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma); | CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma); | ||||
KASSERT(ifma->ifma_protospec == inm, | KASSERT(ifma->ifma_protospec == NULL, | ||||
("%s: ifma_protospec != inm", __func__)); | ("%s: ifma_protospec != NULL", __func__)); | ||||
ifma->ifma_protospec = NULL; | |||||
if (ifp) | |||||
CURVNET_SET(ifp->if_vnet); | |||||
in6m_purge(inm); | in6m_purge(inm); | ||||
free(inm, M_IP6MADDR); | free(inm, M_IP6MADDR); | ||||
if_delmulti_ifma(ifma); | if_delmulti_ifma(ifma); | ||||
if (ifp) | |||||
CURVNET_RESTORE(); | |||||
} | } | ||||
static struct grouptask free_gtask; | |||||
static struct in6_multi_head in6m_free_list; | |||||
static void in6m_release_task(void *arg __unused); | |||||
static void in6m_init(void) | |||||
{ | |||||
SLIST_INIT(&in6m_free_list); | |||||
taskqgroup_config_gtask_init(NULL, &free_gtask, in6m_release_task, "in6m release task"); | |||||
} | |||||
SYSINIT(in6m_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, | |||||
in6m_init, NULL); | |||||
void | |||||
in6m_release_list_deferred(struct in6_multi_head *inmh) | |||||
{ | |||||
if (SLIST_EMPTY(inmh)) | |||||
return; | |||||
mtx_lock(&in6_multi_free_mtx); | |||||
SLIST_CONCAT(&in6m_free_list, inmh, in6_multi, in6m_nrele); | |||||
mtx_unlock(&in6_multi_free_mtx); | |||||
GROUPTASK_ENQUEUE(&free_gtask); | |||||
} | |||||
void | |||||
in6m_release_deferred(struct in6_multi *inm) | |||||
{ | |||||
struct in6_multi_head tmp; | |||||
struct ifnet *ifp; | |||||
struct ifaddr *ifa; | |||||
struct in6_ifaddr *ifa6; | |||||
struct in6_multi_mship *imm; | |||||
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; | |||||
} | |||||
} | |||||
SLIST_INIT(&tmp); | |||||
inm->in6m_ifma->ifma_protospec = NULL; | |||||
SLIST_INSERT_HEAD(&tmp, inm, in6m_nrele); | |||||
in6m_release_list_deferred(&tmp); | |||||
} | |||||
} | |||||
static void | |||||
in6m_release_task(void *arg __unused) | |||||
{ | |||||
struct in6_multi_head in6m_free_tmp; | |||||
struct in6_multi *inm, *tinm; | |||||
SLIST_INIT(&in6m_free_tmp); | |||||
mtx_lock(&in6_multi_free_mtx); | |||||
SLIST_CONCAT(&in6m_free_tmp, &in6m_free_list, in6_multi, in6m_nrele); | |||||
mtx_unlock(&in6_multi_free_mtx); | |||||
IN6_MULTI_LOCK(); | |||||
SLIST_FOREACH_SAFE(inm, &in6m_free_tmp, in6m_nrele, tinm) { | |||||
SLIST_REMOVE_HEAD(&in6m_free_tmp, in6m_nrele); | |||||
in6m_release(inm); | |||||
} | |||||
IN6_MULTI_UNLOCK(); | |||||
} | |||||
/* | /* | ||||
* Clear recorded source entries for a group. | * Clear recorded source entries for a group. | ||||
* Used by the MLD code. Caller must hold the IN6_MULTI lock. | * Used by the MLD code. Caller must hold the IN6_MULTI lock. | ||||
* FIXME: Should reap. | * FIXME: Should reap. | ||||
*/ | */ | ||||
void | void | ||||
in6m_clear_recorded(struct in6_multi *inm) | in6m_clear_recorded(struct in6_multi *inm) | ||||
{ | { | ||||
struct ip6_msource *ims; | struct ip6_msource *ims; | ||||
IN6_MULTI_LOCK_ASSERT(); | IN6_MULTI_LIST_LOCK_ASSERT(); | ||||
RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { | RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { | ||||
if (ims->im6s_stp) { | if (ims->im6s_stp) { | ||||
ims->im6s_stp = 0; | ims->im6s_stp = 0; | ||||
--inm->in6m_st[1].iss_rec; | --inm->in6m_st[1].iss_rec; | ||||
} | } | ||||
} | } | ||||
KASSERT(inm->in6m_st[1].iss_rec == 0, | KASSERT(inm->in6m_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 | ||||
in6m_record_source(struct in6_multi *inm, const struct in6_addr *addr) | in6m_record_source(struct in6_multi *inm, const struct in6_addr *addr) | ||||
{ | { | ||||
struct ip6_msource find; | struct ip6_msource find; | ||||
struct ip6_msource *ims, *nims; | struct ip6_msource *ims, *nims; | ||||
IN6_MULTI_LOCK_ASSERT(); | IN6_MULTI_LIST_LOCK_ASSERT(); | ||||
find.im6s_addr = *addr; | find.im6s_addr = *addr; | ||||
ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); | ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); | ||||
if (ims && ims->im6s_stp) | if (ims && ims->im6s_stp) | ||||
return (0); | return (0); | ||||
if (ims == NULL) { | if (ims == NULL) { | ||||
if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) | if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) | ||||
return (-ENOSPC); | return (-ENOSPC); | ||||
▲ Show 20 Lines • Show All 310 Lines • ▼ Show 20 Lines | in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) | ||||
struct ip6_msource *ims, *nims; | struct ip6_msource *ims, *nims; | ||||
struct in6_msource *lims; | struct in6_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; | ||||
IN6_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 160 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/* | /* | ||||
* Join a multicast address w/o sources. | * Join a multicast address w/o sources. | ||||
* KAME compatibility entry point. | * KAME compatibility entry point. | ||||
* | * | ||||
* SMPng: Assume no mc locks held by caller. | * SMPng: Assume no mc locks held by caller. | ||||
*/ | */ | ||||
struct in6_multi_mship * | |||||
in6_joingroup(struct ifnet *ifp, struct in6_addr *mcaddr, | |||||
int *errorp, int delay) | |||||
{ | |||||
struct in6_multi_mship *imm; | |||||
int error; | |||||
imm = malloc(sizeof(*imm), M_IP6MADDR, M_NOWAIT); | |||||
if (imm == NULL) { | |||||
*errorp = ENOBUFS; | |||||
return (NULL); | |||||
} | |||||
delay = (delay * PR_FASTHZ) / hz; | |||||
error = in6_mc_join(ifp, mcaddr, NULL, &imm->i6mm_maddr, delay); | |||||
if (error) { | |||||
*errorp = error; | |||||
free(imm, M_IP6MADDR); | |||||
return (NULL); | |||||
} | |||||
return (imm); | |||||
} | |||||
/* | |||||
* Leave a multicast address w/o sources. | |||||
* KAME compatibility entry point. | |||||
* | |||||
* SMPng: Assume no mc locks held by caller. | |||||
*/ | |||||
int | int | ||||
in6_leavegroup(struct in6_multi_mship *imm) | in6_joingroup(struct ifnet *ifp, const struct in6_addr *mcaddr, | ||||
{ | |||||
if (imm->i6mm_maddr != NULL) | |||||
in6_mc_leave(imm->i6mm_maddr, NULL); | |||||
free(imm, M_IP6MADDR); | |||||
return 0; | |||||
} | |||||
/* | |||||
* Join a multicast group; unlocked entry point. | |||||
* | |||||
* SMPng: XXX: in6_mc_join() is called from in6_control() when upper | |||||
* locks are not held. Fortunately, ifp is unlikely to have been detached | |||||
* at this point, so we assume it's OK to recurse. | |||||
*/ | |||||
int | |||||
in6_mc_join(struct ifnet *ifp, const struct in6_addr *mcaddr, | |||||
/*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, | /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, | ||||
const int delay) | const int delay) | ||||
{ | { | ||||
int error; | int error; | ||||
IN6_MULTI_LOCK(); | IN6_MULTI_LOCK(); | ||||
error = in6_mc_join_locked(ifp, mcaddr, imf, pinm, delay); | error = in6_joingroup_locked(ifp, mcaddr, NULL, pinm, delay); | ||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Join a multicast group; real entry point. | * Join a multicast group; real entry point. | ||||
* | * | ||||
* Only preserves atomicity at inm level. | * Only preserves atomicity at inm level. | ||||
* NOTE: imf argument cannot be const due to sys/tree.h limitations. | * NOTE: imf argument cannot be const due to sys/tree.h limitations. | ||||
* | * | ||||
* If the MLD downcall fails, the group is not joined, and an error | * If the MLD downcall fails, the group is not joined, and an error | ||||
* code is returned. | * code is returned. | ||||
*/ | */ | ||||
int | int | ||||
in6_mc_join_locked(struct ifnet *ifp, const struct in6_addr *mcaddr, | in6_joingroup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr, | ||||
/*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, | /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, | ||||
const int delay) | const int delay) | ||||
{ | { | ||||
struct in6_mfilter timf; | struct in6_mfilter timf; | ||||
struct in6_multi *inm; | struct in6_multi *inm; | ||||
struct ifmultiaddr *ifma; | |||||
int error; | int error; | ||||
#ifdef KTR | #ifdef KTR | ||||
char ip6tbuf[INET6_ADDRSTRLEN]; | char ip6tbuf[INET6_ADDRSTRLEN]; | ||||
#endif | #endif | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
/* | /* | ||||
* Sanity: Check scope zone ID was set for ifp, if and | * Sanity: Check scope zone ID was set for ifp, if and | ||||
* only if group is scoped to an interface. | * only if group is scoped to an interface. | ||||
*/ | */ | ||||
KASSERT(IN6_IS_ADDR_MULTICAST(mcaddr), | KASSERT(IN6_IS_ADDR_MULTICAST(mcaddr), | ||||
("%s: not a multicast address", __func__)); | ("%s: not a multicast address", __func__)); | ||||
if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr) || | if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr) || | ||||
IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr)) { | IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr)) { | ||||
KASSERT(mcaddr->s6_addr16[1] != 0, | KASSERT(mcaddr->s6_addr16[1] != 0, | ||||
("%s: scope zone ID not set", __func__)); | ("%s: scope zone ID not set", __func__)); | ||||
} | } | ||||
#endif | #endif | ||||
IN6_MULTI_LOCK_ASSERT(); | IN6_MULTI_LOCK_ASSERT(); | ||||
IN6_MULTI_LIST_UNLOCK_ASSERT(); | |||||
CTR4(KTR_MLD, "%s: join %s on %p(%s))", __func__, | CTR4(KTR_MLD, "%s: join %s on %p(%s))", __func__, | ||||
ip6_sprintf(ip6tbuf, mcaddr), ifp, if_name(ifp)); | ip6_sprintf(ip6tbuf, mcaddr), ifp, if_name(ifp)); | ||||
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) { | ||||
im6f_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); | im6f_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); | ||||
imf = &timf; | imf = &timf; | ||||
} | } | ||||
error = in6_getmulti(ifp, mcaddr, &inm); | |||||
error = in6_mc_get(ifp, mcaddr, &inm); | |||||
if (error) { | if (error) { | ||||
CTR1(KTR_MLD, "%s: in6_mc_get() failure", __func__); | CTR1(KTR_MLD, "%s: in6_getmulti() failure", __func__); | ||||
return (error); | return (error); | ||||
} | } | ||||
IN6_MULTI_LIST_LOCK(); | |||||
CTR1(KTR_MLD, "%s: merge inm state", __func__); | CTR1(KTR_MLD, "%s: merge inm state", __func__); | ||||
error = in6m_merge(inm, imf); | error = in6m_merge(inm, imf); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); | CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); | ||||
goto out_in6m_release; | goto out_in6m_release; | ||||
} | } | ||||
CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | ||||
error = mld_change_state(inm, delay); | error = mld_change_state(inm, delay); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_MLD, "%s: failed to update source", __func__); | CTR1(KTR_MLD, "%s: failed to update source", __func__); | ||||
goto out_in6m_release; | goto out_in6m_release; | ||||
} | } | ||||
out_in6m_release: | out_in6m_release: | ||||
if (error) { | if (error) { | ||||
CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); | CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); | ||||
in6m_release_locked(inm); | IF_ADDR_RLOCK(ifp); | ||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | |||||
if (ifma->ifma_protospec == inm) { | |||||
ifma->ifma_protospec = NULL; | |||||
break; | |||||
} | |||||
} | |||||
in6m_release_deferred(inm); | |||||
IF_ADDR_RUNLOCK(ifp); | |||||
} else { | } else { | ||||
*pinm = inm; | *pinm = inm; | ||||
} | } | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Leave a multicast group; unlocked entry point. | * Leave a multicast group; unlocked entry point. | ||||
*/ | */ | ||||
int | int | ||||
in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) | in6_leavegroup(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) | ||||
{ | { | ||||
int error; | int error; | ||||
IN6_MULTI_LOCK(); | IN6_MULTI_LOCK(); | ||||
error = in6_mc_leave_locked(inm, imf); | error = in6_leavegroup_locked(inm, imf); | ||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_UNLOCK(); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Leave a multicast group; real entry point. | * Leave a multicast group; real entry point. | ||||
* All source filters will be expunged. | * All source filters will be expunged. | ||||
* | * | ||||
* Only preserves atomicity at inm level. | * Only preserves atomicity at inm level. | ||||
* | * | ||||
* Holding the write lock for the INP which contains imf | * Holding the write lock for the INP which contains imf | ||||
* is highly advisable. We can't assert for it as imf does not | * is highly advisable. We can't assert for it as imf does not | ||||
* contain a back-pointer to the owning inp. | * contain a back-pointer to the owning inp. | ||||
* | * | ||||
* Note: This is not the same as in6m_release(*) as this function also | * Note: This is not the same as in6m_release(*) as this function also | ||||
* makes a state change downcall into MLD. | * makes a state change downcall into MLD. | ||||
*/ | */ | ||||
int | int | ||||
in6_mc_leave_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) | in6_leavegroup_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) | ||||
{ | { | ||||
struct in6_mfilter timf; | struct in6_mfilter timf; | ||||
struct ifnet *ifp; | |||||
int error; | int error; | ||||
#ifdef KTR | #ifdef KTR | ||||
char ip6tbuf[INET6_ADDRSTRLEN]; | char ip6tbuf[INET6_ADDRSTRLEN]; | ||||
#endif | #endif | ||||
error = 0; | error = 0; | ||||
IN6_MULTI_LOCK_ASSERT(); | IN6_MULTI_LOCK_ASSERT(); | ||||
Show All 14 Lines | #endif | ||||
/* | /* | ||||
* Begin state merge transaction at MLD layer. | * Begin state merge transaction at MLD 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. | ||||
*/ | */ | ||||
ifp = inm->in6m_ifp; | |||||
IN6_MULTI_LIST_LOCK(); | |||||
CTR1(KTR_MLD, "%s: merge inm state", __func__); | CTR1(KTR_MLD, "%s: merge inm state", __func__); | ||||
error = in6m_merge(inm, imf); | error = in6m_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_MLD, "%s: doing mld downcall", __func__); | CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | ||||
error = mld_change_state(inm, 0); | error = mld_change_state(inm, 0); | ||||
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); | ||||
in6m_release_locked(inm); | if (ifp) | ||||
IF_ADDR_RLOCK(ifp); | |||||
in6m_release_deferred(inm); | |||||
if (ifp) | |||||
IF_ADDR_RUNLOCK(ifp); | |||||
IN6_MULTI_LIST_UNLOCK(); | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* 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 MLD downcall will be performed. | * An MLD 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 121 Lines • ▼ Show 20 Lines | #endif | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_MLD, "%s: merge imf state failed", __func__); | CTR1(KTR_MLD, "%s: merge imf state failed", __func__); | ||||
goto out_im6f_rollback; | goto out_im6f_rollback; | ||||
} | } | ||||
/* | /* | ||||
* Begin state merge transaction at MLD layer. | * Begin state merge transaction at MLD layer. | ||||
*/ | */ | ||||
IN6_MULTI_LOCK(); | IN6_MULTI_LIST_LOCK(); | ||||
CTR1(KTR_MLD, "%s: merge inm state", __func__); | CTR1(KTR_MLD, "%s: merge inm state", __func__); | ||||
error = in6m_merge(inm, imf); | error = in6m_merge(inm, imf); | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); | CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); | ||||
else { | else { | ||||
CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | ||||
error = mld_change_state(inm, 0); | error = mld_change_state(inm, 0); | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed mld downcall", __func__); | CTR1(KTR_MLD, "%s: failed mld downcall", __func__); | ||||
} | } | ||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_LIST_UNLOCK(); | ||||
out_im6f_rollback: | out_im6f_rollback: | ||||
if (error) | if (error) | ||||
im6f_rollback(imf); | im6f_rollback(imf); | ||||
else | else | ||||
im6f_commit(imf); | im6f_commit(imf); | ||||
im6f_reap(imf); | im6f_reap(imf); | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | |||||
* SMPng: NOTE: assumes INP write lock is held. | * SMPng: NOTE: assumes INP write lock is held. | ||||
*/ | */ | ||||
void | void | ||||
ip6_freemoptions(struct ip6_moptions *imo) | ip6_freemoptions(struct ip6_moptions *imo) | ||||
{ | { | ||||
struct in6_mfilter *imf; | struct in6_mfilter *imf; | ||||
size_t idx, nmships; | size_t idx, nmships; | ||||
KASSERT(imo != NULL, ("%s: ip6_moptions is NULL", __func__)); | if (imo == NULL) | ||||
return; | |||||
nmships = imo->im6o_num_memberships; | nmships = imo->im6o_num_memberships; | ||||
for (idx = 0; idx < nmships; ++idx) { | for (idx = 0; idx < nmships; ++idx) { | ||||
imf = imo->im6o_mfilters ? &imo->im6o_mfilters[idx] : NULL; | imf = imo->im6o_mfilters ? &imo->im6o_mfilters[idx] : NULL; | ||||
if (imf) | if (imf) | ||||
im6f_leave(imf); | im6f_leave(imf); | ||||
/* XXX this will thrash the lock(s) */ | /* XXX this will thrash the lock(s) */ | ||||
(void)in6_mc_leave(imo->im6o_membership[idx], imf); | (void)in6_leavegroup(imo->im6o_membership[idx], imf); | ||||
if (imf) | if (imf) | ||||
im6f_purge(imf); | im6f_purge(imf); | ||||
} | } | ||||
if (imo->im6o_mfilters) | if (imo->im6o_mfilters) | ||||
free(imo->im6o_mfilters, M_IN6MFILTER); | free(imo->im6o_mfilters, M_IN6MFILTER); | ||||
free(imo->im6o_membership, M_IP6MOPTS); | free(imo->im6o_membership, M_IP6MOPTS); | ||||
free(imo, M_IP6MOPTS); | free(imo, M_IP6MOPTS); | ||||
▲ Show 20 Lines • Show All 474 Lines • ▼ Show 20 Lines | if (is_new) { | ||||
CTR1(KTR_MLD, "%s: new join w/o source", __func__); | CTR1(KTR_MLD, "%s: new join w/o source", __func__); | ||||
im6f_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE); | im6f_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Begin state merge transaction at MLD layer. | * Begin state merge transaction at MLD layer. | ||||
*/ | */ | ||||
in_pcbref(inp); | |||||
INP_WUNLOCK(inp); | |||||
IN6_MULTI_LOCK(); | IN6_MULTI_LOCK(); | ||||
if (is_new) { | if (is_new) { | ||||
error = in6_mc_join_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(); | ||||
goto out_im6o_free; | goto out_im6o_free; | ||||
} | } | ||||
imo->im6o_membership[idx] = inm; | imo->im6o_membership[idx] = inm; | ||||
} else { | } else { | ||||
CTR1(KTR_MLD, "%s: merge inm state", __func__); | CTR1(KTR_MLD, "%s: merge inm state", __func__); | ||||
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 { | ||||
CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | ||||
error = mld_change_state(inm, 0); | error = mld_change_state(inm, 0); | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed mld downcall", | CTR1(KTR_MLD, "%s: failed mld downcall", | ||||
__func__); | __func__); | ||||
} | } | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
} | } | ||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_UNLOCK(); | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK(inp); | ||||
if (in_pcbrele_wlocked(inp)) | |||||
return (ENXIO); | |||||
if (error) { | if (error) { | ||||
im6f_rollback(imf); | im6f_rollback(imf); | ||||
if (is_new) | if (is_new) | ||||
im6f_purge(imf); | im6f_purge(imf); | ||||
else | else | ||||
im6f_reap(imf); | im6f_reap(imf); | ||||
} else { | } else { | ||||
im6f_commit(imf); | im6f_commit(imf); | ||||
▲ Show 20 Lines • Show All 205 Lines • ▼ Show 20 Lines | #endif | ||||
*/ | */ | ||||
IN6_MULTI_LOCK(); | IN6_MULTI_LOCK(); | ||||
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)in6_mc_leave_locked(inm, imf); | (void)in6_leavegroup_locked(inm, imf); | ||||
} else { | } else { | ||||
CTR1(KTR_MLD, "%s: merge inm state", __func__); | CTR1(KTR_MLD, "%s: merge inm state", __func__); | ||||
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 { | ||||
CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | ||||
error = mld_change_state(inm, 0); | error = mld_change_state(inm, 0); | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed mld downcall", | CTR1(KTR_MLD, "%s: failed mld downcall", | ||||
__func__); | __func__); | ||||
} | } | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
} | } | ||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_UNLOCK(); | ||||
if (error) | if (error) | ||||
im6f_rollback(imf); | im6f_rollback(imf); | ||||
else | else | ||||
im6f_commit(imf); | im6f_commit(imf); | ||||
▲ Show 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | if (msfr.msfr_nsrcs > 0) { | ||||
} | } | ||||
free(kss, M_TEMP); | free(kss, M_TEMP); | ||||
} | } | ||||
if (error) | if (error) | ||||
goto out_im6f_rollback; | goto out_im6f_rollback; | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
IN6_MULTI_LOCK(); | IN6_MULTI_LIST_LOCK(); | ||||
/* | /* | ||||
* Begin state merge transaction at MLD layer. | * Begin state merge transaction at MLD layer. | ||||
*/ | */ | ||||
CTR1(KTR_MLD, "%s: merge inm state", __func__); | CTR1(KTR_MLD, "%s: merge inm state", __func__); | ||||
error = in6m_merge(inm, imf); | error = in6m_merge(inm, imf); | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); | CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); | ||||
else { | else { | ||||
CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | CTR1(KTR_MLD, "%s: doing mld downcall", __func__); | ||||
error = mld_change_state(inm, 0); | error = mld_change_state(inm, 0); | ||||
if (error) | if (error) | ||||
CTR1(KTR_MLD, "%s: failed mld downcall", __func__); | CTR1(KTR_MLD, "%s: failed mld downcall", __func__); | ||||
} | } | ||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_LIST_UNLOCK(); | ||||
out_im6f_rollback: | out_im6f_rollback: | ||||
if (error) | if (error) | ||||
im6f_rollback(imf); | im6f_rollback(imf); | ||||
else | else | ||||
im6f_commit(imf); | im6f_commit(imf); | ||||
im6f_reap(imf); | im6f_reap(imf); | ||||
▲ Show 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | #endif | ||||
(void)in6_setscope(&mcaddr, ifp, NULL); | (void)in6_setscope(&mcaddr, ifp, NULL); | ||||
retval = sysctl_wire_old_buffer(req, | retval = sysctl_wire_old_buffer(req, | ||||
sizeof(uint32_t) + (in6_mcast_maxgrpsrc * sizeof(struct in6_addr))); | sizeof(uint32_t) + (in6_mcast_maxgrpsrc * sizeof(struct in6_addr))); | ||||
if (retval) | if (retval) | ||||
return (retval); | return (retval); | ||||
IN6_MULTI_LOCK(); | IN6_MULTI_LOCK(); | ||||
IN6_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_INET6 || | if (ifma->ifma_addr->sa_family != AF_INET6 || | ||||
ifma->ifma_protospec == NULL) | ifma->ifma_protospec == NULL) | ||||
continue; | continue; | ||||
inm = (struct in6_multi *)ifma->ifma_protospec; | inm = (struct in6_multi *)ifma->ifma_protospec; | ||||
if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr)) | if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr)) | ||||
continue; | continue; | ||||
Show All 15 Lines | RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { | ||||
retval = SYSCTL_OUT(req, &src, | retval = SYSCTL_OUT(req, &src, | ||||
sizeof(struct in6_addr)); | sizeof(struct in6_addr)); | ||||
if (retval != 0) | if (retval != 0) | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
IF_ADDR_RUNLOCK(ifp); | IF_ADDR_RUNLOCK(ifp); | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
IN6_MULTI_UNLOCK(); | IN6_MULTI_UNLOCK(); | ||||
return (retval); | return (retval); | ||||
} | } | ||||
#ifdef KTR | #ifdef KTR | ||||
static const char *in6m_modestrs[] = { "un", "in", "ex" }; | static const char *in6m_modestrs[] = { "un", "in", "ex" }; | ||||
▲ Show 20 Lines • Show All 79 Lines • Show Last 20 Lines |