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

📄 igmp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *	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/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/net_namespace.h>#include <net/arp.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_ALL(FORCE_IGMP_VERSION) == 1 || \	 IN_DEV_CONF_GET((in_dev), 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_ALL(FORCE_IGMP_VERSION) == 2 || \	 IN_DEV_CONF_GET((in_dev), 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, __be32 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);static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,			 int sfcount, __be32 *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;		if (!(pmc->gsquery && !psf->sf_gsresp)) {			if (pmc->sfmode == MCAST_INCLUDE)				return 1;			/* don't include if this source is excluded			 * in all filters			 */			if (psf->sf_count[MCAST_INCLUDE])				return type == IGMPV3_MODE_IS_INCLUDE;			return pmc->sfcount[MCAST_EXCLUDE] ==				psf->sf_count[MCAST_EXCLUDE];		}		return 0;	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_reset_network_header(skb);	pip = ip_hdr(skb);	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;	skb->transport_header = skb->network_header + sizeof(struct iphdr) + 4;	skb_put(skb, sizeof(*pig));	pig = igmpv3_report_hdr(skb);	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 = ip_hdr(skb);	struct igmphdr *pig = igmp_hdr(skb);	const int iplen = skb->tail - skb->network_header;	const int igmplen = skb->tail - skb->transport_header;	pip->tot_len = htons(iplen);	ip_send_check(pip);	pig->csum = ip_compute_csum(igmp_hdr(skb), 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 = igmpv3_report_hdr(skb);	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, stotal, 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;	stotal = scount = 0;	psf_list = sdeleted ? &pmc->tomb : &pmc->sources;	if (!*psf_list)		goto empty_source;	pih = skb ? igmpv3_report_hdr(skb) : 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;	psf_prev = NULL;	for (psf=*psf_list; psf; psf=psf_next) {		__be32 *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(__be32) +		    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;		}		if (!skb)			return NULL;		psrc = (__be32 *)skb_put(skb, sizeof(__be32));		*psrc = psf->sf_inaddr;		scount++; stotal++;		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;	}empty_source:	if (!stotal) {		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 */			if (skb && AVAILABLE(skb)<sizeof(struct igmpv3_grec)) {				igmpv3_sendpack(skb);				skb = NULL; /* add_grhead will get a new one */			}			skb = add_grhead(skb, pmc, type, &pgr);		}	}	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);

⌨️ 快捷键说明

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