FreeBSD/sys/netinet/in_mcast.c
/*
 * Atomically update the global in_multi state, when a membership's
 * filter list is being updated in any way.
 *
 * imf is the per-inpcb-membership group filter pointer.
 * A fake imf may be passed for in-kernel consumers.
 *
 * XXX This is a candidate for a set-symmetric-difference style loop
 * which would eliminate the repeated lookup from root of ims nodes,
 * as they share the same key space.
 *
 * If any error occurred this function will back out of refcounts
 * and return a non-zero value.
 */
Atomically update the global in_multi state, when a membership's
filter list is being updated in any way.

imf is the per-inpcb-membership group filter pointer.
A fake imf may be passed for in-kernel consumers.

XXX This is a candidate for a set-symmetric-difference style loop
which would eliminate the repeated lookup from root of ims nodes,
as they share the same key space.

If any error occurred this function will back out of refcounts
and return a non-zero value.
Atomically update the global in_multi state when a membership ' s filter list is being updated in any way . imf is the per - inpcb - membership group filter pointer . A fake imf may be passed for in - kernel consumers . XXX This is a candidate for a set - symmetric - difference style loop which would eliminate the repeated lookup from root of ims nodes as they share the same key space . If any error occurred this function will back out of refcounts and return a non - zero value .
static int
inm_merge(struct in_multi *inm,  struct in_mfilter *imf)
{
	struct ip_msource	*ims, *nims;
	struct in_msource	*lims;
	int			 schanged, error;
	int			 nsrc0, nsrc1;
	schanged = 0;
	error = 0;
	nsrc1 = nsrc0 = 0;
	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
		lims = (struct in_msource *)ims;
		if (lims->imsl_st[0] == imf->imf_st[0]) nsrc0++;
		if (lims->imsl_st[1] == imf->imf_st[1]) nsrc1++;
		if (lims->imsl_st[0] == lims->imsl_st[1]) continue;
		error = inm_get_source(inm, lims->ims_haddr, 0, &nims);
		++schanged;
		if (error)
			break;
		ims_merge(nims, lims, 0);
	}
	if (error) {
		struct ip_msource *bims;
		RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) {
			lims = (struct in_msource *)ims;
			if (lims->imsl_st[0] == lims->imsl_st[1])
				continue;
			(void)inm_get_source(inm, lims->ims_haddr, 1, &bims);
			if (bims == NULL)
				continue;
			ims_merge(bims, lims, 1);
		}
		goto out_reap;
	}
	CTR3(KTR_IGMPV3, "%s: imf filters in-mode: %d at t0, %d at t1",
	    __func__, nsrc0, nsrc1);
	if (imf->imf_st[0] == imf->imf_st[1] &&
	    imf->imf_st[1] == MCAST_INCLUDE) {
		if (nsrc1 == 0) {
			CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__);
			--inm->inm_st[1].iss_in;
		}
	}
	if (imf->imf_st[0] != imf->imf_st[1]) {
		CTR3(KTR_IGMPV3, "%s: imf transition %d to %d",
		    __func__, imf->imf_st[0], imf->imf_st[1]);
		if (imf->imf_st[0] == MCAST_EXCLUDE) {
			CTR1(KTR_IGMPV3, "%s: --ex on inm at t1", __func__);
			--inm->inm_st[1].iss_ex;
		} else if (imf->imf_st[0] == MCAST_INCLUDE) {
			CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__);
			--inm->inm_st[1].iss_in;
		}
		if (imf->imf_st[1] == MCAST_EXCLUDE) {
			CTR1(KTR_IGMPV3, "%s: ex++ on inm at t1", __func__);
			inm->inm_st[1].iss_ex++;
		} else if (imf->imf_st[1] == MCAST_INCLUDE && nsrc1 > 0) {
			CTR1(KTR_IGMPV3, "%s: in++ on inm at t1", __func__);
			inm->inm_st[1].iss_in++;
		}
	}
	if (inm->inm_st[1].iss_ex > 0) {
		CTR1(KTR_IGMPV3, "%s: transition to EX", __func__);
		inm->inm_st[1].iss_fmode = MCAST_EXCLUDE;
	} else if (inm->inm_st[1].iss_in > 0) {
		CTR1(KTR_IGMPV3, "%s: transition to IN", __func__);
		inm->inm_st[1].iss_fmode = MCAST_INCLUDE;
	} else {
		CTR1(KTR_IGMPV3, "%s: transition to UNDEF", __func__);
		inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
	}
	if (imf->imf_st[0] == MCAST_EXCLUDE && nsrc0 == 0) {
		if ((imf->imf_st[1] != MCAST_EXCLUDE) ||
		    (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 > 0))
			CTR1(KTR_IGMPV3, "%s: --asm on inm at t1", __func__);
			--inm->inm_st[1].iss_asm;
	}
	if (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 == 0) {
		CTR1(KTR_IGMPV3, "%s: asm++ on inm at t1", __func__);
		inm->inm_st[1].iss_asm++;
	}
	CTR3(KTR_IGMPV3, "%s: merged imf %p to inm %p", __func__, imf, inm);
	inm_print(inm);
