FreeBSD/sys/netinet/in_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 IGMP 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 IGMP 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 IGMP 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
inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
{
	struct group_source_req		 gsr;
	sockunion_t			*gsa, *ssa;
	struct ifnet			*ifp;
	struct in_mfilter		*imf;
	struct ip_moptions		*imo;
	struct in_msource		*ims;
	struct in_multi			*inm;
	size_t				 idx;
	uint16_t			 fmode;
	int				 error, doblock;
	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 IP_BLOCK_SOURCE:
	case IP_UNBLOCK_SOURCE: {
		struct ip_mreq_source	 mreqs;
		error = sooptcopyin(sopt, &mreqs,
		    sizeof(struct ip_mreq_source),
		    sizeof(struct ip_mreq_source));
		if (error)
			return (error);
		gsa->sin.sin_family = AF_INET;
		gsa->sin.sin_len = sizeof(struct sockaddr_in);
		gsa->sin.sin_addr = mreqs.imr_multiaddr;
		ssa->sin.sin_family = AF_INET;
		ssa->sin.sin_len = sizeof(struct sockaddr_in);
		ssa->sin.sin_addr = mreqs.imr_sourceaddr;
		if (!in_nullhost(mreqs.imr_interface))
			INADDR_TO_IFP(mreqs.imr_interface, ifp);
		if (sopt->sopt_name == IP_BLOCK_SOURCE)
			doblock = 1;
		CTR3(KTR_IGMPV3, "%s: imr_interface = %s, ifp = %p",
		    __func__, inet_ntoa(mreqs.imr_interface), ifp);
		break;
	    }
	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->sin.sin_family != AF_INET ||
		    gsa->sin.sin_len != sizeof(struct sockaddr_in))
			return (EINVAL);
		if (ssa->sin.sin_family != AF_INET ||
		    ssa->sin.sin_len != sizeof(struct sockaddr_in))
			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_IGMPV3, "%s: unknown sopt_name %d",
		    __func__, sopt->sopt_name);
		return (EOPNOTSUPP);
		break;
	}
	if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
		return (EINVAL);
	imo = inp_findmoptions(inp);
	idx = imo_match_group(imo, ifp, &gsa->sa);
	if (idx == -1 || imo->imo_mfilters == NULL) {
		error = EADDRNOTAVAIL;
		goto out_inp_locked;
	}
	KASSERT(imo->imo_mfilters != NULL,
	    ("%s: imo_mfilters not allocated", __func__));
	imf = &imo->imo_mfilters[idx];
	inm = imo->imo_membership[idx];
	fmode = imf->imf_st[0];
	if (fmode != MCAST_EXCLUDE) {
		error = EINVAL;
		goto out_inp_locked;
	}
	ims = imo_match_source(imo, idx, &ssa->sa);
	if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
		CTR3(KTR_IGMPV3, "%s: source %s %spresent", __func__,
		    inet_ntoa(ssa->sin.sin_addr), doblock ? "" : "not ");
		error = EADDRNOTAVAIL;
		goto out_inp_locked;
	}
	INP_WLOCK_ASSERT(inp);
	if (doblock) {
		CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block");
		ims = imf_graft(imf, fmode, &ssa->sin);
		if (ims == NULL)
			error = ENOMEM;
	} else {
		CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow");
		error = imf_prune(imf, &ssa->sin);
	}
	if (error) {
		CTR1(KTR_IGMPV3, "%s: merge imf state failed", __func__);
		goto out_imf_rollback;
	}
	IN_MULTI_LOCK();
	CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
	error = inm_merge(inm, imf);
	if (error) {
		CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
		goto out_in_multi_locked;
	}
	CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
	error = igmp_change_state(inm);
	if (error)
		CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
out_in_multi_locked:
	IN_MULTI_UNLOCK();
out_imf_rollback:
	if (error)
		imf_rollback(imf);
	else
		imf_commit(imf);
	imf_reap(imf);
