FreeBSD/sys/netinet/in_mcast.c
/*
 * Atomically set source filters on a socket for an IPv4 multicast group.
 *
 * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held.
 */
Atomically set source filters on a socket for an IPv4 multicast group.
SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held.
Atomically set source filters on a socket for an IPv4 multicast group . SMPng : NOTE : Potentially calls malloc ( M_WAITOK ) with Giant held .
static int
inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
{
	struct __msfilterreq	 msfr;
	sockunion_t		*gsa;
	struct ifnet		*ifp;
	struct in_mfilter	*imf;
	struct ip_moptions	*imo;
	struct in_multi		*inm;
	size_t			 idx;
	int			 error;
	error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
	    sizeof(struct __msfilterreq));
	if (error)
		return (error);
	if (msfr.msfr_nsrcs > in_mcast_maxsocksrc)
		return (ENOBUFS);
	if ((msfr.msfr_fmode != MCAST_EXCLUDE &&
	     msfr.msfr_fmode != MCAST_INCLUDE))
		return (EINVAL);
	if (msfr.msfr_group.ss_family != AF_INET ||
	    msfr.msfr_group.ss_len != sizeof(struct sockaddr_in))
		return (EINVAL);
	gsa = (sockunion_t *)&msfr.msfr_group;
	if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
		return (EINVAL);
	gsa->sin.sin_port = 0;	
	if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
		return (EADDRNOTAVAIL);
	ifp = ifnet_byindex(msfr.msfr_ifindex);
	if (ifp == NULL)
		return (EADDRNOTAVAIL);
	imo = inp_findmoptions(inp);
	idx = imo_match_group(imo, ifp, &gsa->sa);
	if (idx == -1 || imo->imo_mfilters == NULL) {
		error = EADDRNOTAVAIL;
		goto out_inp_locked;
	}
	inm = imo->imo_membership[idx];
	imf = &imo->imo_mfilters[idx];
	INP_WLOCK_ASSERT(inp);
	imf->imf_st[1] = msfr.msfr_fmode;
	if (msfr.msfr_nsrcs > 0) {
		struct in_msource	*lims;
		struct sockaddr_in	*psin;
		struct sockaddr_storage	*kss, *pkss;
		int			 i;
		INP_WUNLOCK(inp);
		CTR2(KTR_IGMPV3, "%s: loading %lu source list entries",
		    __func__, (unsigned long)msfr.msfr_nsrcs);
		kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
		    M_TEMP, M_WAITOK);
		error = copyin(msfr.msfr_srcs, kss,
		    sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
		if (error) {
			free(kss, M_TEMP);
			return (error);
		}
		INP_WLOCK(inp);
		imf_leave(imf);
		imf->imf_st[1] = msfr.msfr_fmode;
		for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) {
			psin = (struct sockaddr_in *)pkss;
			if (psin->sin_family != AF_INET) {
				error = EAFNOSUPPORT;
				break;
			}
			if (psin->sin_len != sizeof(struct sockaddr_in)) {
				error = EINVAL;
				break;
			}
			error = imf_get_source(imf, psin, &lims);
			if (error)
				break;
			lims->imsl_st[1] = imf->imf_st[1];
		}
		free(kss, M_TEMP);
	}
	if (error)
		goto out_imf_rollback;
	INP_WLOCK_ASSERT(inp);
	IN_MULTI_LOCK();
	CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
	error = inm_merge(inm, imf);
	if (error) {
		CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
		goto out_in_multi_locked;
	}
	CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
	error = igmp_change_state(inm);
	if (error)
		CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
out_in_multi_locked:
	IN_MULTI_UNLOCK();
out_imf_rollback:
	if (error)
		imf_rollback(imf);
	else
		imf_commit(imf);
	imf_reap(imf);