out_reap:
	if (schanged > 0) {
		CTR1(KTR_IGMPV3, "%s: sources changed; reaping", __func__);
		inm_reap(inm);
	}
	return (error);
}
static int inm_merge ( struct in_multi * inm , struct in_mfilter * imf ) { struct ip_msource * ims , * nims ; struct in_msource * lims ; int schanged , error ; int nsrc0 , nsrc1 ; schanged = 0 ; error = 0 ; nsrc1 = nsrc0 = 0 ; RB_FOREACH ( ims , ip_msource_tree , & imf -> imf_sources ) { lims = ( struct in_msource * ) ims ; if ( lims -> imsl_st [ 0 ] == imf -> imf_st [ 0 ] ) nsrc0 ++ ; if ( lims -> imsl_st [ 1 ] == imf -> imf_st [ 1 ] ) nsrc1 ++ ; if ( lims -> imsl_st [ 0 ] == lims -> imsl_st [ 1 ] ) continue ; error = inm_get_source ( inm , lims -> ims_haddr , 0 , & nims ) ; ++ schanged ; if ( error ) break ; ims_merge ( nims , lims , 0 ) ; } if ( error ) { struct ip_msource * bims ; RB_FOREACH_REVERSE_FROM ( ims , ip_msource_tree , nims ) { lims = ( struct in_msource * ) ims ; if ( lims -> imsl_st [ 0 ] == lims -> imsl_st [ 1 ] ) continue ; ( void ) inm_get_source ( inm , lims -> ims_haddr , 1 , & bims ) ; if ( bims == NULL ) continue ; ims_merge ( bims , lims , 1 ) ; } goto out_reap ; } CTR3 ( KTR_IGMPV3 , " " , __func__ , nsrc0 , nsrc1 ) ; if ( imf -> imf_st [ 0 ] == imf -> imf_st [ 1 ] && imf -> imf_st [ 1 ] == MCAST_INCLUDE ) { if ( nsrc1 == 0 ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; -- inm -> inm_st [ 1 ] . iss_in ; } } if ( imf -> imf_st [ 0 ] != imf -> imf_st [ 1 ] ) { CTR3 ( KTR_IGMPV3 , " " , __func__ , imf -> imf_st [ 0 ] , imf -> imf_st [ 1 ] ) ; if ( imf -> imf_st [ 0 ] == MCAST_EXCLUDE ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; -- inm -> inm_st [ 1 ] . iss_ex ; } else if ( imf -> imf_st [ 0 ] == MCAST_INCLUDE ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; -- inm -> inm_st [ 1 ] . iss_in ; } if ( imf -> imf_st [ 1 ] == MCAST_EXCLUDE ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; inm -> inm_st [ 1 ] . iss_ex ++ ; } else if ( imf -> imf_st [ 1 ] == MCAST_INCLUDE && nsrc1 > 0 ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; inm -> inm_st [ 1 ] . iss_in ++ ; } } if ( inm -> inm_st [ 1 ] . iss_ex > 0 ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; inm -> inm_st [ 1 ] . iss_fmode = MCAST_EXCLUDE ; } else if ( inm -> inm_st [ 1 ] . iss_in > 0 ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; inm -> inm_st [ 1 ] . iss_fmode = MCAST_INCLUDE ; } else { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; inm -> inm_st [ 1 ] . iss_fmode = MCAST_UNDEFINED ; } if ( imf -> imf_st [ 0 ] == MCAST_EXCLUDE && nsrc0 == 0 ) { if ( ( imf -> imf_st [ 1 ] != MCAST_EXCLUDE ) || ( imf -> imf_st [ 1 ] == MCAST_EXCLUDE && nsrc1 > 0 ) ) CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; -- inm -> inm_st [ 1 ] . iss_asm ; } if ( imf -> imf_st [ 1 ] == MCAST_EXCLUDE && nsrc1 == 0 ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; inm -> inm_st [ 1 ] . iss_asm ++ ; } CTR3 ( KTR_IGMPV3 , " " , __func__ , imf , inm ) ; inm_print ( inm ) ; out_reap : if ( schanged > 0 ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; inm_reap ( inm ) ; } return ( error ) ; }
Atomically update the global in_multi state, when a membership's
 filter list is being updated in any way.
Atomically update the global in_multi state when a membership ' s filter list is being updated in any way .
/*const*/ /*
	 * Update the source filters first, as this may fail.
	 * Maintain count of in-mode filters at t0, t1. These are
	 * used to work out if we transition into ASM mode or not.
	 * Maintain a count of source filters whose state was
	 * actually modified by this operation.
	 */ /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ /* Handle filter mode transition on socket. */ /*
	 * Track inm filter state in terms of listener counts.
	 * If there are any exclusive listeners, stack-wide
	 * membership is exclusive.
	 * Otherwise, if only inclusive listeners, stack-wide is inclusive.
	 * If no listeners remain, state is undefined at t1,
	 * and the IGMP lifecycle for this group should finish.
	 */ /* Decrement ASM listener count on transition out of ASM mode. */ /* Increment ASM listener count on transition to ASM mode. */