out_inp_locked:
	INP_WUNLOCK(inp);
	return (error);
}
static int inp_block_unblock_source ( struct inpcb * inp , struct sockopt * sopt ) { struct group_source_req gsr ; sockunion_t * gsa , * ssa ; struct ifnet * ifp ; struct in_mfilter * imf ; struct ip_moptions * imo ; struct in_msource * ims ; struct in_multi * inm ; size_t idx ; uint16_t fmode ; int error , doblock ; 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 IP_BLOCK_SOURCE : case IP_UNBLOCK_SOURCE : { struct ip_mreq_source mreqs ; error = sooptcopyin ( sopt , & mreqs , sizeof ( struct ip_mreq_source ) , sizeof ( struct ip_mreq_source ) ) ; if ( error ) return ( error ) ; gsa -> sin . sin_family = AF_INET ; gsa -> sin . sin_len = sizeof ( struct sockaddr_in ) ; gsa -> sin . sin_addr = mreqs . imr_multiaddr ; ssa -> sin . sin_family = AF_INET ; ssa -> sin . sin_len = sizeof ( struct sockaddr_in ) ; ssa -> sin . sin_addr = mreqs . imr_sourceaddr ; if ( ! in_nullhost ( mreqs . imr_interface ) ) INADDR_TO_IFP ( mreqs . imr_interface , ifp ) ; if ( sopt -> sopt_name == IP_BLOCK_SOURCE ) doblock = 1 ; CTR3 ( KTR_IGMPV3 , " " , __func__ , inet_ntoa ( mreqs . imr_interface ) , ifp ) ; break ; } 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 -> sin . sin_family != AF_INET || gsa -> sin . sin_len != sizeof ( struct sockaddr_in ) ) return ( EINVAL ) ; if ( ssa -> sin . sin_family != AF_INET || ssa -> sin . sin_len != sizeof ( struct sockaddr_in ) ) 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_IGMPV3 , " " , __func__ , sopt -> sopt_name ) ; return ( EOPNOTSUPP ) ; break ; } if ( ! IN_MULTICAST ( ntohl ( gsa -> sin . sin_addr . s_addr ) ) ) return ( EINVAL ) ; imo = inp_findmoptions ( inp ) ; idx = imo_match_group ( imo , ifp , & gsa -> sa ) ; if ( idx == -1 || imo -> imo_mfilters == NULL ) { error = EADDRNOTAVAIL ; goto out_inp_locked ; } KASSERT ( imo -> imo_mfilters != NULL , ( " " , __func__ ) ) ; imf = & imo -> imo_mfilters [ idx ] ; inm = imo -> imo_membership [ idx ] ; fmode = imf -> imf_st [ 0 ] ; if ( fmode != MCAST_EXCLUDE ) { error = EINVAL ; goto out_inp_locked ; } ims = imo_match_source ( imo , idx , & ssa -> sa ) ; if ( ( ims != NULL && doblock ) || ( ims == NULL && ! doblock ) ) { CTR3 ( KTR_IGMPV3 , " " , __func__ , inet_ntoa ( ssa -> sin . sin_addr ) , doblock ? " " : " " ) ; error = EADDRNOTAVAIL ; goto out_inp_locked ; } INP_WLOCK_ASSERT ( inp ) ; if ( doblock ) { CTR2 ( KTR_IGMPV3 , " " , __func__ , " " ) ; ims = imf_graft ( imf , fmode , & ssa -> sin ) ; if ( ims == NULL ) error = ENOMEM ; } else { CTR2 ( KTR_IGMPV3 , " " , __func__ , " " ) ; error = imf_prune ( imf , & ssa -> sin ) ; } if ( error ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; goto out_imf_rollback ; } IN_MULTI_LOCK ( ) ; CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; error = inm_merge ( inm , imf ) ; if ( error ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; goto out_in_multi_locked ; } CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; error = igmp_change_state ( inm ) ; if ( error ) CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; out_in_multi_locked : IN_MULTI_UNLOCK ( ) ; out_imf_rollback : if ( error ) imf_rollback ( imf ) ; else imf_commit ( imf ) ; imf_reap ( imf ) ; out_inp_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 IGMP layer.
	 */