FreeBSD/sys/netinet6/in6_mcast.c
/*
 * Block or unblock an ASM multicast source on an inpcb.
 * This implements the delta-based API described in RFC 3678.
 *
 * The delta-based API applies only to exclusive-mode memberships.
 * An MLD downcall will be performed.
 *
 * SMPng: NOTE: Must take Giant as a join may create a new ifma.
 *
 * Return 0 if successful, otherwise return an appropriate error code.
 */
Block or unblock an ASM multicast source on an inpcb.
This implements the delta-based API described in RFC 3678.

The delta-based API applies only to exclusive-mode memberships.
An MLD downcall will be performed.

Must take Giant as a join may create a new ifma.

Return 0 if successful, otherwise return an appropriate error code.
Block or unblock an ASM multicast source on an inpcb . This implements the delta - based API described in RFC 3678 . The delta - based API applies only to exclusive - mode memberships . An MLD downcall will be performed . Must take Giant as a join may create a new ifma . Return 0 if successful otherwise return an appropriate error code .
static int
in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
{
	struct group_source_req		 gsr;
	sockunion_t			*gsa, *ssa;
	struct ifnet			*ifp;
	struct in6_mfilter		*imf;
	struct ip6_moptions		*imo;
	struct in6_msource		*ims;
	struct in6_multi			*inm;
	size_t				 idx;
	uint16_t			 fmode;
	int				 error, doblock;
#ifdef KTR
	char				 ip6tbuf[INET6_ADDRSTRLEN];
#endif
	ifp = NULL;
	error = 0;
	doblock = 0;
	memset(&gsr, 0, sizeof(struct group_source_req));
	gsa = (sockunion_t *)&gsr.gsr_group;
	ssa = (sockunion_t *)&gsr.gsr_source;
	switch (sopt->sopt_name) {
	case MCAST_BLOCK_SOURCE:
	case MCAST_UNBLOCK_SOURCE:
		error = sooptcopyin(sopt, &gsr,
		    sizeof(struct group_source_req),
		    sizeof(struct group_source_req));
		if (error)
			return (error);
		if (gsa->sin6.sin6_family != AF_INET6 ||
		    gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
			return (EINVAL);
		if (ssa->sin6.sin6_family != AF_INET6 ||
		    ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
			return (EINVAL);
		if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
			return (EADDRNOTAVAIL);
		ifp = ifnet_byindex(gsr.gsr_interface);
		if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
			doblock = 1;
		break;
	default:
		CTR2(KTR_MLD, "%s: unknown sopt_name %d",
		    __func__, sopt->sopt_name);
		return (EOPNOTSUPP);
		break;
	}
	if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
		return (EINVAL);
	(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
	imo = in6p_findmoptions(inp);
	idx = im6o_match_group(imo, ifp, &gsa->sa);
	if (idx == -1 || imo->im6o_mfilters == NULL) {
		error = EADDRNOTAVAIL;
		goto out_in6p_locked;
	}
	KASSERT(imo->im6o_mfilters != NULL,
	    ("%s: im6o_mfilters not allocated", __func__));
	imf = &imo->im6o_mfilters[idx];
	inm = imo->im6o_membership[idx];
	fmode = imf->im6f_st[0];
	if (fmode != MCAST_EXCLUDE) {
		error = EINVAL;
		goto out_in6p_locked;
	}
	ims = im6o_match_source(imo, idx, &ssa->sa);
	if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
		CTR3(KTR_MLD, "%s: source %s %spresent", __func__,
		    ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr),
		    doblock ? "" : "not ");
		error = EADDRNOTAVAIL;
		goto out_in6p_locked;
	}
	INP_WLOCK_ASSERT(inp);
	if (doblock) {
		CTR2(KTR_MLD, "%s: %s source", __func__, "block");
		ims = im6f_graft(imf, fmode, &ssa->sin6);
		if (ims == NULL)
			error = ENOMEM;
	} else {
		CTR2(KTR_MLD, "%s: %s source", __func__, "allow");
		error = im6f_prune(imf, &ssa->sin6);
	}
	if (error) {
		CTR1(KTR_MLD, "%s: merge imf state failed", __func__);
		goto out_im6f_rollback;
	}
	IN6_MULTI_LOCK();
	CTR1(KTR_MLD, "%s: merge inm state", __func__);
	error = in6m_merge(inm, imf);
	if (error)
		CTR1(KTR_MLD, "%s: failed to merge inm state", __func__);
	else {
		CTR1(KTR_MLD, "%s: doing mld downcall", __func__);
		error = mld_change_state(inm, 0);
		if (error)
			CTR1(KTR_MLD, "%s: failed mld downcall", __func__);
	}
	IN6_MULTI_UNLOCK();
out_im6f_rollback:
	if (error)
		im6f_rollback(imf);
	else
		im6f_commit(imf);
	im6f_reap(imf);
out_in6p_locked:
	INP_WUNLOCK(inp);
	return (error);
}
static int in6p_block_unblock_source ( struct inpcb * inp , struct sockopt * sopt ) { struct group_source_req gsr ; sockunion_t * gsa , * ssa ; struct ifnet * ifp ; struct in6_mfilter * imf ; struct ip6_moptions * imo ; struct in6_msource * ims ; struct in6_multi * inm ; size_t idx ; uint16_t fmode ; int error , doblock ; #ifdef KTR char ip6tbuf [ INET6_ADDRSTRLEN ] ; #endif ifp = NULL ; error = 0 ; doblock = 0 ; memset ( & gsr , 0 , sizeof ( struct group_source_req ) ) ; gsa = ( sockunion_t * ) & gsr . gsr_group ; ssa = ( sockunion_t * ) & gsr . gsr_source ; switch ( sopt -> sopt_name ) { case MCAST_BLOCK_SOURCE : case MCAST_UNBLOCK_SOURCE : error = sooptcopyin ( sopt , & gsr , sizeof ( struct group_source_req ) , sizeof ( struct group_source_req ) ) ; if ( error ) return ( error ) ; if ( gsa -> sin6 . sin6_family != AF_INET6 || gsa -> sin6 . sin6_len != sizeof ( struct sockaddr_in6 ) ) return ( EINVAL ) ; if ( ssa -> sin6 . sin6_family != AF_INET6 || ssa -> sin6 . sin6_len != sizeof ( struct sockaddr_in6 ) ) return ( EINVAL ) ; if ( gsr . gsr_interface == 0 || V_if_index < gsr . gsr_interface ) return ( EADDRNOTAVAIL ) ; ifp = ifnet_byindex ( gsr . gsr_interface ) ; if ( sopt -> sopt_name == MCAST_BLOCK_SOURCE ) doblock = 1 ; break ; default : CTR2 ( KTR_MLD , " " , __func__ , sopt -> sopt_name ) ; return ( EOPNOTSUPP ) ; break ; } if ( ! IN6_IS_ADDR_MULTICAST ( & gsa -> sin6 . sin6_addr ) ) return ( EINVAL ) ; ( void ) in6_setscope ( & gsa -> sin6 . sin6_addr , ifp , NULL ) ; imo = in6p_findmoptions ( inp ) ; idx = im6o_match_group ( imo , ifp , & gsa -> sa ) ; if ( idx == -1 || imo -> im6o_mfilters == NULL ) { error = EADDRNOTAVAIL ; goto out_in6p_locked ; } KASSERT ( imo -> im6o_mfilters != NULL , ( " " , __func__ ) ) ; imf = & imo -> im6o_mfilters [ idx ] ; inm = imo -> im6o_membership [ idx ] ; fmode = imf -> im6f_st [ 0 ] ; if ( fmode != MCAST_EXCLUDE ) { error = EINVAL ; goto out_in6p_locked ; } ims = im6o_match_source ( imo , idx , & ssa -> sa ) ; if ( ( ims != NULL && doblock ) || ( ims == NULL && ! doblock ) ) { CTR3 ( KTR_MLD , " " , __func__ , ip6_sprintf ( ip6tbuf , & ssa -> sin6 . sin6_addr ) , doblock ? " " : " " ) ; error = EADDRNOTAVAIL ; goto out_in6p_locked ; } INP_WLOCK_ASSERT ( inp ) ; if ( doblock ) { CTR2 ( KTR_MLD , " " , __func__ , " " ) ; ims = im6f_graft ( imf , fmode , & ssa -> sin6 ) ; if ( ims == NULL ) error = ENOMEM ; } else { CTR2 ( KTR_MLD , " " , __func__ , " " ) ; error = im6f_prune ( imf , & ssa -> sin6 ) ; } if ( error ) { CTR1 ( KTR_MLD , " " , __func__ ) ; goto out_im6f_rollback ; } IN6_MULTI_LOCK ( ) ; CTR1 ( KTR_MLD , " " , __func__ ) ; error = in6m_merge ( inm , imf ) ; if ( error ) CTR1 ( KTR_MLD , " " , __func__ ) ; else { CTR1 ( KTR_MLD , " " , __func__ ) ; error = mld_change_state ( inm , 0 ) ; if ( error ) CTR1 ( KTR_MLD , " " , __func__ ) ; } IN6_MULTI_UNLOCK ( ) ; out_im6f_rollback : if ( error ) im6f_rollback ( imf ) ; else im6f_commit ( imf ) ; im6f_reap ( imf ) ; out_in6p_locked : INP_WUNLOCK ( inp ) ; return ( error ) ; }
Block or unblock an ASM multicast source on an inpcb.
Block or unblock an ASM multicast source on an inpcb .
/*
	 * Check if we are actually a member of this group.
	 */ /*
	 * Attempting to use the delta-based API on an
	 * non exclusive-mode membership is an error.
	 */ /*
	 * Deal with error cases up-front:
	 *  Asked to block, but already blocked; or
	 *  Asked to unblock, but nothing to unblock.
	 * If adding a new block entry, allocate it.
	 */ /*
	 * Begin state merge transaction at socket layer.
	 */ /*
	 * Begin state merge transaction at MLD layer.
	 */