out_inp_locked:
	INP_WUNLOCK(inp);
	return (error);
}
static int inp_set_source_filters ( struct inpcb * inp , struct sockopt * sopt ) { struct __msfilterreq msfr ; sockunion_t * gsa ; struct ifnet * ifp ; struct in_mfilter * imf ; struct ip_moptions * imo ; struct in_multi * inm ; size_t idx ; int error ; error = sooptcopyin ( sopt , & msfr , sizeof ( struct __msfilterreq ) , sizeof ( struct __msfilterreq ) ) ; if ( error ) return ( error ) ; if ( msfr . msfr_nsrcs > in_mcast_maxsocksrc ) return ( ENOBUFS ) ; if ( ( msfr . msfr_fmode != MCAST_EXCLUDE && msfr . msfr_fmode != MCAST_INCLUDE ) ) return ( EINVAL ) ; if ( msfr . msfr_group . ss_family != AF_INET || msfr . msfr_group . ss_len != sizeof ( struct sockaddr_in ) ) return ( EINVAL ) ; gsa = ( sockunion_t * ) & msfr . msfr_group ; if ( ! IN_MULTICAST ( ntohl ( gsa -> sin . sin_addr . s_addr ) ) ) return ( EINVAL ) ; gsa -> sin . sin_port = 0 ; if ( msfr . msfr_ifindex == 0 || V_if_index < msfr . msfr_ifindex ) return ( EADDRNOTAVAIL ) ; ifp = ifnet_byindex ( msfr . msfr_ifindex ) ; if ( ifp == NULL ) return ( EADDRNOTAVAIL ) ; imo = inp_findmoptions ( inp ) ; idx = imo_match_group ( imo , ifp , & gsa -> sa ) ; if ( idx == -1 || imo -> imo_mfilters == NULL ) { error = EADDRNOTAVAIL ; goto out_inp_locked ; } inm = imo -> imo_membership [ idx ] ; imf = & imo -> imo_mfilters [ idx ] ; INP_WLOCK_ASSERT ( inp ) ; imf -> imf_st [ 1 ] = msfr . msfr_fmode ; if ( msfr . msfr_nsrcs > 0 ) { struct in_msource * lims ; struct sockaddr_in * psin ; struct sockaddr_storage * kss , * pkss ; int i ; INP_WUNLOCK ( inp ) ; CTR2 ( KTR_IGMPV3 , " " , __func__ , ( unsigned long ) msfr . msfr_nsrcs ) ; kss = malloc ( sizeof ( struct sockaddr_storage ) * msfr . msfr_nsrcs , M_TEMP , M_WAITOK ) ; error = copyin ( msfr . msfr_srcs , kss , sizeof ( struct sockaddr_storage ) * msfr . msfr_nsrcs ) ; if ( error ) { free ( kss , M_TEMP ) ; return ( error ) ; } INP_WLOCK ( inp ) ; imf_leave ( imf ) ; imf -> imf_st [ 1 ] = msfr . msfr_fmode ; for ( i = 0 , pkss = kss ; i < msfr . msfr_nsrcs ; i ++ , pkss ++ ) { psin = ( struct sockaddr_in * ) pkss ; if ( psin -> sin_family != AF_INET ) { error = EAFNOSUPPORT ; break ; } if ( psin -> sin_len != sizeof ( struct sockaddr_in ) ) { error = EINVAL ; break ; } error = imf_get_source ( imf , psin , & lims ) ; if ( error ) break ; lims -> imsl_st [ 1 ] = imf -> imf_st [ 1 ] ; } free ( kss , M_TEMP ) ; } if ( error ) goto out_imf_rollback ; INP_WLOCK_ASSERT ( inp ) ; IN_MULTI_LOCK ( ) ; CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; error = inm_merge ( inm , imf ) ; if ( error ) { CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; goto out_in_multi_locked ; } CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; error = igmp_change_state ( inm ) ; if ( error ) CTR1 ( KTR_IGMPV3 , " " , __func__ ) ; out_in_multi_locked : IN_MULTI_UNLOCK ( ) ; out_imf_rollback : if ( error ) imf_rollback ( imf ) ; else imf_commit ( imf ) ; imf_reap ( imf ) ; out_inp_locked : INP_WUNLOCK ( inp ) ; return ( error ) ; }
Atomically set source filters on a socket for an IPv4 multicast group.
Atomically set source filters on a socket for an IPv4 multicast group .
/* ignore port */ /*
	 * Take the INP write lock.
	 * Check if this socket is a member of this group.
	 */ /*
	 * Begin state merge transaction at socket layer.
	 */ /*
	 * Apply any new source filters, if present.
	 * Make a copy of the user-space source vector so
	 * that we may copy them with a single copyin. This
	 * allows us to deal with page faults up-front.
	 */ /*
		 * Mark all source filters as UNDEFINED at t1.
		 * Restore new group filter mode, as imf_leave()
		 * will set it to INCLUDE.
		 */ /*
		 * Update socket layer filters at t1, lazy-allocating
		 * new entries. This saves a bunch of memory at the
		 * cost of one RB_FIND() per source entry; duplicate
		 * entries in the msfr_nsrcs vector are ignored.
		 * If we encounter an error, rollback transaction.
		 *
		 * XXX This too could be replaced with a set-symmetric
		 * difference like loop to avoid walking from root
		 * every time, as the key space is common.
		 */ /*
	 * Begin state merge transaction at IGMP layer.
	 */