FreeBSD/sys/netinet6/mld6.c
/*
 * Process a state change from the upper layer for the given IPv6 group.
 *
 * Each socket holds a reference on the in_multi in its own ip_moptions.
 * The socket layer will have made the necessary updates to.the group
 * state, it is now up to MLD to issue a state change report if there
 * has been any change between T0 (when the last state-change was issued)
 * and T1 (now).
 *
 * We use the MLDv2 state machine at group level. The MLd module
 * however makes the decision as to which MLD protocol version to speak.
 * A state change *from* INCLUDE {} always means an initial join.
 * A state change *to* INCLUDE {} always means a final leave.
 *
 * If delay is non-zero, and the state change is an initial multicast
 * join, the state change report will be delayed by 'delay' ticks
 * in units of PR_FASTHZ if MLDv1 is active on the link; otherwise
 * the initial MLDv2 state change report will be delayed by whichever
 * is sooner, a pending state-change timer or delay itself.
 *
 * VIMAGE: curvnet should have been set by caller, as this routine
 * is called from the socket option handlers.
 */
Process a state change from the upper layer for the given IPv6 group.
Each socket holds a reference on the in_multi in its own ip_moptions.
The socket layer will have made the necessary updates to.the group
state, it is now up to MLD to issue a state change report if there
has been any change between T0 (when the last state-change was issued)
and T1 (now).

We use the MLDv2 state machine at group level. The MLd module
however makes the decision as to which MLD protocol version to speak.
A state change *from* INCLUDE {} always means an initial join.
A state change *to* INCLUDE {} always means a final leave.

If delay is non-zero, and the state change is an initial multicast
join, the state change report will be delayed by 'delay' ticks
in units of PR_FASTHZ if MLDv1 is active on the link; otherwise
the initial MLDv2 state change report will be delayed by whichever
is sooner, a pending state-change timer or delay itself.

curvnet should have been set by caller, as this routine
is called from the socket option handlers.
Process a state change from the upper layer for the given IPv6 group . Each socket holds a reference on the in_multi in its own ip_moptions . The socket layer will have made the necessary updates to . the group state it is now up to MLD to issue a state change report if there has been any change between T0 ( when the last state - change was issued ) and T1 ( now ) . We use the MLDv2 state machine at group level . The MLd module however makes the decision as to which MLD protocol version to speak . A state change * from * INCLUDE {} always means an initial join . A state change * to * INCLUDE {} always means a final leave . If delay is non - zero and the state change is an initial multicast join the state change report will be delayed by ' delay ' ticks in units of PR_FASTHZ if MLDv1 is active on the link ; otherwise the initial MLDv2 state change report will be delayed by whichever is sooner a pending state - change timer or delay itself . curvnet should have been set by caller as this routine is called from the socket option handlers .
int
mld_change_state(struct in6_multi *inm, const int delay)
{
	struct mld_ifsoftc *mli;
	struct ifnet *ifp;
	int error;
	IN6_MULTI_LOCK_ASSERT();
	error = 0;
	KASSERT(inm->in6m_ifma != NULL, ("%s: no ifma", __func__));
	ifp = inm->in6m_ifma->ifma_ifp;
	if (ifp != NULL) {
		KASSERT(inm->in6m_ifp == ifp, ("%s: bad ifp", __func__));
	}
	MLD_LOCK();
	mli = MLD_IFINFO(ifp);
	KASSERT(mli != NULL, ("%s: no mld_ifsoftc for ifp %p", __func__, ifp));
	if (inm->in6m_st[1].iss_fmode != inm->in6m_st[0].iss_fmode) {
		CTR3(KTR_MLD, "%s: inm transition %d -> %d", __func__,
		    inm->in6m_st[0].iss_fmode, inm->in6m_st[1].iss_fmode);
		if (inm->in6m_st[0].iss_fmode == MCAST_UNDEFINED) {
			CTR1(KTR_MLD, "%s: initial join", __func__);
			error = mld_initial_join(inm, mli, delay);
			goto out_locked;
		} else if (inm->in6m_st[1].iss_fmode == MCAST_UNDEFINED) {
			CTR1(KTR_MLD, "%s: final leave", __func__);
			mld_final_leave(inm, mli);
			goto out_locked;
		}
	} else {
		CTR1(KTR_MLD, "%s: filter set change", __func__);
	}
	error = mld_handle_state_change(inm, mli);
out_locked:
	MLD_UNLOCK();
	return (error);
}
int mld_change_state ( struct in6_multi * inm , const int delay ) { struct mld_ifsoftc * mli ; struct ifnet * ifp ; int error ; IN6_MULTI_LOCK_ASSERT ( ) ; error = 0 ; KASSERT ( inm -> in6m_ifma != NULL , ( " " , __func__ ) ) ; ifp = inm -> in6m_ifma -> ifma_ifp ; if ( ifp != NULL ) { KASSERT ( inm -> in6m_ifp == ifp , ( " " , __func__ ) ) ; } MLD_LOCK ( ) ; mli = MLD_IFINFO ( ifp ) ; KASSERT ( mli != NULL , ( " " , __func__ , ifp ) ) ; if ( inm -> in6m_st [ 1 ] . iss_fmode != inm -> in6m_st [ 0 ] . iss_fmode ) { CTR3 ( KTR_MLD , " " , __func__ , inm -> in6m_st [ 0 ] . iss_fmode , inm -> in6m_st [ 1 ] . iss_fmode ) ; if ( inm -> in6m_st [ 0 ] . iss_fmode == MCAST_UNDEFINED ) { CTR1 ( KTR_MLD , " " , __func__ ) ; error = mld_initial_join ( inm , mli , delay ) ; goto out_locked ; } else if ( inm -> in6m_st [ 1 ] . iss_fmode == MCAST_UNDEFINED ) { CTR1 ( KTR_MLD , " " , __func__ ) ; mld_final_leave ( inm , mli ) ; goto out_locked ; } } else { CTR1 ( KTR_MLD , " " , __func__ ) ; } error = mld_handle_state_change ( inm , mli ) ; out_locked : MLD_UNLOCK ( ) ; return ( error ) ; }
Process a state change from the upper layer for the given IPv6 group.
Process a state change from the upper layer for the given IPv6 group .
/*
	 * Try to detect if the upper layer just asked us to change state
	 * for an interface which has now gone away.
	 */ /*
		 * Sanity check that netinet6's notion of ifp is the
		 * same as net's.
		 */ /*
	 * If we detect a state transition to or from MCAST_UNDEFINED
	 * for this group, then we are starting or finishing an MLD
	 * life cycle for this group.
	 */