FreeBSD/sys/netinet6/in6_mcast.c
/*
 * Find and return a reference to an in6_multi record for (ifp, group),
 * and bump its reference count.
 * If one does not exist, try to allocate it, and update link-layer multicast
 * filters on ifp to listen for group.
 * Assumes the IN6_MULTI lock is held across the call.
 * Return 0 if successful, otherwise return an appropriate error code.
 */
Find and return a reference to an in6_multi record for (ifp, group),
and bump its reference count.
If one does not exist, try to allocate it, and update link-layer multicast
filters on ifp to listen for group.
Assumes the IN6_MULTI lock is held across the call.
Return 0 if successful, otherwise return an appropriate error code.
Find and return a reference to an in6_multi record for ( ifp group ) and bump its reference count . If one does not exist try to allocate it and update link - layer multicast filters on ifp to listen for group . Assumes the IN6_MULTI lock is held across the call . Return 0 if successful otherwise return an appropriate error code .
static int
in6_mc_get(struct ifnet *ifp, const struct in6_addr *group,
    struct in6_multi **pinm)
{
	struct sockaddr_in6	 gsin6;
	struct ifmultiaddr	*ifma;
	struct in6_multi	*inm;
	int			 error;
	error = 0;
	IN6_MULTI_LOCK_ASSERT();
	IF_ADDR_WLOCK(ifp);
	inm = in6m_lookup_locked(ifp, group);
	if (inm != NULL) {
		KASSERT(inm->in6m_refcount >= 1,
		    ("%s: bad refcount %d", __func__, inm->in6m_refcount));
		++inm->in6m_refcount;
		*pinm = inm;
		goto out_locked;
	}
	memset(&gsin6, 0, sizeof(gsin6));
	gsin6.sin6_family = AF_INET6;
	gsin6.sin6_len = sizeof(struct sockaddr_in6);
	gsin6.sin6_addr = *group;
	IF_ADDR_WUNLOCK(ifp);
	error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma);
	if (error != 0)
		return (error);
	IF_ADDR_WLOCK(ifp);
	if (ifma->ifma_protospec != NULL) {
		inm = (struct in6_multi *)ifma->ifma_protospec;
#ifdef INVARIANTS
		KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr",
		    __func__));
		KASSERT(ifma->ifma_addr->sa_family == AF_INET6,
		    ("%s: ifma not AF_INET6", __func__));
		KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__));
		if (inm->in6m_ifma != ifma || inm->in6m_ifp != ifp ||
		    !IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, group))
			panic("%s: ifma %p is inconsistent with %p (%p)",
			    __func__, ifma, inm, group);
#endif
		++inm->in6m_refcount;
		*pinm = inm;
		goto out_locked;
	}
	IF_ADDR_WLOCK_ASSERT(ifp);
	inm = malloc(sizeof(*inm), M_IP6MADDR, M_NOWAIT | M_ZERO);
	if (inm == NULL) {
		IF_ADDR_WUNLOCK(ifp);
		if_delmulti_ifma(ifma);
		return (ENOMEM);
	}
	inm->in6m_addr = *group;
	inm->in6m_ifp = ifp;
	inm->in6m_mli = MLD_IFINFO(ifp);
	inm->in6m_ifma = ifma;
	inm->in6m_refcount = 1;
	inm->in6m_state = MLD_NOT_MEMBER;
	mbufq_init(&inm->in6m_scq, MLD_MAX_STATE_CHANGES);
	inm->in6m_st[0].iss_fmode = MCAST_UNDEFINED;
	inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED;
	RB_INIT(&inm->in6m_srcs);
	ifma->ifma_protospec = inm;
	*pinm = inm;
