⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mcast.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
static void mld_ifc_start_timer(struct inet6_dev *idev, int delay){	int tv = net_random() % delay;	if (!mod_timer(&idev->mc_ifc_timer, jiffies+tv+2))		in6_dev_hold(idev);}/* *	IGMP handling (alias multicast ICMPv6 messages) */static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime){	unsigned long delay = resptime;	/* Do not start timer for these addresses */	if (ipv6_addr_is_ll_all_nodes(&ma->mca_addr) ||	    IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL)		return;	if (del_timer(&ma->mca_timer)) {		atomic_dec(&ma->mca_refcnt);		delay = ma->mca_timer.expires - jiffies;	}	if (delay >= resptime) {		if (resptime)			delay = net_random() % resptime;		else			delay = 1;	}	ma->mca_timer.expires = jiffies + delay;	if (!mod_timer(&ma->mca_timer, jiffies + delay))		atomic_inc(&ma->mca_refcnt);	ma->mca_flags |= MAF_TIMER_RUNNING;}/* mark EXCLUDE-mode sources */static int mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs,	struct in6_addr *srcs){	struct ip6_sf_list *psf;	int i, scount;	scount = 0;	for (psf=pmc->mca_sources; psf; psf=psf->sf_next) {		if (scount == nsrcs)			break;		for (i=0; i<nsrcs; i++) {			/* skip inactive filters */			if (pmc->mca_sfcount[MCAST_INCLUDE] ||			    pmc->mca_sfcount[MCAST_EXCLUDE] !=			    psf->sf_count[MCAST_EXCLUDE])				continue;			if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) {				scount++;				break;			}		}	}	pmc->mca_flags &= ~MAF_GSQUERY;	if (scount == nsrcs)	/* all sources excluded */		return 0;	return 1;}static int mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,	struct in6_addr *srcs){	struct ip6_sf_list *psf;	int i, scount;	if (pmc->mca_sfmode == MCAST_EXCLUDE)		return mld_xmarksources(pmc, nsrcs, srcs);	/* mark INCLUDE-mode sources */	scount = 0;	for (psf=pmc->mca_sources; psf; psf=psf->sf_next) {		if (scount == nsrcs)			break;		for (i=0; i<nsrcs; i++) {			if (ipv6_addr_equal(&srcs[i], &psf->sf_addr)) {				psf->sf_gsresp = 1;				scount++;				break;			}		}	}	if (!scount) {		pmc->mca_flags &= ~MAF_GSQUERY;		return 0;	}	pmc->mca_flags |= MAF_GSQUERY;	return 1;}int igmp6_event_query(struct sk_buff *skb){	struct mld2_query *mlh2 = NULL;	struct ifmcaddr6 *ma;	struct in6_addr *group;	unsigned long max_delay;	struct inet6_dev *idev;	struct icmp6hdr *hdr;	int group_type;	int mark = 0;	int len;	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))		return -EINVAL;	/* compute payload length excluding extension headers */	len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr);	len -= skb_network_header_len(skb);	/* Drop queries with not link local source */	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL))		return -EINVAL;	idev = in6_dev_get(skb->dev);	if (idev == NULL)		return 0;	hdr = icmp6_hdr(skb);	group = (struct in6_addr *) (hdr + 1);	group_type = ipv6_addr_type(group);	if (group_type != IPV6_ADDR_ANY &&	    !(group_type&IPV6_ADDR_MULTICAST)) {		in6_dev_put(idev);		return -EINVAL;	}	if (len == 24) {		int switchback;		/* MLDv1 router present */		/* Translate milliseconds to jiffies */		max_delay = (ntohs(hdr->icmp6_maxdelay)*HZ)/1000;		switchback = (idev->mc_qrv + 1) * max_delay;		idev->mc_v1_seen = jiffies + switchback;		/* cancel the interface change timer */		idev->mc_ifc_count = 0;		if (del_timer(&idev->mc_ifc_timer))			__in6_dev_put(idev);		/* clear deleted report items */		mld_clear_delrec(idev);	} else if (len >= 28) {		int srcs_offset = sizeof(struct mld2_query) -				  sizeof(struct icmp6hdr);		if (!pskb_may_pull(skb, srcs_offset)) {			in6_dev_put(idev);			return -EINVAL;		}		mlh2 = (struct mld2_query *)skb_transport_header(skb);		max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000;		if (!max_delay)			max_delay = 1;		idev->mc_maxdelay = max_delay;		if (mlh2->qrv)			idev->mc_qrv = mlh2->qrv;		if (group_type == IPV6_ADDR_ANY) { /* general query */			if (mlh2->nsrcs) {				in6_dev_put(idev);				return -EINVAL; /* no sources allowed */			}			mld_gq_start_timer(idev);			in6_dev_put(idev);			return 0;		}		/* mark sources to include, if group & source-specific */		if (mlh2->nsrcs != 0) {			if (!pskb_may_pull(skb, srcs_offset +			    ntohs(mlh2->nsrcs) * sizeof(struct in6_addr))) {				in6_dev_put(idev);				return -EINVAL;			}			mlh2 = (struct mld2_query *)skb_transport_header(skb);			mark = 1;		}	} else {		in6_dev_put(idev);		return -EINVAL;	}	read_lock_bh(&idev->lock);	if (group_type == IPV6_ADDR_ANY) {		for (ma = idev->mc_list; ma; ma=ma->next) {			spin_lock_bh(&ma->mca_lock);			igmp6_group_queried(ma, max_delay);			spin_unlock_bh(&ma->mca_lock);		}	} else {		for (ma = idev->mc_list; ma; ma=ma->next) {			if (!ipv6_addr_equal(group, &ma->mca_addr))				continue;			spin_lock_bh(&ma->mca_lock);			if (ma->mca_flags & MAF_TIMER_RUNNING) {				/* gsquery <- gsquery && mark */				if (!mark)					ma->mca_flags &= ~MAF_GSQUERY;			} else {				/* gsquery <- mark */				if (mark)					ma->mca_flags |= MAF_GSQUERY;				else					ma->mca_flags &= ~MAF_GSQUERY;			}			if (!(ma->mca_flags & MAF_GSQUERY) ||			    mld_marksources(ma, ntohs(mlh2->nsrcs), mlh2->srcs))				igmp6_group_queried(ma, max_delay);			spin_unlock_bh(&ma->mca_lock);			break;		}	}	read_unlock_bh(&idev->lock);	in6_dev_put(idev);	return 0;}int igmp6_event_report(struct sk_buff *skb){	struct ifmcaddr6 *ma;	struct in6_addr *addrp;	struct inet6_dev *idev;	struct icmp6hdr *hdr;	int addr_type;	/* Our own report looped back. Ignore it. */	if (skb->pkt_type == PACKET_LOOPBACK)		return 0;	/* send our report if the MC router may not have heard this report */	if (skb->pkt_type != PACKET_MULTICAST &&	    skb->pkt_type != PACKET_BROADCAST)		return 0;	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))		return -EINVAL;	hdr = icmp6_hdr(skb);	/* Drop reports with not link local source */	addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);	if (addr_type != IPV6_ADDR_ANY &&	    !(addr_type&IPV6_ADDR_LINKLOCAL))		return -EINVAL;	addrp = (struct in6_addr *) (hdr + 1);	idev = in6_dev_get(skb->dev);	if (idev == NULL)		return -ENODEV;	/*	 *	Cancel the timer for this group	 */	read_lock_bh(&idev->lock);	for (ma = idev->mc_list; ma; ma=ma->next) {		if (ipv6_addr_equal(&ma->mca_addr, addrp)) {			spin_lock(&ma->mca_lock);			if (del_timer(&ma->mca_timer))				atomic_dec(&ma->mca_refcnt);			ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING);			spin_unlock(&ma->mca_lock);			break;		}	}	read_unlock_bh(&idev->lock);	in6_dev_put(idev);	return 0;}static int is_in(struct ifmcaddr6 *pmc, struct ip6_sf_list *psf, int type,	int gdeleted, int sdeleted){	switch (type) {	case MLD2_MODE_IS_INCLUDE:	case MLD2_MODE_IS_EXCLUDE:		if (gdeleted || sdeleted)			return 0;		if (!((pmc->mca_flags & MAF_GSQUERY) && !psf->sf_gsresp)) {			if (pmc->mca_sfmode == MCAST_INCLUDE)				return 1;			/* don't include if this source is excluded			 * in all filters			 */			if (psf->sf_count[MCAST_INCLUDE])				return type == MLD2_MODE_IS_INCLUDE;			return pmc->mca_sfcount[MCAST_EXCLUDE] ==				psf->sf_count[MCAST_EXCLUDE];		}		return 0;	case MLD2_CHANGE_TO_INCLUDE:		if (gdeleted || sdeleted)			return 0;		return psf->sf_count[MCAST_INCLUDE] != 0;	case MLD2_CHANGE_TO_EXCLUDE:		if (gdeleted || sdeleted)			return 0;		if (pmc->mca_sfcount[MCAST_EXCLUDE] == 0 ||		    psf->sf_count[MCAST_INCLUDE])			return 0;		return pmc->mca_sfcount[MCAST_EXCLUDE] ==			psf->sf_count[MCAST_EXCLUDE];	case MLD2_ALLOW_NEW_SOURCES:		if (gdeleted || !psf->sf_crcount)			return 0;		return (pmc->mca_sfmode == MCAST_INCLUDE) ^ sdeleted;	case MLD2_BLOCK_OLD_SOURCES:		if (pmc->mca_sfmode == MCAST_INCLUDE)			return gdeleted || (psf->sf_crcount && sdeleted);		return psf->sf_crcount && !gdeleted && !sdeleted;	}	return 0;}static intmld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted){	struct ip6_sf_list *psf;	int scount = 0;	for (psf=pmc->mca_sources; psf; psf=psf->sf_next) {		if (!is_in(pmc, psf, type, gdeleted, sdeleted))			continue;		scount++;	}	return scount;}static struct sk_buff *mld_newpack(struct net_device *dev, int size){	struct sock *sk = igmp6_socket->sk;	struct sk_buff *skb;	struct mld2_report *pmr;	struct in6_addr addr_buf;	int err;	u8 ra[8] = { IPPROTO_ICMPV6, 0,		     IPV6_TLV_ROUTERALERT, 2, 0, 0,		     IPV6_TLV_PADN, 0 };	/* we assume size > sizeof(ra) here */	skb = sock_alloc_send_skb(sk, size + LL_RESERVED_SPACE(dev), 1, &err);	if (!skb)		return NULL;	skb_reserve(skb, LL_RESERVED_SPACE(dev));	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {		/* <draft-ietf-magma-mld-source-05.txt>:		 * use unspecified address as the source address		 * when a valid link-local address is not available.		 */		memset(&addr_buf, 0, sizeof(addr_buf));	}	ip6_nd_hdr(sk, skb, dev, &addr_buf, &mld2_all_mcr, NEXTHDR_HOP, 0);	memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));	skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data);	skb_put(skb, sizeof(*pmr));	pmr = (struct mld2_report *)skb_transport_header(skb);	pmr->type = ICMPV6_MLD2_REPORT;	pmr->resv1 = 0;	pmr->csum = 0;	pmr->resv2 = 0;	pmr->ngrec = 0;	return skb;}static inline int mld_dev_queue_xmit2(struct sk_buff *skb){	struct net_device *dev = skb->dev;	unsigned char ha[MAX_ADDR_LEN];	ndisc_mc_map(&ipv6_hdr(skb)->daddr, ha, dev, 1);	if (dev_hard_header(skb, dev, ETH_P_IPV6, ha, NULL, skb->len) < 0) {		kfree_skb(skb);		return -EINVAL;	}	return dev_queue_xmit(skb);}static inline int mld_dev_queue_xmit(struct sk_buff *skb){	return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dev,		       mld_dev_queue_xmit2);}static void mld_sendpack(struct sk_buff *skb){	struct ipv6hdr *pip6 = ipv6_hdr(skb);	struct mld2_report *pmr =			      (struct mld2_report *)skb_transport_header(skb);	int payload_len, mldlen;	struct inet6_dev *idev = in6_dev_get(skb->dev);	int err;	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);	payload_len = (skb->tail - skb->network_header) - sizeof(*pip6);	mldlen = skb->tail - skb->transport_header;	pip6->payload_len = htons(payload_len);	pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,		IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb),					     mldlen, 0));	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,		mld_dev_queue_xmit);	if (!err) {		ICMP6MSGOUT_INC_STATS_BH(idev, ICMPV6_MLD2_REPORT);		ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);		IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTMCASTPKTS);	} else		IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTDISCARDS);	if (likely(idev != NULL))		in6_dev_put(idev);}static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel){	return sizeof(struct mld2_grec) + 16 * mld_scount(pmc,type,gdel,sdel);}static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,	int type, struct mld2_grec **ppgr){	struct net_device *dev = pmc->idev->dev;	struct mld2_report *pmr;	struct mld2_grec *pgr;	if (!skb)		skb = mld_newpack(dev, dev->mtu);	if (!skb)		return NULL;	pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec));	pgr->grec_type = type;	pgr->grec_auxwords = 0;	pgr->grec_nsrcs = 0;	pgr->grec_mca = pmc->mca_addr;	/* structure copy */	pmr = (struct mld2_report *)skb_transport_header(skb);	pmr->ngrec = htons(ntohs(pmr->ngrec)+1);	*ppgr = pgr;	return skb;}#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \	skb_tailroom(skb)) : 0)static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,	int type, int gdeleted, int sdeleted){	struct net_device *dev = pmc->idev->dev;	struct mld2_report *pmr;	struct mld2_grec *pgr = NULL;	struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list;	int scount, stotal, first, isquery, truncate;	if (pmc->mca_flags & MAF_NOREPORT)		return skb;	isquery = type == MLD2_MODE_IS_INCLUDE ||		  type == MLD2_MODE_IS_EXCLUDE;	truncate = type == MLD2_MODE_IS_EXCLUDE ||		    type == MLD2_CHANGE_TO_EXCLUDE;	stotal = scount = 0;	psf_list = sdeleted ? &pmc->mca_tomb : &pmc->mca_sources;	if (!*psf_list)		goto empty_source;	pmr = skb ? (struct mld2_report *)skb_transport_header(skb) : NULL;	/* EX and TO_EX get a fresh packet, if needed */	if (truncate) {		if (pmr && pmr->ngrec &&		    AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {			if (skb)				mld_sendpack(skb);			skb = mld_newpack(dev, dev->mtu);		}	}	first = 1;	psf_prev = NULL;	for (psf=*psf_list; psf; psf=psf_next) {		struct in6_addr *psrc;		psf_next = psf->sf_next;		if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {			psf_prev = psf;			continue;		}		/* clear marks on query responses */		if (isquery)			psf->sf_gsresp = 0;		if (AVAILABLE(skb) < sizeof(*psrc) +		    first*sizeof(struct mld2_grec)) {			if (truncate && !first)				break;	 /* truncate these */			if (pgr)				pgr->grec_nsrcs = htons(scount);			if (skb)				mld_sendpack(skb);			skb = mld_newpack(dev, dev->mtu);			first = 1;			scount = 0;		}		if (first) {			skb = add_grhead(skb, pmc, type, &pgr);			first = 0;		}		if (!skb)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -