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

📄 igmp.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *	Linux NET3:	Internet Group Management Protocol  [IGMP] * *	This code implements the IGMP protocol as defined in RFC1112. There has *	been a further revision of this protocol since which is now supported. * *	If you have trouble with this module be careful what gcc you have used, *	the older version didn't come out right using gcc 2.5.8, the newer one *	seems to fall out with gcc 2.6.2. * *	Version: $Id: igmp.c,v 1.47 2002/02/01 22:01:03 davem Exp $ * *	Authors: *		Alan Cox <Alan.Cox@linux.org> * *	This program is free software; you can redistribute it and/or *	modify it under the terms of the GNU General Public License *	as published by the Free Software Foundation; either version *	2 of the License, or (at your option) any later version. * *	Fixes: * *		Alan Cox	:	Added lots of __inline__ to optimise *					the memory usage of all the tiny little *					functions. *		Alan Cox	:	Dumped the header building experiment. *		Alan Cox	:	Minor tweaks ready for multicast routing *					and extended IGMP protocol. *		Alan Cox	:	Removed a load of inline directives. Gcc 2.5.8 *					writes utterly bogus code otherwise (sigh) *					fixed IGMP loopback to behave in the manner *					desired by mrouted, fixed the fact it has been *					broken since 1.3.6 and cleaned up a few minor *					points. * *		Chih-Jen Chang	:	Tried to revise IGMP to Version 2 *		Tsu-Sheng Tsao		E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu *					The enhancements are mainly based on Steve Deering's  * 					ipmulti-3.5 source code. *		Chih-Jen Chang	:	Added the igmp_get_mrouter_info and *		Tsu-Sheng Tsao		igmp_set_mrouter_info to keep track of *					the mrouted version on that device. *		Chih-Jen Chang	:	Added the max_resp_time parameter to *		Tsu-Sheng Tsao		igmp_heard_query(). Using this parameter *					to identify the multicast router version *					and do what the IGMP version 2 specified. *		Chih-Jen Chang	:	Added a timer to revert to IGMP V2 router *		Tsu-Sheng Tsao		if the specified time expired. *		Alan Cox	:	Stop IGMP from 0.0.0.0 being accepted. *		Alan Cox	:	Use GFP_ATOMIC in the right places. *		Christian Daudt :	igmp timer wasn't set for local group *					memberships but was being deleted,  *					which caused a "del_timer() called  *					from %p with timer not initialized\n" *					message (960131). *		Christian Daudt :	removed del_timer from  *					igmp_timer_expire function (960205). *             Christian Daudt :       igmp_heard_report now only calls *                                     igmp_timer_expire if tm->running is *                                     true (960216). *		Malcolm Beattie :	ttl comparison wrong in igmp_rcv made *					igmp_heard_query never trigger. Expiry *					miscalculation fixed in igmp_heard_query *					and random() made to return unsigned to *					prevent negative expiry times. *		Alexey Kuznetsov:	Wrong group leaving behaviour, backport *					fix from pending 2.1.x patches. *		Alan Cox:		Forget to enable FDDI support earlier. *		Alexey Kuznetsov:	Fixed leaving groups on device down. *		Alexey Kuznetsov:	Accordance to igmp-v2-06 draft. *		David L Stevens:	IGMPv3 support, with help from *					Vinay Kulkarni */#include <linux/config.h>#include <linux/module.h>#include <asm/uaccess.h>#include <asm/system.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/jiffies.h>#include <linux/string.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/inetdevice.h>#include <linux/igmp.h>#include <linux/if_arp.h>#include <linux/rtnetlink.h>#include <linux/times.h>#include <net/ip.h>#include <net/protocol.h>#include <net/route.h>#include <net/sock.h>#include <net/checksum.h>#include <linux/netfilter_ipv4.h>#ifdef CONFIG_IP_MROUTE#include <linux/mroute.h>#endif#ifdef CONFIG_PROC_FS#include <linux/proc_fs.h>#include <linux/seq_file.h>#endif#define IP_MAX_MEMBERSHIPS	20#define IP_MAX_MSF		10#ifdef CONFIG_IP_MULTICAST/* Parameter names and values are taken from igmp-v2-06 draft */#define IGMP_V1_Router_Present_Timeout		(400*HZ)#define IGMP_V2_Router_Present_Timeout		(400*HZ)#define IGMP_Unsolicited_Report_Interval	(10*HZ)#define IGMP_Query_Response_Interval		(10*HZ)#define IGMP_Unsolicited_Report_Count		2#define IGMP_Initial_Report_Delay		(1)/* IGMP_Initial_Report_Delay is not from IGMP specs! * IGMP specs require to report membership immediately after * joining a group, but we delay the first report by a * small interval. It seems more natural and still does not * contradict to specs provided this delay is small enough. */#define IGMP_V1_SEEN(in_dev) (ipv4_devconf.force_igmp_version == 1 || \		(in_dev)->cnf.force_igmp_version == 1 || \		((in_dev)->mr_v1_seen && \		time_before(jiffies, (in_dev)->mr_v1_seen)))#define IGMP_V2_SEEN(in_dev) (ipv4_devconf.force_igmp_version == 2 || \		(in_dev)->cnf.force_igmp_version == 2 || \		((in_dev)->mr_v2_seen && \		time_before(jiffies, (in_dev)->mr_v2_seen)))static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr);static void igmpv3_clear_delrec(struct in_device *in_dev);static int sf_setstate(struct ip_mc_list *pmc);static void sf_markstate(struct ip_mc_list *pmc);#endifstatic void ip_mc_clear_src(struct ip_mc_list *pmc);int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,	int sfcount, __u32 *psfsrc, int delta);static void ip_ma_put(struct ip_mc_list *im){	if (atomic_dec_and_test(&im->refcnt)) {		in_dev_put(im->interface);		kfree(im);	}}#ifdef CONFIG_IP_MULTICAST/* *	Timer management */static __inline__ void igmp_stop_timer(struct ip_mc_list *im){	spin_lock_bh(&im->lock);	if (del_timer(&im->timer))		atomic_dec(&im->refcnt);	im->tm_running=0;	im->reporter = 0;	im->unsolicit_count = 0;	spin_unlock_bh(&im->lock);}/* It must be called with locked im->lock */static void igmp_start_timer(struct ip_mc_list *im, int max_delay){	int tv=net_random() % max_delay;	im->tm_running=1;	if (!mod_timer(&im->timer, jiffies+tv+2))		atomic_inc(&im->refcnt);}static void igmp_gq_start_timer(struct in_device *in_dev){	int tv = net_random() % in_dev->mr_maxdelay;	in_dev->mr_gq_running = 1;	if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2))		in_dev_hold(in_dev);}static void igmp_ifc_start_timer(struct in_device *in_dev, int delay){	int tv = net_random() % delay;	if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2))		in_dev_hold(in_dev);}static void igmp_mod_timer(struct ip_mc_list *im, int max_delay){	spin_lock_bh(&im->lock);	im->unsolicit_count = 0;	if (del_timer(&im->timer)) {		if ((long)(im->timer.expires-jiffies) < max_delay) {			add_timer(&im->timer);			im->tm_running=1;			spin_unlock_bh(&im->lock);			return;		}		atomic_dec(&im->refcnt);	}	igmp_start_timer(im, max_delay);	spin_unlock_bh(&im->lock);}/* *	Send an IGMP report. */#define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type,	int gdeleted, int sdeleted){	switch (type) {	case IGMPV3_MODE_IS_INCLUDE:	case IGMPV3_MODE_IS_EXCLUDE:		if (gdeleted || sdeleted)			return 0;		return !(pmc->gsquery && !psf->sf_gsresp);	case IGMPV3_CHANGE_TO_INCLUDE:		if (gdeleted || sdeleted)			return 0;		return psf->sf_count[MCAST_INCLUDE] != 0;	case IGMPV3_CHANGE_TO_EXCLUDE:		if (gdeleted || sdeleted)			return 0;		if (pmc->sfcount[MCAST_EXCLUDE] == 0 ||		    psf->sf_count[MCAST_INCLUDE])			return 0;		return pmc->sfcount[MCAST_EXCLUDE] ==			psf->sf_count[MCAST_EXCLUDE];	case IGMPV3_ALLOW_NEW_SOURCES:		if (gdeleted || !psf->sf_crcount)			return 0;		return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted;	case IGMPV3_BLOCK_OLD_SOURCES:		if (pmc->sfmode == MCAST_INCLUDE)			return gdeleted || (psf->sf_crcount && sdeleted);		return psf->sf_crcount && !gdeleted && !sdeleted;	}	return 0;}static intigmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted){	struct ip_sf_list *psf;	int scount = 0;	for (psf=pmc->sources; psf; psf=psf->sf_next) {		if (!is_in(pmc, psf, type, gdeleted, sdeleted))			continue;		scount++;	}	return scount;}static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size){	struct sk_buff *skb;	struct rtable *rt;	struct iphdr *pip;	struct igmpv3_report *pig;	skb = alloc_skb(size + LL_RESERVED_SPACE(dev), GFP_ATOMIC);	if (skb == NULL)		return NULL;	{		struct flowi fl = { .oif = dev->ifindex,				    .nl_u = { .ip4_u = {				    .daddr = IGMPV3_ALL_MCR } },				    .proto = IPPROTO_IGMP };		if (ip_route_output_key(&rt, &fl)) {			kfree_skb(skb);			return NULL;		}	}	if (rt->rt_src == 0) {		kfree_skb(skb);		ip_rt_put(rt);		return NULL;	}	skb->dst = &rt->u.dst;	skb->dev = dev;	skb_reserve(skb, LL_RESERVED_SPACE(dev));	skb->nh.iph = pip =(struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);	pip->version  = 4;	pip->ihl      = (sizeof(struct iphdr)+4)>>2;	pip->tos      = 0xc0;	pip->frag_off = htons(IP_DF);	pip->ttl      = 1;	pip->daddr    = rt->rt_dst;	pip->saddr    = rt->rt_src;	pip->protocol = IPPROTO_IGMP;	pip->tot_len  = 0;	/* filled in later */	ip_select_ident(pip, &rt->u.dst, NULL);	((u8*)&pip[1])[0] = IPOPT_RA;	((u8*)&pip[1])[1] = 4;	((u8*)&pip[1])[2] = 0;	((u8*)&pip[1])[3] = 0;	pig =(struct igmpv3_report *)skb_put(skb, sizeof(*pig));	skb->h.igmph = (struct igmphdr *)pig;	pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT;	pig->resv1 = 0;	pig->csum = 0;	pig->resv2 = 0;	pig->ngrec = 0;	return skb;}static int igmpv3_sendpack(struct sk_buff *skb){	struct iphdr *pip = skb->nh.iph;	struct igmphdr *pig = skb->h.igmph;	int iplen, igmplen;	iplen = skb->tail - (unsigned char *)skb->nh.iph;	pip->tot_len = htons(iplen);	ip_send_check(pip);	igmplen = skb->tail - (unsigned char *)skb->h.igmph;	pig->csum = ip_compute_csum((void *)skb->h.igmph, igmplen);	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev,		       dst_output);}static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel){	return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc,type,gdel,sdel);}static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,	int type, struct igmpv3_grec **ppgr){	struct net_device *dev = pmc->interface->dev;	struct igmpv3_report *pih;	struct igmpv3_grec *pgr;	if (!skb)		skb = igmpv3_newpack(dev, dev->mtu);	if (!skb)		return NULL;	pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));	pgr->grec_type = type;	pgr->grec_auxwords = 0;	pgr->grec_nsrcs = 0;	pgr->grec_mca = pmc->multiaddr;	pih = (struct igmpv3_report *)skb->h.igmph;	pih->ngrec = htons(ntohs(pih->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 ip_mc_list *pmc,	int type, int gdeleted, int sdeleted){	struct net_device *dev = pmc->interface->dev;	struct igmpv3_report *pih;	struct igmpv3_grec *pgr = NULL;	struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;	int scount, first, isquery, truncate;	if (pmc->multiaddr == IGMP_ALL_HOSTS)		return skb;	isquery = type == IGMPV3_MODE_IS_INCLUDE ||		  type == IGMPV3_MODE_IS_EXCLUDE;	truncate = type == IGMPV3_MODE_IS_EXCLUDE ||		    type == IGMPV3_CHANGE_TO_EXCLUDE;	psf_list = sdeleted ? &pmc->tomb : &pmc->sources;	if (!*psf_list) {		if (type == IGMPV3_ALLOW_NEW_SOURCES ||		    type == IGMPV3_BLOCK_OLD_SOURCES)			return skb;		if (pmc->crcount || isquery) {			/* make sure we have room for group header and at			 * least one source.			 */			if (skb && AVAILABLE(skb) < sizeof(struct igmpv3_grec)+			    sizeof(__u32)) {				igmpv3_sendpack(skb);				skb = NULL; /* add_grhead will get a new one */			}			skb = add_grhead(skb, pmc, type, &pgr);		}		return skb;	}	pih = skb ? (struct igmpv3_report *)skb->h.igmph : NULL;	/* EX and TO_EX get a fresh packet, if needed */	if (truncate) {		if (pih && pih->ngrec &&		    AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {			if (skb)				igmpv3_sendpack(skb);			skb = igmpv3_newpack(dev, dev->mtu);		}	}	first = 1;	scount = 0;	psf_prev = NULL;	for (psf=*psf_list; psf; psf=psf_next) {		u32 *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(u32) +		    first*sizeof(struct igmpv3_grec)) {			if (truncate && !first)				break;	 /* truncate these */			if (pgr)				pgr->grec_nsrcs = htons(scount);			if (skb)				igmpv3_sendpack(skb);			skb = igmpv3_newpack(dev, dev->mtu);			first = 1;			scount = 0;		}		if (first) {			skb = add_grhead(skb, pmc, type, &pgr);			first = 0;		}		psrc = (u32 *)skb_put(skb, sizeof(u32));		*psrc = psf->sf_inaddr;		scount++;		if ((type == IGMPV3_ALLOW_NEW_SOURCES ||		     type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) {			psf->sf_crcount--;			if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {				if (psf_prev)					psf_prev->sf_next = psf->sf_next;				else					*psf_list = psf->sf_next;				kfree(psf);				continue;			}		}		psf_prev = psf;	}	if (pgr)		pgr->grec_nsrcs = htons(scount);	if (isquery)		pmc->gsquery = 0;	/* clear query state on report */	return skb;}static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc){	struct sk_buff *skb = NULL;	int type;	if (!pmc) {		read_lock(&in_dev->mc_list_lock);		for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {			if (pmc->multiaddr == IGMP_ALL_HOSTS)				continue;			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);		}		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) {			pmc->crcount--;			if (pmc->sfmode == MCAST_EXCLUDE) {				type = IGMPV3_CHANGE_TO_INCLUDE;				skb = add_grec(skb, pmc, type, 1, 0);			}			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) {			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);		}		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;	u32	group = pmc ? pmc->multiaddr : 0;

⌨️ 快捷键说明

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