FreeBSD/sys/netinet6/mld6.c
/*
 * Perform the initial join for an MLD group.
 *
 * When joining a group:
 *  If the group should have its MLD traffic suppressed, do nothing.
 *  MLDv1 starts sending MLDv1 host membership reports.
 *  MLDv2 will schedule an MLDv2 state-change report containing the
 *  initial state of the membership.
 *
 * If the delay argument is non-zero, then we must delay sending the
 * initial state change for delay ticks (in units of PR_FASTHZ).
 */
Perform the initial join for an MLD group.
When joining a group:
If the group should have its MLD traffic suppressed, do nothing.
MLDv1 starts sending MLDv1 host membership reports.
MLDv2 will schedule an MLDv2 state-change report containing the
initial state of the membership.

If the delay argument is non-zero, then we must delay sending the
initial state change for delay ticks (in units of PR_FASTHZ).
Perform the initial join for an MLD group . When joining a group : If the group should have its MLD traffic suppressed do nothing . MLDv1 starts sending MLDv1 host membership reports . MLDv2 will schedule an MLDv2 state - change report containing the initial state of the membership . If the delay argument is non - zero then we must delay sending the initial state change for delay ticks ( in units of PR_FASTHZ ) .
static int
mld_initial_join(struct in6_multi *inm, struct mld_ifsoftc *mli,
    const int delay)
{
	struct ifnet		*ifp;
	struct mbufq		*mq;
	int			 error, retval, syncstates;
	int			 odelay;
#ifdef KTR
	char			 ip6tbuf[INET6_ADDRSTRLEN];
#endif
	CTR4(KTR_MLD, "%s: initial join %s on ifp %p(%s)",
	    __func__, ip6_sprintf(ip6tbuf, &inm->in6m_addr),
	    inm->in6m_ifp, if_name(inm->in6m_ifp));
	error = 0;
	syncstates = 1;
	ifp = inm->in6m_ifp;
	IN6_MULTI_LOCK_ASSERT();
	MLD_LOCK_ASSERT();
	KASSERT(mli && mli->mli_ifp == ifp, ("%s: inconsistent ifp", __func__));
	if ((ifp->if_flags & IFF_LOOPBACK) ||
	    (mli->mli_flags & MLIF_SILENT) ||
	    !mld_is_addr_reported(&inm->in6m_addr)) {
		CTR1(KTR_MLD,
"%s: not kicking state machine for silent group", __func__);
		inm->in6m_state = MLD_SILENT_MEMBER;
		inm->in6m_timer = 0;
	} else {
		if (mli->mli_version == MLD_VERSION_2 &&
		    inm->in6m_state == MLD_LEAVING_MEMBER)
			in6m_release_locked(inm);
		inm->in6m_state = MLD_REPORTING_MEMBER;
		switch (mli->mli_version) {
		case MLD_VERSION_1:
			odelay = MLD_RANDOM_DELAY(MLD_V1_MAX_RI * PR_FASTHZ);
			if (delay) {
				inm->in6m_timer = max(delay, odelay);
				V_current_state_timers_running6 = 1;
			} else {
				inm->in6m_state = MLD_IDLE_MEMBER;
				error = mld_v1_transmit_report(inm,
				     MLD_LISTENER_REPORT);
				if (error == 0) {
					inm->in6m_timer = odelay;
					V_current_state_timers_running6 = 1;
				}
			}
			break;
		case MLD_VERSION_2:
			syncstates = 0;
			mq = &inm->in6m_scq;
			mbufq_drain(mq);
			retval = mld_v2_enqueue_group_record(mq, inm, 1,
			    0, 0, (mli->mli_flags & MLIF_USEALLOW));
			CTR2(KTR_MLD, "%s: enqueue record = %d",
			    __func__, retval);
			if (retval <= 0) {
				error = retval * -1;
				break;
			}
			KASSERT(mli->mli_rv > 1,
			   ("%s: invalid robustness %d", __func__,
			    mli->mli_rv));
			inm->in6m_scrv = mli->mli_rv;
			if (delay) {
				if (inm->in6m_sctimer > 1) {
					inm->in6m_sctimer =
					    min(inm->in6m_sctimer, delay);
				} else
					inm->in6m_sctimer = delay;
			} else
				inm->in6m_sctimer = 1;
			V_state_change_timers_running6 = 1;
			error = 0;
			break;
		}
	}
	if (syncstates) {
		in6m_commit(inm);
		CTR3(KTR_MLD, "%s: T1 -> T0 for %s/%s", __func__,
		    ip6_sprintf(ip6tbuf, &inm->in6m_addr),
		    if_name(inm->in6m_ifp));
	}
	return (error);
}
static int mld_initial_join ( struct in6_multi * inm , struct mld_ifsoftc * mli , const int delay ) { struct ifnet * ifp ; struct mbufq * mq ; int error , retval , syncstates ; int odelay ; #ifdef KTR char ip6tbuf [ INET6_ADDRSTRLEN ] ; #endif CTR4 ( KTR_MLD , " " , __func__ , ip6_sprintf ( ip6tbuf , & inm -> in6m_addr ) , inm -> in6m_ifp , if_name ( inm -> in6m_ifp ) ) ; error = 0 ; syncstates = 1 ; ifp = inm -> in6m_ifp ; IN6_MULTI_LOCK_ASSERT ( ) ; MLD_LOCK_ASSERT ( ) ; KASSERT ( mli && mli -> mli_ifp == ifp , ( " " , __func__ ) ) ; if ( ( ifp -> if_flags & IFF_LOOPBACK ) || ( mli -> mli_flags & MLIF_SILENT ) || ! mld_is_addr_reported ( & inm -> in6m_addr ) ) { CTR1 ( KTR_MLD , " " , __func__ ) ; inm -> in6m_state = MLD_SILENT_MEMBER ; inm -> in6m_timer = 0 ; } else { if ( mli -> mli_version == MLD_VERSION_2 && inm -> in6m_state == MLD_LEAVING_MEMBER ) in6m_release_locked ( inm ) ; inm -> in6m_state = MLD_REPORTING_MEMBER ; switch ( mli -> mli_version ) { case MLD_VERSION_1 : odelay = MLD_RANDOM_DELAY ( MLD_V1_MAX_RI * PR_FASTHZ ) ; if ( delay ) { inm -> in6m_timer = max ( delay , odelay ) ; V_current_state_timers_running6 = 1 ; } else { inm -> in6m_state = MLD_IDLE_MEMBER ; error = mld_v1_transmit_report ( inm , MLD_LISTENER_REPORT ) ; if ( error == 0 ) { inm -> in6m_timer = odelay ; V_current_state_timers_running6 = 1 ; } } break ; case MLD_VERSION_2 : syncstates = 0 ; mq = & inm -> in6m_scq ; mbufq_drain ( mq ) ; retval = mld_v2_enqueue_group_record ( mq , inm , 1 , 0 , 0 , ( mli -> mli_flags & MLIF_USEALLOW ) ) ; CTR2 ( KTR_MLD , " " , __func__ , retval ) ; if ( retval <= 0 ) { error = retval * -1 ; break ; } KASSERT ( mli -> mli_rv > 1 , ( " " , __func__ , mli -> mli_rv ) ) ; inm -> in6m_scrv = mli -> mli_rv ; if ( delay ) { if ( inm -> in6m_sctimer > 1 ) { inm -> in6m_sctimer = min ( inm -> in6m_sctimer , delay ) ; } else inm -> in6m_sctimer = delay ; } else inm -> in6m_sctimer = 1 ; V_state_change_timers_running6 = 1 ; error = 0 ; break ; } } if ( syncstates ) { in6m_commit ( inm ) ; CTR3 ( KTR_MLD , " " , __func__ , ip6_sprintf ( ip6tbuf , & inm -> in6m_addr ) , if_name ( inm -> in6m_ifp ) ) ; } return ( error ) ; }
Perform the initial join for an MLD group.
Perform the initial join for an MLD group .
/*
	 * Groups joined on loopback or marked as 'not reported',
	 * enter the MLD_SILENT_MEMBER state and
	 * are never reported in any protocol exchanges.
	 * All other groups enter the appropriate state machine
	 * for the version in use on this link.
	 * A link marked as MLIF_SILENT causes MLD to be completely
	 * disabled for the link.
	 */ /*
		 * Deal with overlapping in_multi lifecycle.
		 * If this group was LEAVING, then make sure
		 * we drop the reference we picked up to keep the
		 * group around for the final INCLUDE {} enqueue.
		 */ /*
			 * If a delay was provided, only use it if
			 * it is greater than the delay normally
			 * used for an MLDv1 state change report,
			 * and delay sending the initial MLDv1 report
			 * by not transitioning to the IDLE state.
			 */ /*
			 * Defer update of T0 to T1, until the first copy
			 * of the state change has been transmitted.
			 */ /*
			 * Immediately enqueue a State-Change Report for
			 * this interface, freeing any previous reports.
			 * Don't kick the timers if there is nothing to do,
			 * or if an error occurred.
			 */ /*
			 * Schedule transmission of pending state-change
			 * report up to RV times for this link. The timer
			 * will fire at the next mld_fasttimo (~200ms),
			 * giving us an opportunity to merge the reports.
			 *
			 * If a delay was provided to this function, only
			 * use this delay if sooner than the existing one.
			 */ /*
	 * Only update the T0 state if state change is atomic,
	 * i.e. we don't need to wait for a timer to fire before we
	 * can consider the state change to have been communicated.
	 */