Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/in6_mcast.c
Show First 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | |||||
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, IN6_MULTI_LOCK, INP_WLOCK, | ||||
* IN6_MULTI_LIST_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. | ||||
*/ | */ | ||||
Show All 15 Lines | |||||
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 struct in6_mfilter * | ||||
static size_t im6o_match_group(const struct ip6_moptions *, | 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(struct in6_mfilter *, 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_getmulti(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 | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
im6f_init(struct in6_mfilter *imf, const int st0, const int st1) | im6f_init(struct in6_mfilter *imf, const int st0, const int st1) | ||||
{ | { | ||||
memset(imf, 0, sizeof(struct in6_mfilter)); | memset(imf, 0, sizeof(struct in6_mfilter)); | ||||
RB_INIT(&imf->im6f_sources); | RB_INIT(&imf->im6f_sources); | ||||
imf->im6f_st[0] = st0; | imf->im6f_st[0] = st0; | ||||
imf->im6f_st[1] = st1; | imf->im6f_st[1] = st1; | ||||
} | } | ||||
/* | struct in6_mfilter * | ||||
* Resize the ip6_moptions vector to the next power-of-two minus 1. | ip6_alloc_mfilter(const int mflags, const int st0, const int st1) | ||||
* May be called with locks held; do not sleep. | |||||
*/ | |||||
static int | |||||
im6o_grow(struct ip6_moptions *imo) | |||||
{ | { | ||||
struct in6_multi **nmships; | struct in6_mfilter *imf; | ||||
struct in6_multi **omships; | |||||
struct in6_mfilter *nmfilters; | |||||
struct in6_mfilter *omfilters; | |||||
size_t idx; | |||||
size_t newmax; | |||||
size_t oldmax; | |||||
nmships = NULL; | imf = malloc(sizeof(*imf), M_IN6MFILTER, mflags); | ||||
nmfilters = NULL; | |||||
omships = imo->im6o_membership; | |||||
omfilters = imo->im6o_mfilters; | |||||
oldmax = imo->im6o_max_memberships; | |||||
newmax = ((oldmax + 1) * 2) - 1; | |||||
if (newmax <= IPV6_MAX_MEMBERSHIPS) { | if (imf != NULL) | ||||
nmships = (struct in6_multi **)realloc(omships, | im6f_init(imf, st0, st1); | ||||
sizeof(struct in6_multi *) * newmax, M_IP6MOPTS, M_NOWAIT); | |||||
nmfilters = (struct in6_mfilter *)realloc(omfilters, | |||||
sizeof(struct in6_mfilter) * newmax, M_IN6MFILTER, | |||||
M_NOWAIT); | |||||
if (nmships != NULL && nmfilters != NULL) { | |||||
/* Initialize newly allocated source filter heads. */ | |||||
for (idx = oldmax; idx < newmax; idx++) { | |||||
im6f_init(&nmfilters[idx], MCAST_UNDEFINED, | |||||
MCAST_EXCLUDE); | |||||
} | |||||
imo->im6o_max_memberships = newmax; | |||||
imo->im6o_membership = nmships; | |||||
imo->im6o_mfilters = nmfilters; | |||||
} | |||||
} | |||||
if (nmships == NULL || nmfilters == NULL) { | return (imf); | ||||
if (nmships != NULL) | |||||
free(nmships, M_IP6MOPTS); | |||||
if (nmfilters != NULL) | |||||
free(nmfilters, M_IN6MFILTER); | |||||
return (ETOOMANYREFS); | |||||
} | } | ||||
return (0); | void | ||||
ip6_free_mfilter(struct in6_mfilter *imf) | |||||
{ | |||||
im6f_purge(imf); | |||||
free(imf, M_IN6MFILTER); | |||||
} | } | ||||
/* | /* | ||||
* Find an IPv6 multicast group entry for this ip6_moptions instance | * Find an IPv6 multicast group entry for this ip6_moptions instance | ||||
* which matches the specified group, and optionally an interface. | * which matches the specified group, and optionally an interface. | ||||
* Return its index into the array, or -1 if not found. | * Return its index into the array, or -1 if not found. | ||||
*/ | */ | ||||
static size_t | static struct in6_mfilter * | ||||
im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp, | im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp, | ||||
const struct sockaddr *group) | const struct sockaddr *group) | ||||
{ | { | ||||
const struct sockaddr_in6 *gsin6; | const struct sockaddr_in6 *gsin6; | ||||
struct in6_multi **pinm; | struct in6_mfilter *imf; | ||||
int idx; | struct in6_multi *inm; | ||||
int nmships; | |||||
gsin6 = (const struct sockaddr_in6 *)group; | gsin6 = (const struct sockaddr_in6 *)group; | ||||
/* The im6o_membership array may be lazy allocated. */ | IP6_FOREACH_MFILTER(imf, &imo->im6o_head) { | ||||
if (imo->im6o_membership == NULL || imo->im6o_num_memberships == 0) | inm = imf->im6f_in6m; | ||||
return (-1); | if (inm == NULL) | ||||
nmships = imo->im6o_num_memberships; | |||||
pinm = &imo->im6o_membership[0]; | |||||
for (idx = 0; idx < nmships; idx++, pinm++) { | |||||
if (*pinm == NULL) | |||||
continue; | continue; | ||||
if ((ifp == NULL || ((*pinm)->in6m_ifp == ifp)) && | if ((ifp == NULL || (inm->in6m_ifp == ifp)) && | ||||
IN6_ARE_ADDR_EQUAL(&(*pinm)->in6m_addr, | IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, | ||||
&gsin6->sin6_addr)) { | &gsin6->sin6_addr)) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (idx >= nmships) | return (imf); | ||||
idx = -1; | |||||
return (idx); | |||||
} | } | ||||
/* | /* | ||||
* Find an IPv6 multicast source entry for this imo which matches | * Find an IPv6 multicast source entry for this imo which matches | ||||
* the given group index for this socket, and source address. | * the given group index for this socket, and source address. | ||||
* | * | ||||
* XXX TODO: The scope ID, if present in src, is stripped before | * XXX TODO: The scope ID, if present in src, is stripped before | ||||
* any comparison. We SHOULD enforce scope/zone checks where the source | * any comparison. We SHOULD enforce scope/zone checks where the source | ||||
* filter entry has a link scope. | * filter entry has a link scope. | ||||
* | * | ||||
* NOTE: This does not check if the entry is in-mode, merely if | * NOTE: This does not check if the entry is in-mode, merely if | ||||
* it exists, which may not be the desired behaviour. | * it exists, which may not be the desired behaviour. | ||||
*/ | */ | ||||
static struct in6_msource * | static struct in6_msource * | ||||
im6o_match_source(const struct ip6_moptions *imo, const size_t gidx, | im6o_match_source(struct in6_mfilter *imf, const struct sockaddr *src) | ||||
const struct sockaddr *src) | |||||
{ | { | ||||
struct ip6_msource find; | struct ip6_msource find; | ||||
struct in6_mfilter *imf; | |||||
struct ip6_msource *ims; | struct ip6_msource *ims; | ||||
const sockunion_t *psa; | const sockunion_t *psa; | ||||
KASSERT(src->sa_family == AF_INET6, ("%s: !AF_INET6", __func__)); | KASSERT(src->sa_family == AF_INET6, ("%s: !AF_INET6", __func__)); | ||||
KASSERT(gidx != -1 && gidx < imo->im6o_num_memberships, | |||||
("%s: invalid index %d\n", __func__, (int)gidx)); | |||||
/* The im6o_mfilters array may be lazy allocated. */ | |||||
if (imo->im6o_mfilters == NULL) | |||||
return (NULL); | |||||
imf = &imo->im6o_mfilters[gidx]; | |||||
psa = (const sockunion_t *)src; | psa = (const sockunion_t *)src; | ||||
find.im6s_addr = psa->sin6.sin6_addr; | find.im6s_addr = psa->sin6.sin6_addr; | ||||
in6_clearscope(&find.im6s_addr); /* XXX */ | in6_clearscope(&find.im6s_addr); /* XXX */ | ||||
ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); | ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); | ||||
return ((struct in6_msource *)ims); | return ((struct in6_msource *)ims); | ||||
} | } | ||||
/* | /* | ||||
* Perform filtering for multicast datagrams on a socket by group and source. | * Perform filtering for multicast datagrams on a socket by group and source. | ||||
* | * | ||||
* Returns 0 if a datagram should be allowed through, or various error codes | * Returns 0 if a datagram should be allowed through, or various error codes | ||||
* if the socket was not a member of the group, or the source was muted, etc. | * if the socket was not a member of the group, or the source was muted, etc. | ||||
*/ | */ | ||||
int | int | ||||
im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp, | im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp, | ||||
const struct sockaddr *group, const struct sockaddr *src) | const struct sockaddr *group, const struct sockaddr *src) | ||||
{ | { | ||||
size_t gidx; | struct in6_mfilter *imf; | ||||
struct in6_msource *ims; | struct in6_msource *ims; | ||||
int mode; | int mode; | ||||
KASSERT(ifp != NULL, ("%s: null ifp", __func__)); | KASSERT(ifp != NULL, ("%s: null ifp", __func__)); | ||||
gidx = im6o_match_group(imo, ifp, group); | imf = im6o_match_group(imo, ifp, group); | ||||
if (gidx == -1) | if (imf == NULL) | ||||
return (MCAST_NOTGMEMBER); | return (MCAST_NOTGMEMBER); | ||||
/* | /* | ||||
* Check if the source was included in an (S,G) join. | * Check if the source was included in an (S,G) join. | ||||
* Allow reception on exclusive memberships by default, | * Allow reception on exclusive memberships by default, | ||||
* reject reception on inclusive memberships by default. | * reject reception on inclusive memberships by default. | ||||
* Exclude source only if an in-mode exclude filter exists. | * Exclude source only if an in-mode exclude filter exists. | ||||
* Include source only if an in-mode include filter exists. | * Include source only if an in-mode include filter exists. | ||||
* NOTE: We are comparing group state here at MLD t1 (now) | * NOTE: We are comparing group state here at MLD t1 (now) | ||||
* with socket-layer t0 (since last downcall). | * with socket-layer t0 (since last downcall). | ||||
*/ | */ | ||||
mode = imo->im6o_mfilters[gidx].im6f_st[1]; | mode = imf->im6f_st[1]; | ||||
ims = im6o_match_source(imo, gidx, src); | ims = im6o_match_source(imf, src); | ||||
if ((ims == NULL && mode == MCAST_INCLUDE) || | if ((ims == NULL && mode == MCAST_INCLUDE) || | ||||
(ims != NULL && ims->im6sl_st[0] != mode)) | (ims != NULL && ims->im6sl_st[0] != mode)) | ||||
return (MCAST_NOTSMEMBER); | return (MCAST_NOTSMEMBER); | ||||
return (MCAST_PASS); | return (MCAST_PASS); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,047 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct group_source_req gsr; | struct group_source_req gsr; | ||||
sockunion_t *gsa, *ssa; | sockunion_t *gsa, *ssa; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct in6_mfilter *imf; | struct in6_mfilter *imf; | ||||
struct ip6_moptions *imo; | struct ip6_moptions *imo; | ||||
struct in6_msource *ims; | struct in6_msource *ims; | ||||
struct in6_multi *inm; | struct in6_multi *inm; | ||||
size_t idx; | |||||
uint16_t fmode; | uint16_t fmode; | ||||
int error, doblock; | int error, doblock; | ||||
#ifdef KTR | #ifdef KTR | ||||
char ip6tbuf[INET6_ADDRSTRLEN]; | char ip6tbuf[INET6_ADDRSTRLEN]; | ||||
#endif | #endif | ||||
ifp = NULL; | ifp = NULL; | ||||
error = 0; | error = 0; | ||||
Show All 40 Lines | if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) | ||||
return (EINVAL); | return (EINVAL); | ||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | ||||
/* | /* | ||||
* Check if we are actually a member of this group. | * Check if we are actually a member of this group. | ||||
*/ | */ | ||||
imo = in6p_findmoptions(inp); | imo = in6p_findmoptions(inp); | ||||
idx = im6o_match_group(imo, ifp, &gsa->sa); | imf = im6o_match_group(imo, ifp, &gsa->sa); | ||||
if (idx == -1 || imo->im6o_mfilters == NULL) { | if (imf == NULL) { | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
inm = imf->im6f_in6m; | |||||
KASSERT(imo->im6o_mfilters != NULL, | |||||
("%s: im6o_mfilters not allocated", __func__)); | |||||
imf = &imo->im6o_mfilters[idx]; | |||||
inm = imo->im6o_membership[idx]; | |||||
/* | /* | ||||
* Attempting to use the delta-based API on an | * Attempting to use the delta-based API on an | ||||
* non exclusive-mode membership is an error. | * non exclusive-mode membership is an error. | ||||
*/ | */ | ||||
fmode = imf->im6f_st[0]; | fmode = imf->im6f_st[0]; | ||||
if (fmode != MCAST_EXCLUDE) { | if (fmode != MCAST_EXCLUDE) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
/* | /* | ||||
* Deal with error cases up-front: | * Deal with error cases up-front: | ||||
* Asked to block, but already blocked; or | * Asked to block, but already blocked; or | ||||
* Asked to unblock, but nothing to unblock. | * Asked to unblock, but nothing to unblock. | ||||
* If adding a new block entry, allocate it. | * If adding a new block entry, allocate it. | ||||
*/ | */ | ||||
ims = im6o_match_source(imo, idx, &ssa->sa); | ims = im6o_match_source(imf, &ssa->sa); | ||||
if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { | if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { | ||||
CTR3(KTR_MLD, "%s: source %s %spresent", __func__, | CTR3(KTR_MLD, "%s: source %s %spresent", __func__, | ||||
ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), | ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), | ||||
doblock ? "" : "not "); | doblock ? "" : "not "); | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
* | * | ||||
* SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. | * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. | ||||
* SMPng: NOTE: Returns with the INP write lock held. | * SMPng: NOTE: Returns with the INP write lock held. | ||||
*/ | */ | ||||
static struct ip6_moptions * | static struct ip6_moptions * | ||||
in6p_findmoptions(struct inpcb *inp) | in6p_findmoptions(struct inpcb *inp) | ||||
{ | { | ||||
struct ip6_moptions *imo; | struct ip6_moptions *imo; | ||||
struct in6_multi **immp; | |||||
struct in6_mfilter *imfp; | |||||
size_t idx; | |||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (inp->in6p_moptions != NULL) | if (inp->in6p_moptions != NULL) | ||||
return (inp->in6p_moptions); | return (inp->in6p_moptions); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
imo = malloc(sizeof(*imo), M_IP6MOPTS, M_WAITOK); | imo = malloc(sizeof(*imo), M_IP6MOPTS, M_WAITOK); | ||||
immp = malloc(sizeof(*immp) * IPV6_MIN_MEMBERSHIPS, M_IP6MOPTS, | |||||
M_WAITOK | M_ZERO); | |||||
imfp = malloc(sizeof(struct in6_mfilter) * IPV6_MIN_MEMBERSHIPS, | |||||
M_IN6MFILTER, M_WAITOK); | |||||
imo->im6o_multicast_ifp = NULL; | imo->im6o_multicast_ifp = NULL; | ||||
imo->im6o_multicast_hlim = V_ip6_defmcasthlim; | imo->im6o_multicast_hlim = V_ip6_defmcasthlim; | ||||
imo->im6o_multicast_loop = in6_mcast_loop; | imo->im6o_multicast_loop = in6_mcast_loop; | ||||
imo->im6o_num_memberships = 0; | STAILQ_INIT(&imo->im6o_head); | ||||
imo->im6o_max_memberships = IPV6_MIN_MEMBERSHIPS; | |||||
imo->im6o_membership = immp; | |||||
/* Initialize per-group source filters. */ | |||||
for (idx = 0; idx < IPV6_MIN_MEMBERSHIPS; idx++) | |||||
im6f_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE); | |||||
imo->im6o_mfilters = imfp; | |||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
if (inp->in6p_moptions != NULL) { | if (inp->in6p_moptions != NULL) { | ||||
free(imfp, M_IN6MFILTER); | |||||
free(immp, M_IP6MOPTS); | |||||
free(imo, M_IP6MOPTS); | free(imo, M_IP6MOPTS); | ||||
return (inp->in6p_moptions); | return (inp->in6p_moptions); | ||||
} | } | ||||
inp->in6p_moptions = imo; | inp->in6p_moptions = imo; | ||||
return (imo); | return (imo); | ||||
} | } | ||||
/* | /* | ||||
* Discard the IPv6 multicast options (and source filters). | * Discard the IPv6 multicast options (and source filters). | ||||
* | * | ||||
* SMPng: NOTE: assumes INP write lock is held. | * SMPng: NOTE: assumes INP write lock is held. | ||||
* | * | ||||
* XXX can all be safely deferred to epoch_call | * XXX can all be safely deferred to epoch_call | ||||
* | * | ||||
*/ | */ | ||||
static void | static void | ||||
inp_gcmoptions(struct ip6_moptions *imo) | inp_gcmoptions(struct ip6_moptions *imo) | ||||
{ | { | ||||
struct in6_mfilter *imf; | struct in6_mfilter *imf; | ||||
struct in6_multi *inm; | struct in6_multi *inm; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
size_t idx, nmships; | |||||
nmships = imo->im6o_num_memberships; | while ((imf = ip6_first_mfilter(&imo->im6o_head)) != NULL) { | ||||
for (idx = 0; idx < nmships; ++idx) { | ip6_remove_mfilter(&imo->im6o_head, imf); | ||||
imf = imo->im6o_mfilters ? &imo->im6o_mfilters[idx] : NULL; | |||||
if (imf) | |||||
im6f_leave(imf); | im6f_leave(imf); | ||||
inm = imo->im6o_membership[idx]; | if ((inm = imf->im6f_in6m) != NULL) { | ||||
ifp = inm->in6m_ifp; | if ((ifp = inm->in6m_ifp) != NULL) { | ||||
if (ifp != NULL) { | |||||
CURVNET_SET(ifp->if_vnet); | CURVNET_SET(ifp->if_vnet); | ||||
(void)in6_leavegroup(inm, imf); | (void)in6_leavegroup(inm, imf); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} else { | } else { | ||||
(void)in6_leavegroup(inm, imf); | (void)in6_leavegroup(inm, imf); | ||||
} | } | ||||
if (imf) | |||||
im6f_purge(imf); | |||||
} | } | ||||
ip6_free_mfilter(imf); | |||||
if (imo->im6o_mfilters) | } | ||||
free(imo->im6o_mfilters, M_IN6MFILTER); | |||||
free(imo->im6o_membership, M_IP6MOPTS); | |||||
free(imo, M_IP6MOPTS); | free(imo, M_IP6MOPTS); | ||||
} | } | ||||
void | void | ||||
ip6_freemoptions(struct ip6_moptions *imo) | ip6_freemoptions(struct ip6_moptions *imo) | ||||
{ | { | ||||
if (imo == NULL) | if (imo == NULL) | ||||
return; | return; | ||||
inp_gcmoptions(imo); | inp_gcmoptions(imo); | ||||
Show All 12 Lines | in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt) | ||||
struct ip6_moptions *imo; | struct ip6_moptions *imo; | ||||
struct in6_mfilter *imf; | struct in6_mfilter *imf; | ||||
struct ip6_msource *ims; | struct ip6_msource *ims; | ||||
struct in6_msource *lims; | struct in6_msource *lims; | ||||
struct sockaddr_in6 *psin; | struct sockaddr_in6 *psin; | ||||
struct sockaddr_storage *ptss; | struct sockaddr_storage *ptss; | ||||
struct sockaddr_storage *tss; | struct sockaddr_storage *tss; | ||||
int error; | int error; | ||||
size_t idx, nsrcs, ncsrcs; | size_t nsrcs, ncsrcs; | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
imo = inp->in6p_moptions; | imo = inp->in6p_moptions; | ||||
KASSERT(imo != NULL, ("%s: null ip6_moptions", __func__)); | KASSERT(imo != NULL, ("%s: null ip6_moptions", __func__)); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
Show All 17 Lines | if (ifp == NULL) | ||||
return (EADDRNOTAVAIL); | return (EADDRNOTAVAIL); | ||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | ||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
/* | /* | ||||
* Lookup group on the socket. | * Lookup group on the socket. | ||||
*/ | */ | ||||
idx = im6o_match_group(imo, ifp, &gsa->sa); | imf = im6o_match_group(imo, ifp, &gsa->sa); | ||||
if (idx == -1 || imo->im6o_mfilters == NULL) { | if (imf == NULL) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (EADDRNOTAVAIL); | return (EADDRNOTAVAIL); | ||||
} | } | ||||
imf = &imo->im6o_mfilters[idx]; | |||||
/* | /* | ||||
* Ignore memberships which are in limbo. | * Ignore memberships which are in limbo. | ||||
*/ | */ | ||||
if (imf->im6f_st[1] == MCAST_UNDEFINED) { | if (imf->im6f_st[1] == MCAST_UNDEFINED) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (EAGAIN); | return (EAGAIN); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | in6p_join_group(struct inpcb *inp, struct sockopt *sopt) | ||||
struct in6_multi_head inmh; | struct in6_multi_head inmh; | ||||
struct group_source_req gsr; | struct group_source_req gsr; | ||||
sockunion_t *gsa, *ssa; | sockunion_t *gsa, *ssa; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct in6_mfilter *imf; | struct in6_mfilter *imf; | ||||
struct ip6_moptions *imo; | struct ip6_moptions *imo; | ||||
struct in6_multi *inm; | struct in6_multi *inm; | ||||
struct in6_msource *lims; | struct in6_msource *lims; | ||||
size_t idx; | |||||
int error, is_new; | int error, is_new; | ||||
SLIST_INIT(&inmh); | SLIST_INIT(&inmh); | ||||
ifp = NULL; | ifp = NULL; | ||||
imf = NULL; | |||||
lims = NULL; | lims = NULL; | ||||
error = 0; | error = 0; | ||||
is_new = 0; | |||||
memset(&gsr, 0, sizeof(struct group_source_req)); | memset(&gsr, 0, sizeof(struct group_source_req)); | ||||
gsa = (sockunion_t *)&gsr.gsr_group; | gsa = (sockunion_t *)&gsr.gsr_group; | ||||
gsa->ss.ss_family = AF_UNSPEC; | gsa->ss.ss_family = AF_UNSPEC; | ||||
ssa = (sockunion_t *)&gsr.gsr_source; | ssa = (sockunion_t *)&gsr.gsr_source; | ||||
ssa->ss.ss_family = AF_UNSPEC; | ssa->ss.ss_family = AF_UNSPEC; | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | in6p_join_group(struct inpcb *inp, struct sockopt *sopt) | ||||
/* | /* | ||||
* Always set the scope zone ID on memberships created from userland. | * Always set the scope zone ID on memberships created from userland. | ||||
* Use the passed-in ifp to do this. | * Use the passed-in ifp to do this. | ||||
* XXX The in6_setscope() return value is meaningless. | * XXX The in6_setscope() return value is meaningless. | ||||
* XXX SCOPE6_LOCK() is taken by in6_setscope(). | * XXX SCOPE6_LOCK() is taken by in6_setscope(). | ||||
*/ | */ | ||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | ||||
IN6_MULTI_LOCK(); | |||||
/* | |||||
* Find the membership in the membership list. | |||||
*/ | |||||
imo = in6p_findmoptions(inp); | imo = in6p_findmoptions(inp); | ||||
idx = im6o_match_group(imo, ifp, &gsa->sa); | imf = im6o_match_group(imo, ifp, &gsa->sa); | ||||
if (idx == -1) { | if (imf == NULL) { | ||||
is_new = 1; | is_new = 1; | ||||
inm = NULL; | |||||
if (ip6_count_mfilter(&imo->im6o_head) >= IPV6_MAX_MEMBERSHIPS) { | |||||
error = ENOMEM; | |||||
goto out_in6p_locked; | |||||
} | |||||
} else { | } else { | ||||
inm = imo->im6o_membership[idx]; | is_new = 0; | ||||
imf = &imo->im6o_mfilters[idx]; | inm = imf->im6f_in6m; | ||||
if (ssa->ss.ss_family != AF_UNSPEC) { | if (ssa->ss.ss_family != AF_UNSPEC) { | ||||
/* | /* | ||||
* MCAST_JOIN_SOURCE_GROUP on an exclusive membership | * MCAST_JOIN_SOURCE_GROUP on an exclusive membership | ||||
* is an error. On an existing inclusive membership, | * is an error. On an existing inclusive membership, | ||||
* it just adds the source to the filter list. | * it just adds the source to the filter list. | ||||
*/ | */ | ||||
if (imf->im6f_st[1] != MCAST_INCLUDE) { | if (imf->im6f_st[1] != MCAST_INCLUDE) { | ||||
error = EINVAL; | error = EINVAL; | ||||
Show All 10 Lines | if (ssa->ss.ss_family != AF_UNSPEC) { | ||||
* in6_msource is transactioned just as for anything | * in6_msource is transactioned just as for anything | ||||
* else in SSM -- but note naive use of in6m_graft() | * else in SSM -- but note naive use of in6m_graft() | ||||
* below for allocating new filter entries. | * below for allocating new filter entries. | ||||
* | * | ||||
* This is only an issue if someone mixes the | * This is only an issue if someone mixes the | ||||
* full-state SSM API with the delta-based API, | * full-state SSM API with the delta-based API, | ||||
* which is discouraged in the relevant RFCs. | * which is discouraged in the relevant RFCs. | ||||
*/ | */ | ||||
lims = im6o_match_source(imo, idx, &ssa->sa); | lims = im6o_match_source(imf, &ssa->sa); | ||||
if (lims != NULL /*&& | if (lims != NULL /*&& | ||||
lims->im6sl_st[1] == MCAST_INCLUDE*/) { | lims->im6sl_st[1] == MCAST_INCLUDE*/) { | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
} else { | } else { | ||||
/* | /* | ||||
* MCAST_JOIN_GROUP alone, on any existing membership, | * MCAST_JOIN_GROUP alone, on any existing membership, | ||||
Show All 11 Lines | if (imf == NULL) { | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Begin state merge transaction at socket layer. | * Begin state merge transaction at socket layer. | ||||
*/ | */ | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
if (is_new) { | |||||
if (imo->im6o_num_memberships == imo->im6o_max_memberships) { | |||||
error = im6o_grow(imo); | |||||
if (error) | |||||
goto out_in6p_locked; | |||||
} | |||||
/* | /* | ||||
* Allocate the new slot upfront so we can deal with | |||||
* grafting the new source filter in same code path | |||||
* as for join-source on existing membership. | |||||
*/ | |||||
idx = imo->im6o_num_memberships; | |||||
imo->im6o_membership[idx] = NULL; | |||||
imo->im6o_num_memberships++; | |||||
KASSERT(imo->im6o_mfilters != NULL, | |||||
("%s: im6f_mfilters vector was not allocated", __func__)); | |||||
imf = &imo->im6o_mfilters[idx]; | |||||
KASSERT(RB_EMPTY(&imf->im6f_sources), | |||||
("%s: im6f_sources not empty", __func__)); | |||||
} | |||||
/* | |||||
* Graft new source into filter list for this inpcb's | * Graft new source into filter list for this inpcb's | ||||
* membership of the group. The in6_multi may not have | * membership of the group. The in6_multi may not have | ||||
* been allocated yet if this is a new membership, however, | * been allocated yet if this is a new membership, however, | ||||
* the in_mfilter slot will be allocated and must be initialized. | * the in_mfilter slot will be allocated and must be initialized. | ||||
* | * | ||||
* Note: Grafting of exclusive mode filters doesn't happen | * Note: Grafting of exclusive mode filters doesn't happen | ||||
* in this path. | * in this path. | ||||
* XXX: Should check for non-NULL lims (node exists but may | * XXX: Should check for non-NULL lims (node exists but may | ||||
* not be in-mode) for interop with full-state API. | * not be in-mode) for interop with full-state API. | ||||
*/ | */ | ||||
if (ssa->ss.ss_family != AF_UNSPEC) { | if (ssa->ss.ss_family != AF_UNSPEC) { | ||||
/* Membership starts in IN mode */ | /* Membership starts in IN mode */ | ||||
if (is_new) { | if (is_new) { | ||||
CTR1(KTR_MLD, "%s: new join w/source", __func__); | CTR1(KTR_MLD, "%s: new join w/source", __func__); | ||||
im6f_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE); | imf = ip6_alloc_mfilter(M_NOWAIT, MCAST_UNDEFINED, MCAST_INCLUDE); | ||||
if (imf == NULL) { | |||||
error = ENOMEM; | |||||
goto out_in6p_locked; | |||||
} | |||||
} else { | } else { | ||||
CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); | CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); | ||||
} | } | ||||
lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6); | lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6); | ||||
if (lims == NULL) { | if (lims == NULL) { | ||||
CTR1(KTR_MLD, "%s: merge imf state failed", | CTR1(KTR_MLD, "%s: merge imf state failed", | ||||
__func__); | __func__); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto out_im6o_free; | goto out_in6p_locked; | ||||
} | } | ||||
} else { | } else { | ||||
/* No address specified; Membership starts in EX mode */ | /* No address specified; Membership starts in EX mode */ | ||||
if (is_new) { | 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); | imf = ip6_alloc_mfilter(M_NOWAIT, MCAST_UNDEFINED, MCAST_EXCLUDE); | ||||
if (imf == NULL) { | |||||
error = ENOMEM; | |||||
goto out_in6p_locked; | |||||
} | } | ||||
} | } | ||||
} | |||||
/* | /* | ||||
* Begin state merge transaction at MLD layer. | * Begin state merge transaction at MLD layer. | ||||
*/ | */ | ||||
if (is_new) { | |||||
in_pcbref(inp); | in_pcbref(inp); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
IN6_MULTI_LOCK(); | |||||
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); | &imf->im6f_in6m, 0); | ||||
INP_WLOCK(inp); | |||||
if (in_pcbrele_wlocked(inp)) { | |||||
error = ENXIO; | |||||
goto out_in6p_unlocked; | |||||
} | |||||
if (error) { | if (error) { | ||||
IN6_MULTI_UNLOCK(); | goto out_in6p_locked; | ||||
goto out_im6o_free; | |||||
} | } | ||||
/* | /* | ||||
* NOTE: Refcount from in6_joingroup_locked() | * NOTE: Refcount from in6_joingroup_locked() | ||||
* is protecting membership. | * is protecting membership. | ||||
*/ | */ | ||||
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(); | 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 { | IN6_MULTI_LIST_UNLOCK(); | ||||
im6f_rollback(imf); | |||||
im6f_reap(imf); | |||||
goto out_in6p_locked; | |||||
} | |||||
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) | |||||
CTR1(KTR_MLD, "%s: failed mld downcall", | |||||
__func__); | |||||
} | |||||
IN6_MULTI_LIST_UNLOCK(); | IN6_MULTI_LIST_UNLOCK(); | ||||
} | |||||
IN6_MULTI_UNLOCK(); | |||||
INP_WLOCK(inp); | |||||
if (in_pcbrele_wlocked(inp)) | |||||
return (ENXIO); | |||||
if (error) { | if (error) { | ||||
CTR1(KTR_MLD, "%s: failed mld downcall", | |||||
__func__); | |||||
im6f_rollback(imf); | im6f_rollback(imf); | ||||
if (is_new) | |||||
im6f_purge(imf); | |||||
else | |||||
im6f_reap(imf); | im6f_reap(imf); | ||||
} else { | goto out_in6p_locked; | ||||
im6f_commit(imf); | |||||
} | } | ||||
out_im6o_free: | |||||
if (error && is_new) { | |||||
inm = imo->im6o_membership[idx]; | |||||
if (inm != NULL) { | |||||
IN6_MULTI_LIST_LOCK(); | |||||
in6m_rele_locked(&inmh, inm); | |||||
IN6_MULTI_LIST_UNLOCK(); | |||||
} | } | ||||
imo->im6o_membership[idx] = NULL; | |||||
--imo->im6o_num_memberships; | |||||
} | |||||
if (is_new) | |||||
ip6_insert_mfilter(&imo->im6o_head, imf); | |||||
im6f_commit(imf); | |||||
imf = NULL; | |||||
out_in6p_locked: | out_in6p_locked: | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
out_in6p_unlocked: | |||||
IN6_MULTI_UNLOCK(); | |||||
if (is_new && imf) { | |||||
if (imf->im6f_in6m != NULL) { | |||||
struct in6_multi_head inmh; | |||||
SLIST_INIT(&inmh); | |||||
SLIST_INSERT_HEAD(&inmh, imf->im6f_in6m, in6m_defer); | |||||
in6m_release_list_deferred(&inmh); | in6m_release_list_deferred(&inmh); | ||||
} | |||||
ip6_free_mfilter(imf); | |||||
} | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Leave an IPv6 multicast group on an inpcb, possibly with a source. | * Leave an IPv6 multicast group on an inpcb, possibly with a source. | ||||
*/ | */ | ||||
static int | static int | ||||
in6p_leave_group(struct inpcb *inp, struct sockopt *sopt) | in6p_leave_group(struct inpcb *inp, struct sockopt *sopt) | ||||
{ | { | ||||
struct ipv6_mreq mreq; | struct ipv6_mreq mreq; | ||||
struct group_source_req gsr; | struct group_source_req gsr; | ||||
sockunion_t *gsa, *ssa; | sockunion_t *gsa, *ssa; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct in6_mfilter *imf; | struct in6_mfilter *imf; | ||||
struct ip6_moptions *imo; | struct ip6_moptions *imo; | ||||
struct in6_msource *ims; | struct in6_msource *ims; | ||||
struct in6_multi *inm; | struct in6_multi *inm; | ||||
uint32_t ifindex; | uint32_t ifindex; | ||||
size_t idx; | int error; | ||||
int error, is_final; | bool is_final; | ||||
#ifdef KTR | #ifdef KTR | ||||
char ip6tbuf[INET6_ADDRSTRLEN]; | char ip6tbuf[INET6_ADDRSTRLEN]; | ||||
#endif | #endif | ||||
ifp = NULL; | ifp = NULL; | ||||
ifindex = 0; | ifindex = 0; | ||||
error = 0; | error = 0; | ||||
is_final = 1; | is_final = true; | ||||
memset(&gsr, 0, sizeof(struct group_source_req)); | memset(&gsr, 0, sizeof(struct group_source_req)); | ||||
gsa = (sockunion_t *)&gsr.gsr_group; | gsa = (sockunion_t *)&gsr.gsr_group; | ||||
gsa->ss.ss_family = AF_UNSPEC; | gsa->ss.ss_family = AF_UNSPEC; | ||||
ssa = (sockunion_t *)&gsr.gsr_source; | ssa = (sockunion_t *)&gsr.gsr_source; | ||||
ssa->ss.ss_family = AF_UNSPEC; | ssa->ss.ss_family = AF_UNSPEC; | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | if (ifindex != 0) { | ||||
} | } | ||||
if (ifp == NULL) | if (ifp == NULL) | ||||
return (EADDRNOTAVAIL); | return (EADDRNOTAVAIL); | ||||
} | } | ||||
CTR2(KTR_MLD, "%s: ifp = %p", __func__, ifp); | CTR2(KTR_MLD, "%s: ifp = %p", __func__, ifp); | ||||
KASSERT(ifp != NULL, ("%s: ifp did not resolve", __func__)); | KASSERT(ifp != NULL, ("%s: ifp did not resolve", __func__)); | ||||
IN6_MULTI_LOCK(); | |||||
markj: Is this supposed to be IN6_MULTI_LOCK()? Note that we lock it further below. | |||||
Done Inline ActionsYes, you're right. I'll update the patch tomorrow. hselasky: Yes, you're right. I'll update the patch tomorrow. | |||||
/* | /* | ||||
* Find the membership in the membership array. | * Find the membership in the membership list. | ||||
*/ | */ | ||||
imo = in6p_findmoptions(inp); | imo = in6p_findmoptions(inp); | ||||
idx = im6o_match_group(imo, ifp, &gsa->sa); | imf = im6o_match_group(imo, ifp, &gsa->sa); | ||||
if (idx == -1) { | if (imf == NULL) { | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
inm = imo->im6o_membership[idx]; | inm = imf->im6f_in6m; | ||||
imf = &imo->im6o_mfilters[idx]; | |||||
if (ssa->ss.ss_family != AF_UNSPEC) | if (ssa->ss.ss_family != AF_UNSPEC) | ||||
is_final = 0; | is_final = false; | ||||
/* | /* | ||||
* Begin state merge transaction at socket layer. | * Begin state merge transaction at socket layer. | ||||
*/ | */ | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
/* | /* | ||||
* If we were instructed only to leave a given source, do so. | * If we were instructed only to leave a given source, do so. | ||||
* MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships. | * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships. | ||||
*/ | */ | ||||
if (is_final) { | if (is_final) { | ||||
ip6_remove_mfilter(&imo->im6o_head, imf); | |||||
im6f_leave(imf); | im6f_leave(imf); | ||||
} else { | } else { | ||||
if (imf->im6f_st[0] == MCAST_EXCLUDE) { | if (imf->im6f_st[0] == MCAST_EXCLUDE) { | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
ims = im6o_match_source(imo, idx, &ssa->sa); | ims = im6o_match_source(imf, &ssa->sa); | ||||
if (ims == NULL) { | if (ims == NULL) { | ||||
CTR3(KTR_MLD, "%s: source %p %spresent", __func__, | CTR3(KTR_MLD, "%s: source %p %spresent", __func__, | ||||
ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), | ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), | ||||
"not "); | "not "); | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
CTR2(KTR_MLD, "%s: %s source", __func__, "block"); | CTR2(KTR_MLD, "%s: %s source", __func__, "block"); | ||||
error = im6f_prune(imf, &ssa->sin6); | error = im6f_prune(imf, &ssa->sin6); | ||||
if (error) { | if (error) { | ||||
CTR1(KTR_MLD, "%s: merge imf state failed", | CTR1(KTR_MLD, "%s: merge imf state failed", | ||||
__func__); | __func__); | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Begin state merge transaction at MLD layer. | * Begin state merge transaction at MLD layer. | ||||
*/ | */ | ||||
in_pcbref(inp); | if (!is_final) { | ||||
INP_WUNLOCK(inp); | |||||
IN6_MULTI_LOCK(); | |||||
if (is_final) { | |||||
/* | |||||
* Give up the multicast address record to which | |||||
* the membership points. | |||||
*/ | |||||
(void)in6_leavegroup_locked(inm, imf); | |||||
} 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 { | IN6_MULTI_LIST_UNLOCK(); | ||||
im6f_rollback(imf); | |||||
im6f_reap(imf); | |||||
goto out_in6p_locked; | |||||
} | |||||
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) | IN6_MULTI_LIST_UNLOCK(); | ||||
if (error) { | |||||
CTR1(KTR_MLD, "%s: failed mld downcall", | CTR1(KTR_MLD, "%s: failed mld downcall", | ||||
__func__); | __func__); | ||||
im6f_rollback(imf); | |||||
im6f_reap(imf); | |||||
goto out_in6p_locked; | |||||
} | } | ||||
IN6_MULTI_LIST_UNLOCK(); | |||||
} | } | ||||
IN6_MULTI_UNLOCK(); | |||||
INP_WLOCK(inp); | |||||
if (in_pcbrele_wlocked(inp)) | |||||
return (ENXIO); | |||||
if (error) | |||||
im6f_rollback(imf); | |||||
else | |||||
im6f_commit(imf); | im6f_commit(imf); | ||||
im6f_reap(imf); | im6f_reap(imf); | ||||
if (is_final) { | |||||
/* Remove the gap in the membership array. */ | |||||
KASSERT(RB_EMPTY(&imf->im6f_sources), | |||||
("%s: im6f_sources not empty", __func__)); | |||||
for (++idx; idx < imo->im6o_num_memberships; ++idx) { | |||||
imo->im6o_membership[idx - 1] = imo->im6o_membership[idx]; | |||||
imo->im6o_mfilters[idx - 1] = imo->im6o_mfilters[idx]; | |||||
} | |||||
im6f_init(&imo->im6o_mfilters[idx - 1], MCAST_UNDEFINED, | |||||
MCAST_EXCLUDE); | |||||
imo->im6o_num_memberships--; | |||||
} | |||||
out_in6p_locked: | out_in6p_locked: | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
if (is_final && imf) { | |||||
/* | |||||
* Give up the multicast address record to which | |||||
* the membership points. | |||||
*/ | |||||
(void)in6_leavegroup_locked(inm, imf); | |||||
ip6_free_mfilter(imf); | |||||
} | |||||
IN6_MULTI_UNLOCK(); | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Select the interface for transmitting IPv6 multicast datagrams. | * Select the interface for transmitting IPv6 multicast datagrams. | ||||
* | * | ||||
* Either an instance of struct in6_addr or an instance of struct ipv6_mreqn | * Either an instance of struct in6_addr or an instance of struct ipv6_mreqn | ||||
* may be passed to this socket option. An address of in6addr_any or an | * may be passed to this socket option. An address of in6addr_any or an | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) | in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) | ||||
{ | { | ||||
struct __msfilterreq msfr; | struct __msfilterreq msfr; | ||||
sockunion_t *gsa; | sockunion_t *gsa; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct in6_mfilter *imf; | struct in6_mfilter *imf; | ||||
struct ip6_moptions *imo; | struct ip6_moptions *imo; | ||||
struct in6_multi *inm; | struct in6_multi *inm; | ||||
size_t idx; | |||||
int error; | int error; | ||||
error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), | error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), | ||||
sizeof(struct __msfilterreq)); | sizeof(struct __msfilterreq)); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) | if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) | ||||
Show All 20 Lines | if (ifp == NULL) | ||||
return (EADDRNOTAVAIL); | return (EADDRNOTAVAIL); | ||||
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); | ||||
/* | /* | ||||
* Take the INP write lock. | * Take the INP write lock. | ||||
* Check if this socket is a member of this group. | * Check if this socket is a member of this group. | ||||
*/ | */ | ||||
imo = in6p_findmoptions(inp); | imo = in6p_findmoptions(inp); | ||||
idx = im6o_match_group(imo, ifp, &gsa->sa); | imf = im6o_match_group(imo, ifp, &gsa->sa); | ||||
if (idx == -1 || imo->im6o_mfilters == NULL) { | if (imf == NULL) { | ||||
error = EADDRNOTAVAIL; | error = EADDRNOTAVAIL; | ||||
goto out_in6p_locked; | goto out_in6p_locked; | ||||
} | } | ||||
inm = imo->im6o_membership[idx]; | inm = imf->im6f_in6m; | ||||
imf = &imo->im6o_mfilters[idx]; | |||||
/* | /* | ||||
* Begin state merge transaction at socket layer. | * Begin state merge transaction at socket layer. | ||||
*/ | */ | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
imf->im6f_st[1] = msfr.msfr_fmode; | imf->im6f_st[1] = msfr.msfr_fmode; | ||||
▲ Show 20 Lines • Show All 405 Lines • Show Last 20 Lines |
Is this supposed to be IN6_MULTI_LOCK()? Note that we lock it further below.