out_locked:
	IF_ADDR_WUNLOCK(ifp);
	return (error);
}
static int in6_mc_get ( struct ifnet * ifp , const struct in6_addr * group , struct in6_multi * * pinm ) { struct sockaddr_in6 gsin6 ; struct ifmultiaddr * ifma ; struct in6_multi * inm ; int error ; error = 0 ; IN6_MULTI_LOCK_ASSERT ( ) ; IF_ADDR_WLOCK ( ifp ) ; inm = in6m_lookup_locked ( ifp , group ) ; if ( inm != NULL ) { KASSERT ( inm -> in6m_refcount >= 1 , ( " " , __func__ , inm -> in6m_refcount ) ) ; ++ inm -> in6m_refcount ; * pinm = inm ; goto out_locked ; } memset ( & gsin6 , 0 , sizeof ( gsin6 ) ) ; gsin6 . sin6_family = AF_INET6 ; gsin6 . sin6_len = sizeof ( struct sockaddr_in6 ) ; gsin6 . sin6_addr = * group ; IF_ADDR_WUNLOCK ( ifp ) ; error = if_addmulti ( ifp , ( struct sockaddr * ) & gsin6 , & ifma ) ; if ( error != 0 ) return ( error ) ; IF_ADDR_WLOCK ( ifp ) ; if ( ifma -> ifma_protospec != NULL ) { inm = ( struct in6_multi * ) ifma -> ifma_protospec ; #ifdef INVARIANTS KASSERT ( ifma -> ifma_addr != NULL , ( " " , __func__ ) ) ; KASSERT ( ifma -> ifma_addr -> sa_family == AF_INET6 , ( " " , __func__ ) ) ; KASSERT ( inm != NULL , ( " " , __func__ ) ) ; if ( inm -> in6m_ifma != ifma || inm -> in6m_ifp != ifp || ! IN6_ARE_ADDR_EQUAL ( & inm -> in6m_addr , group ) ) panic ( " " , __func__ , ifma , inm , group ) ; #endif ++ inm -> in6m_refcount ; * pinm = inm ; goto out_locked ; } IF_ADDR_WLOCK_ASSERT ( ifp ) ; inm = malloc ( sizeof ( * inm ) , M_IP6MADDR , M_NOWAIT | M_ZERO ) ; if ( inm == NULL ) { IF_ADDR_WUNLOCK ( ifp ) ; if_delmulti_ifma ( ifma ) ; return ( ENOMEM ) ; } inm -> in6m_addr = * group ; inm -> in6m_ifp = ifp ; inm -> in6m_mli = MLD_IFINFO ( ifp ) ; inm -> in6m_ifma = ifma ; inm -> in6m_refcount = 1 ; inm -> in6m_state = MLD_NOT_MEMBER ; mbufq_init ( & inm -> in6m_scq , MLD_MAX_STATE_CHANGES ) ; inm -> in6m_st [ 0 ] . iss_fmode = MCAST_UNDEFINED ; inm -> in6m_st [ 1 ] . iss_fmode = MCAST_UNDEFINED ; RB_INIT ( & inm -> in6m_srcs ) ; ifma -> ifma_protospec = inm ; * pinm = inm ; out_locked : IF_ADDR_WUNLOCK ( ifp ) ; return ( error ) ; }
Find and return a reference to an in6_multi record for (ifp, group),
 and bump its reference count.
Find and return a reference to an in6_multi record for ( ifp group ) and bump its reference count .
/*
	 * XXX: Accesses to ifma_protospec must be covered by IF_ADDR_LOCK;
	 * if_addmulti() takes this mutex itself, so we must drop and
	 * re-acquire around the call.
	 */ /*
		 * If we already joined this group, just bump the
		 * refcount and return it.
		 */ /*
	 * Check if a link-layer group is already associated
	 * with this network-layer group on the given ifnet.
	 */ /*
	 * If something other than netinet6 is occupying the link-layer
	 * group, print a meaningful error message and back out of
	 * the allocation.
	 * Otherwise, bump the refcount on the existing network-layer
	 * group association and return it.
	 */ /*
	 * A new in6_multi record is needed; allocate and initialize it.
	 * We DO NOT perform an MLD join as the in6_ layer may need to
	 * push an initial source list down to MLD to support SSM.
	 *
	 * The initial source filter state is INCLUDE, {} as per the RFC.
	 * Pending state-changes per group are subject to a bounds check.
	 */