FreeBSD/sys/netinet/in_mcast.c
/*
 * Find and return a reference to an in_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 IN_MULTI lock is held across the call.
 * Return 0 if successful, otherwise return an appropriate error code.
 */
Find and return a reference to an in_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 IN_MULTI lock is held across the call.
Return 0 if successful, otherwise return an appropriate error code.
Find and return a reference to an in_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 IN_MULTI lock is held across the call . Return 0 if successful otherwise return an appropriate error code .
static int
in_getmulti(struct ifnet *ifp, const struct in_addr *group,
    struct in_multi **pinm)
{
	struct sockaddr_in	 gsin;
	struct ifmultiaddr	*ifma;
	struct in_ifinfo	*ii;
	struct in_multi		*inm;
	int error;
	IN_MULTI_LOCK_ASSERT();
	ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET];
	inm = inm_lookup(ifp, *group);
	if (inm != NULL) {
		KASSERT(inm->inm_refcount >= 1,
		    ("%s: bad refcount %d", __func__, inm->inm_refcount));
		++inm->inm_refcount;
		*pinm = inm;
		return (0);
	}
	memset(&gsin, 0, sizeof(gsin));
	gsin.sin_family = AF_INET;
	gsin.sin_len = sizeof(struct sockaddr_in);
	gsin.sin_addr = *group;
	error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma);
	if (error != 0)
		return (error);
	IF_ADDR_WLOCK(ifp);
	if (ifma->ifma_protospec != NULL) {
		inm = (struct in_multi *)ifma->ifma_protospec;
#ifdef INVARIANTS
		KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr",
		    __func__));
		KASSERT(ifma->ifma_addr->sa_family == AF_INET,
		    ("%s: ifma not AF_INET", __func__));
		KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__));
		if (inm->inm_ifma != ifma || inm->inm_ifp != ifp ||
		    !in_hosteq(inm->inm_addr, *group))
			panic("%s: ifma %p is inconsistent with %p (%s)",
			    __func__, ifma, inm, inet_ntoa(*group));
#endif
		++inm->inm_refcount;
		*pinm = inm;
		IF_ADDR_WUNLOCK(ifp);
		return (0);
	}
	IF_ADDR_WLOCK_ASSERT(ifp);
	inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO);
	if (inm == NULL) {
		IF_ADDR_WUNLOCK(ifp);
		if_delmulti_ifma(ifma);
		return (ENOMEM);
	}
	inm->inm_addr = *group;
	inm->inm_ifp = ifp;
	inm->inm_igi = ii->ii_igmp;
	inm->inm_ifma = ifma;
	inm->inm_refcount = 1;
	inm->inm_state = IGMP_NOT_MEMBER;
	mbufq_init(&inm->inm_scq, IGMP_MAX_STATE_CHANGES);
	inm->inm_st[0].iss_fmode = MCAST_UNDEFINED;
	inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
	RB_INIT(&inm->inm_srcs);
	ifma->ifma_protospec = inm;
	*pinm = inm;
	IF_ADDR_WUNLOCK(ifp);
	return (0);
}
static int in_getmulti ( struct ifnet * ifp , const struct in_addr * group , struct in_multi * * pinm ) { struct sockaddr_in gsin ; struct ifmultiaddr * ifma ; struct in_ifinfo * ii ; struct in_multi * inm ; int error ; IN_MULTI_LOCK_ASSERT ( ) ; ii = ( struct in_ifinfo * ) ifp -> if_afdata [ AF_INET ] ; inm = inm_lookup ( ifp , * group ) ; if ( inm != NULL ) { KASSERT ( inm -> inm_refcount >= 1 , ( " " , __func__ , inm -> inm_refcount ) ) ; ++ inm -> inm_refcount ; * pinm = inm ; return ( 0 ) ; } memset ( & gsin , 0 , sizeof ( gsin ) ) ; gsin . sin_family = AF_INET ; gsin . sin_len = sizeof ( struct sockaddr_in ) ; gsin . sin_addr = * group ; error = if_addmulti ( ifp , ( struct sockaddr * ) & gsin , & ifma ) ; if ( error != 0 ) return ( error ) ; IF_ADDR_WLOCK ( ifp ) ; if ( ifma -> ifma_protospec != NULL ) { inm = ( struct in_multi * ) ifma -> ifma_protospec ; #ifdef INVARIANTS KASSERT ( ifma -> ifma_addr != NULL , ( " " , __func__ ) ) ; KASSERT ( ifma -> ifma_addr -> sa_family == AF_INET , ( " " , __func__ ) ) ; KASSERT ( inm != NULL , ( " " , __func__ ) ) ; if ( inm -> inm_ifma != ifma || inm -> inm_ifp != ifp || ! in_hosteq ( inm -> inm_addr , * group ) ) panic ( " " , __func__ , ifma , inm , inet_ntoa ( * group ) ) ; #endif ++ inm -> inm_refcount ; * pinm = inm ; IF_ADDR_WUNLOCK ( ifp ) ; return ( 0 ) ; } IF_ADDR_WLOCK_ASSERT ( ifp ) ; inm = malloc ( sizeof ( * inm ) , M_IPMADDR , M_NOWAIT | M_ZERO ) ; if ( inm == NULL ) { IF_ADDR_WUNLOCK ( ifp ) ; if_delmulti_ifma ( ifma ) ; return ( ENOMEM ) ; } inm -> inm_addr = * group ; inm -> inm_ifp = ifp ; inm -> inm_igi = ii -> ii_igmp ; inm -> inm_ifma = ifma ; inm -> inm_refcount = 1 ; inm -> inm_state = IGMP_NOT_MEMBER ; mbufq_init ( & inm -> inm_scq , IGMP_MAX_STATE_CHANGES ) ; inm -> inm_st [ 0 ] . iss_fmode = MCAST_UNDEFINED ; inm -> inm_st [ 1 ] . iss_fmode = MCAST_UNDEFINED ; RB_INIT ( & inm -> inm_srcs ) ; ifma -> ifma_protospec = inm ; * pinm = inm ; IF_ADDR_WUNLOCK ( ifp ) ; return ( 0 ) ; }
Find and return a reference to an in_multi record for (ifp, group),
 and bump its reference count.
Find and return a reference to an in_multi record for ( ifp group ) and bump its reference count .
/*
		 * 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.
	 */ /* XXX ifma_protospec must be covered by IF_ADDR_LOCK */ /*
	 * If something other than netinet 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 in_multi record is needed; allocate and initialize it.
	 * We DO NOT perform an IGMP join as the in_ layer may need to
	 * push an initial source list down to IGMP to support SSM.
	 *
	 * The initial source filter state is INCLUDE, {} as per the RFC.
	 */