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

📄 igmp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		}		read_unlock(&in_dev->mc_list_lock);	} else {		spin_lock_bh(&pmc->lock);		if (pmc->sfcount[MCAST_EXCLUDE])			type = IGMPV3_MODE_IS_EXCLUDE;		else			type = IGMPV3_MODE_IS_INCLUDE;		skb = add_grec(skb, pmc, type, 0, 0);		spin_unlock_bh(&pmc->lock);	}	if (!skb)		return 0;	return igmpv3_sendpack(skb);}/* * remove zero-count source records from a source filter list */static void igmpv3_clear_zeros(struct ip_sf_list **ppsf){	struct ip_sf_list *psf_prev, *psf_next, *psf;	psf_prev = NULL;	for (psf=*ppsf; psf; psf = psf_next) {		psf_next = psf->sf_next;		if (psf->sf_crcount == 0) {			if (psf_prev)				psf_prev->sf_next = psf->sf_next;			else				*ppsf = psf->sf_next;			kfree(psf);		} else			psf_prev = psf;	}}static void igmpv3_send_cr(struct in_device *in_dev){	struct ip_mc_list *pmc, *pmc_prev, *pmc_next;	struct sk_buff *skb = NULL;	int type, dtype;	read_lock(&in_dev->mc_list_lock);	spin_lock_bh(&in_dev->mc_tomb_lock);	/* deleted MCA's */	pmc_prev = NULL;	for (pmc=in_dev->mc_tomb; pmc; pmc=pmc_next) {		pmc_next = pmc->next;		if (pmc->sfmode == MCAST_INCLUDE) {			type = IGMPV3_BLOCK_OLD_SOURCES;			dtype = IGMPV3_BLOCK_OLD_SOURCES;			skb = add_grec(skb, pmc, type, 1, 0);			skb = add_grec(skb, pmc, dtype, 1, 1);		}		if (pmc->crcount) {			if (pmc->sfmode == MCAST_EXCLUDE) {				type = IGMPV3_CHANGE_TO_INCLUDE;				skb = add_grec(skb, pmc, type, 1, 0);			}			pmc->crcount--;			if (pmc->crcount == 0) {				igmpv3_clear_zeros(&pmc->tomb);				igmpv3_clear_zeros(&pmc->sources);			}		}		if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {			if (pmc_prev)				pmc_prev->next = pmc_next;			else				in_dev->mc_tomb = pmc_next;			in_dev_put(pmc->interface);			kfree(pmc);		} else			pmc_prev = pmc;	}	spin_unlock_bh(&in_dev->mc_tomb_lock);	/* change recs */	for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {		spin_lock_bh(&pmc->lock);		if (pmc->sfcount[MCAST_EXCLUDE]) {			type = IGMPV3_BLOCK_OLD_SOURCES;			dtype = IGMPV3_ALLOW_NEW_SOURCES;		} else {			type = IGMPV3_ALLOW_NEW_SOURCES;			dtype = IGMPV3_BLOCK_OLD_SOURCES;		}		skb = add_grec(skb, pmc, type, 0, 0);		skb = add_grec(skb, pmc, dtype, 0, 1);	/* deleted sources */		/* filter mode changes */		if (pmc->crcount) {			if (pmc->sfmode == MCAST_EXCLUDE)				type = IGMPV3_CHANGE_TO_EXCLUDE;			else				type = IGMPV3_CHANGE_TO_INCLUDE;			skb = add_grec(skb, pmc, type, 0, 0);			pmc->crcount--;		}		spin_unlock_bh(&pmc->lock);	}	read_unlock(&in_dev->mc_list_lock);	if (!skb)		return;	(void) igmpv3_sendpack(skb);}static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,	int type){	struct sk_buff *skb;	struct iphdr *iph;	struct igmphdr *ih;	struct rtable *rt;	struct net_device *dev = in_dev->dev;	__be32	group = pmc ? pmc->multiaddr : 0;	__be32	dst;	if (type == IGMPV3_HOST_MEMBERSHIP_REPORT)		return igmpv3_send_report(in_dev, pmc);	else if (type == IGMP_HOST_LEAVE_MESSAGE)		dst = IGMP_ALL_ROUTER;	else		dst = group;	{		struct flowi fl = { .oif = dev->ifindex,				    .nl_u = { .ip4_u = { .daddr = dst } },				    .proto = IPPROTO_IGMP };		if (ip_route_output_key(&rt, &fl))			return -1;	}	if (rt->rt_src == 0) {		ip_rt_put(rt);		return -1;	}	skb=alloc_skb(IGMP_SIZE+LL_RESERVED_SPACE(dev), GFP_ATOMIC);	if (skb == NULL) {		ip_rt_put(rt);		return -1;	}	skb->dst = &rt->u.dst;	skb_reserve(skb, LL_RESERVED_SPACE(dev));	skb_reset_network_header(skb);	iph = ip_hdr(skb);	skb_put(skb, sizeof(struct iphdr) + 4);	iph->version  = 4;	iph->ihl      = (sizeof(struct iphdr)+4)>>2;	iph->tos      = 0xc0;	iph->frag_off = htons(IP_DF);	iph->ttl      = 1;	iph->daddr    = dst;	iph->saddr    = rt->rt_src;	iph->protocol = IPPROTO_IGMP;	iph->tot_len  = htons(IGMP_SIZE);	ip_select_ident(iph, &rt->u.dst, NULL);	((u8*)&iph[1])[0] = IPOPT_RA;	((u8*)&iph[1])[1] = 4;	((u8*)&iph[1])[2] = 0;	((u8*)&iph[1])[3] = 0;	ip_send_check(iph);	ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));	ih->type=type;	ih->code=0;	ih->csum=0;	ih->group=group;	ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,		       dst_output);}static void igmp_gq_timer_expire(unsigned long data){	struct in_device *in_dev = (struct in_device *)data;	in_dev->mr_gq_running = 0;	igmpv3_send_report(in_dev, NULL);	__in_dev_put(in_dev);}static void igmp_ifc_timer_expire(unsigned long data){	struct in_device *in_dev = (struct in_device *)data;	igmpv3_send_cr(in_dev);	if (in_dev->mr_ifc_count) {		in_dev->mr_ifc_count--;		igmp_ifc_start_timer(in_dev, IGMP_Unsolicited_Report_Interval);	}	__in_dev_put(in_dev);}static void igmp_ifc_event(struct in_device *in_dev){	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev))		return;	in_dev->mr_ifc_count = in_dev->mr_qrv ? in_dev->mr_qrv :		IGMP_Unsolicited_Report_Count;	igmp_ifc_start_timer(in_dev, 1);}static void igmp_timer_expire(unsigned long data){	struct ip_mc_list *im=(struct ip_mc_list *)data;	struct in_device *in_dev = im->interface;	spin_lock(&im->lock);	im->tm_running=0;	if (im->unsolicit_count) {		im->unsolicit_count--;		igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);	}	im->reporter = 1;	spin_unlock(&im->lock);	if (IGMP_V1_SEEN(in_dev))		igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT);	else if (IGMP_V2_SEEN(in_dev))		igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT);	else		igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT);	ip_ma_put(im);}/* mark EXCLUDE-mode sources */static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs){	struct ip_sf_list *psf;	int i, scount;	scount = 0;	for (psf=pmc->sources; psf; psf=psf->sf_next) {		if (scount == nsrcs)			break;		for (i=0; i<nsrcs; i++) {			/* skip inactive filters */			if (pmc->sfcount[MCAST_INCLUDE] ||			    pmc->sfcount[MCAST_EXCLUDE] !=			    psf->sf_count[MCAST_EXCLUDE])				continue;			if (srcs[i] == psf->sf_inaddr) {				scount++;				break;			}		}	}	pmc->gsquery = 0;	if (scount == nsrcs)	/* all sources excluded */		return 0;	return 1;}static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs){	struct ip_sf_list *psf;	int i, scount;	if (pmc->sfmode == MCAST_EXCLUDE)		return igmp_xmarksources(pmc, nsrcs, srcs);	/* mark INCLUDE-mode sources */	scount = 0;	for (psf=pmc->sources; psf; psf=psf->sf_next) {		if (scount == nsrcs)			break;		for (i=0; i<nsrcs; i++)			if (srcs[i] == psf->sf_inaddr) {				psf->sf_gsresp = 1;				scount++;				break;			}	}	if (!scount) {		pmc->gsquery = 0;		return 0;	}	pmc->gsquery = 1;	return 1;}static void igmp_heard_report(struct in_device *in_dev, __be32 group){	struct ip_mc_list *im;	/* Timers are only set for non-local groups */	if (group == IGMP_ALL_HOSTS)		return;	read_lock(&in_dev->mc_list_lock);	for (im=in_dev->mc_list; im!=NULL; im=im->next) {		if (im->multiaddr == group) {			igmp_stop_timer(im);			break;		}	}	read_unlock(&in_dev->mc_list_lock);}static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,	int len){	struct igmphdr 		*ih = igmp_hdr(skb);	struct igmpv3_query *ih3 = igmpv3_query_hdr(skb);	struct ip_mc_list	*im;	__be32			group = ih->group;	int			max_delay;	int			mark = 0;	if (len == 8) {		if (ih->code == 0) {			/* Alas, old v1 router presents here. */			max_delay = IGMP_Query_Response_Interval;			in_dev->mr_v1_seen = jiffies +				IGMP_V1_Router_Present_Timeout;			group = 0;		} else {			/* v2 router present */			max_delay = ih->code*(HZ/IGMP_TIMER_SCALE);			in_dev->mr_v2_seen = jiffies +				IGMP_V2_Router_Present_Timeout;		}		/* cancel the interface change timer */		in_dev->mr_ifc_count = 0;		if (del_timer(&in_dev->mr_ifc_timer))			__in_dev_put(in_dev);		/* clear deleted report items */		igmpv3_clear_delrec(in_dev);	} else if (len < 12) {		return;	/* ignore bogus packet; freed by caller */	} else { /* v3 */		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))			return;		ih3 = igmpv3_query_hdr(skb);		if (ih3->nsrcs) {			if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)					   + ntohs(ih3->nsrcs)*sizeof(__be32)))				return;			ih3 = igmpv3_query_hdr(skb);		}		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);		if (!max_delay)			max_delay = 1;	/* can't mod w/ 0 */		in_dev->mr_maxdelay = max_delay;		if (ih3->qrv)			in_dev->mr_qrv = ih3->qrv;		if (!group) { /* general query */			if (ih3->nsrcs)				return;	/* no sources allowed */			igmp_gq_start_timer(in_dev);			return;		}		/* mark sources to include, if group & source-specific */		mark = ih3->nsrcs != 0;	}	/*	 * - Start the timers in all of our membership records	 *   that the query applies to for the interface on	 *   which the query arrived excl. those that belong	 *   to a "local" group (224.0.0.X)	 * - For timers already running check if they need to	 *   be reset.	 * - Use the igmp->igmp_code field as the maximum	 *   delay possible	 */	read_lock(&in_dev->mc_list_lock);	for (im=in_dev->mc_list; im!=NULL; im=im->next) {		int changed;		if (group && group != im->multiaddr)			continue;		if (im->multiaddr == IGMP_ALL_HOSTS)			continue;		spin_lock_bh(&im->lock);		if (im->tm_running)			im->gsquery = im->gsquery && mark;		else			im->gsquery = mark;		changed = !im->gsquery ||			igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);		spin_unlock_bh(&im->lock);		if (changed)			igmp_mod_timer(im, max_delay);	}	read_unlock(&in_dev->mc_list_lock);}int igmp_rcv(struct sk_buff *skb){	/* This basically follows the spec line by line -- see RFC1112 */	struct igmphdr *ih;	struct in_device *in_dev = in_dev_get(skb->dev);	int len = skb->len;	if (in_dev==NULL) {		kfree_skb(skb);		return 0;	}	if (!pskb_may_pull(skb, sizeof(struct igmphdr)))		goto drop;	switch (skb->ip_summed) {	case CHECKSUM_COMPLETE:		if (!csum_fold(skb->csum))			break;		/* fall through */	case CHECKSUM_NONE:		skb->csum = 0;		if (__skb_checksum_complete(skb))			goto drop;	}	ih = igmp_hdr(skb);	switch (ih->type) {	case IGMP_HOST_MEMBERSHIP_QUERY:		igmp_heard_query(in_dev, skb, len);		break;	case IGMP_HOST_MEMBERSHIP_REPORT:	case IGMPV2_HOST_MEMBERSHIP_REPORT:	case IGMPV3_HOST_MEMBERSHIP_REPORT:		/* Is it our report looped back? */		if (((struct rtable*)skb->dst)->fl.iif == 0)			break;		/* don't rely on MC router hearing unicast reports */		if (skb->pkt_type == PACKET_MULTICAST ||		    skb->pkt_type == PACKET_BROADCAST)			igmp_heard_report(in_dev, ih->group);		break;	case IGMP_PIM:#ifdef CONFIG_IP_PIMSM_V1		in_dev_put(in_dev);		return pim_rcv_v1(skb);#endif	case IGMP_DVMRP:	case IGMP_TRACE:	case IGMP_HOST_LEAVE_MESSAGE:	case IGMP_MTRACE:	case IGMP_MTRACE_RESP:		break;	default:		break;	}drop:	in_dev_put(in_dev);	kfree_skb(skb);	return 0;}#endif/* *	Add a filter to a device */static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr){	char buf[MAX_ADDR_LEN];	struct net_device *dev = in_dev->dev;	/* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.	   We will get multicast token leakage, when IFF_MULTICAST	   is changed. This check should be done in dev->set_multicast_list	   routine. Something sort of:	   if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }	   --ANK	   */	if (arp_mc_map(addr, buf, dev, 0) == 0)		dev_mc_add(dev,buf,dev->addr_len,0);}/* *	Remove a filter from a device */static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr){	char buf[MAX_ADDR_LEN];	struct net_device *dev = in_dev->dev;	if (arp_mc_map(addr, buf, dev, 0) == 0)		dev_mc_delete(dev,buf,dev->addr_len,0);}#ifdef CONFIG_IP_MULTICAST/* * deleted ip_mc_list manipulation */static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im){	struct ip_mc_list *pmc;	/* this is an "ip_mc_list" for convenience; only the fields below	 * are actually used. In particular, the refcnt and users are not	 * used for management of the delete list. Using the same structure	 * for deleted items allows change reports to use common code with	 * non-deleted or query-response MCA's.	 */	pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);	if (!pmc)

⌨️ 快捷键说明

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