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

📄 ipmr.c

📁 GNU Hurd 源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *	IP multicast routing support for mrouted 3.6/3.8 * *		(c) 1995 Alan Cox, <alan@redhat.com> *	  Linux Consultancy and Custom Driver Development * *	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. * *	Version: $Id: ipmr.c,v 1.40.2.2 1999/06/20 21:27:44 davem Exp $ * *	Fixes: *	Michael Chastain	:	Incorrect size of copying. *	Alan Cox		:	Added the cache manager code *	Alan Cox		:	Fixed the clone/copy bug and device race. *	Mike McLagan		:	Routing by source *	Malcolm Beattie		:	Buffer handling fixes. *	Alexey Kuznetsov	:	Double buffer free and other fixes. *	SVR Anand		:	Fixed several multicast bugs and problems. *	Alexey Kuznetsov	:	Status, optimisations and more. *	Brad Parker		:	Better behaviour on mrouted upcall *					overflow. *      Carlos Picoto           :       PIMv1 Support *	Pavlin Ivanov Radoslavov:	PIMv2 Registers must checksum only PIM header *					Relax this requrement to work with older peers. * */#include <linux/config.h>#include <asm/system.h>#include <asm/uaccess.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/fcntl.h>#include <linux/stat.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/inetdevice.h>#include <linux/igmp.h>#include <linux/proc_fs.h>#include <linux/mroute.h>#include <linux/init.h>#include <net/ip.h>#include <net/protocol.h>#include <linux/skbuff.h>#include <net/sock.h>#include <net/icmp.h>#include <net/udp.h>#include <net/raw.h>#include <linux/notifier.h>#include <linux/if_arp.h>#include <linux/ip_fw.h>#include <linux/firewall.h>#include <net/ipip.h>#include <net/checksum.h>#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)#define CONFIG_IP_PIMSM	1#endif/* *	Multicast router control variables */static struct vif_device vif_table[MAXVIFS];		/* Devices 		*/static unsigned long vifc_map;				/* Active device map	*/static int maxvif;int mroute_do_assert = 0;				/* Set in PIM assert	*/int mroute_do_pim = 0;static struct mfc_cache *mfc_cache_array[MFC_LINES];	/* Forwarding cache	*/int cache_resolve_queue_len = 0;			/* Size of unresolved	*/static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local);static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert);static int ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm);extern struct inet_protocol pim_protocol;staticstruct device *ipmr_new_tunnel(struct vifctl *v){	struct device  *dev = NULL;	rtnl_lock();	dev = dev_get("tunl0");	if (dev) {		int err;		struct ifreq ifr;		mm_segment_t	oldfs;		struct ip_tunnel_parm p;		struct in_device  *in_dev;		memset(&p, 0, sizeof(p));		p.iph.daddr = v->vifc_rmt_addr.s_addr;		p.iph.saddr = v->vifc_lcl_addr.s_addr;		p.iph.version = 4;		p.iph.ihl = 5;		p.iph.protocol = IPPROTO_IPIP;		sprintf(p.name, "dvmrp%d", v->vifc_vifi);		ifr.ifr_ifru.ifru_data = (void*)&p;		oldfs = get_fs(); set_fs(KERNEL_DS);		err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);		set_fs(oldfs);		if (err == 0 && (dev = dev_get(p.name)) != NULL) {			dev->flags |= IFF_MULTICAST;			in_dev = dev->ip_ptr;			if (in_dev == NULL && (in_dev = inetdev_init(dev)) == NULL)				goto failure;			in_dev->cnf.rp_filter = 0;			if (dev_open(dev))				goto failure;		}	}	rtnl_unlock();	return dev;failure:	unregister_netdevice(dev);	rtnl_unlock();	return NULL;}#ifdef CONFIG_IP_PIMSMstatic int reg_vif_num = -1;static struct device * reg_dev;static int reg_vif_xmit(struct sk_buff *skb, struct device *dev){	((struct net_device_stats*)dev->priv)->tx_bytes += skb->len;	((struct net_device_stats*)dev->priv)->tx_packets++;	ipmr_cache_report(skb, reg_vif_num, IGMPMSG_WHOLEPKT);	kfree_skb(skb);	return 0;}static struct net_device_stats *reg_vif_get_stats(struct device *dev){	return (struct net_device_stats*)dev->priv;}staticstruct device *ipmr_reg_vif(struct vifctl *v){	struct device  *dev;	struct in_device *in_dev;	int size;	size = sizeof(*dev) + IFNAMSIZ + sizeof(struct net_device_stats);	dev = kmalloc(size, GFP_KERNEL);	if (!dev)		return NULL;	memset(dev, 0, size);	dev->priv = dev + 1;	dev->name = dev->priv + sizeof(struct net_device_stats);	strcpy(dev->name, "pimreg");	dev->type		= ARPHRD_PIMREG;	dev->mtu		= 1500 - sizeof(struct iphdr) - 8;	dev->flags		= IFF_NOARP;	dev->hard_start_xmit	= reg_vif_xmit;	dev->get_stats		= reg_vif_get_stats;	rtnl_lock();	if (register_netdevice(dev)) {		rtnl_unlock();		kfree(dev);		return NULL;	}	dev->iflink = 0;	if ((in_dev = inetdev_init(dev)) == NULL)		goto failure;	in_dev->cnf.rp_filter = 0;	if (dev_open(dev))		goto failure;	rtnl_unlock();	reg_dev = dev;	return dev;failure:	unregister_netdevice(dev);	rtnl_unlock();	kfree(dev);	return NULL;}#endif/* *	Delete a VIF entry */ static int vif_delete(int vifi){	struct vif_device *v;	struct device *dev;	struct in_device *in_dev;		if (vifi < 0 || vifi >= maxvif || !(vifc_map&(1<<vifi)))		return -EADDRNOTAVAIL;	v = &vif_table[vifi];	dev = v->dev;	v->dev = NULL;	vifc_map &= ~(1<<vifi);	if ((in_dev = dev->ip_ptr) != NULL)		in_dev->cnf.mc_forwarding = 0;	dev_set_allmulti(dev, -1);	ip_rt_multicast_event(in_dev);	if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER)) {#ifdef CONFIG_IP_PIMSM		if (vifi == reg_vif_num) {			reg_vif_num = -1;			reg_dev = NULL;		}#endif		unregister_netdevice(dev);		if (v->flags&VIFF_REGISTER)			kfree(dev);	}	if (vifi+1 == maxvif) {		int tmp;		for (tmp=vifi-1; tmp>=0; tmp--) {			if (vifc_map&(1<<tmp))				break;		}		maxvif = tmp+1;	}	return 0;}static void ipmr_update_threshoulds(struct mfc_cache *cache, unsigned char *ttls){	int vifi;	start_bh_atomic();	cache->mfc_minvif = MAXVIFS;	cache->mfc_maxvif = 0;	memset(cache->mfc_ttls, 255, MAXVIFS);	for (vifi=0; vifi<maxvif; vifi++) {		if (vifc_map&(1<<vifi) && ttls[vifi] && ttls[vifi] < 255) {			cache->mfc_ttls[vifi] = ttls[vifi];			if (cache->mfc_minvif > vifi)				cache->mfc_minvif = vifi;			if (cache->mfc_maxvif <= vifi)				cache->mfc_maxvif = vifi + 1;		}	}	end_bh_atomic();}/* *	Delete a multicast route cache entry */ static void ipmr_cache_delete(struct mfc_cache *cache){	struct sk_buff *skb;	int line;	struct mfc_cache **cp;		/*	 *	Find the right cache line	 */	line=MFC_HASH(cache->mfc_mcastgrp,cache->mfc_origin);	cp=&(mfc_cache_array[line]);	if(cache->mfc_flags&MFC_QUEUED)		del_timer(&cache->mfc_timer);		/*	 *	Unlink the buffer	 */	while(*cp!=NULL)	{		if(*cp==cache)		{			*cp=cache->next;			break;		}		cp=&((*cp)->next);	}	/*	 *	Free the buffer. If it is a pending resolution	 *	clean up the other resources.	 */	if(cache->mfc_flags&MFC_QUEUED)	{		cache_resolve_queue_len--;		while((skb=skb_dequeue(&cache->mfc_unresolved))) {#ifdef CONFIG_RTNETLINK			if (skb->nh.iph->version == 0) {				struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));				nlh->nlmsg_type = NLMSG_ERROR;				nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));				skb_trim(skb, nlh->nlmsg_len);				((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -ETIMEDOUT;				netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);			} else#endif			kfree_skb(skb);		}	}	kfree_s(cache,sizeof(cache));}/* *	Cache expiry timer */	 static void ipmr_cache_timer(unsigned long data){	struct mfc_cache *cache=(struct mfc_cache *)data;	ipmr_cache_delete(cache);}/* *	Insert a multicast cache entry */static void ipmr_cache_insert(struct mfc_cache *c){	int line=MFC_HASH(c->mfc_mcastgrp,c->mfc_origin);	c->next=mfc_cache_array[line];	mfc_cache_array[line]=c;} /* *	Find a multicast cache entry */ struct mfc_cache *ipmr_cache_find(__u32 origin, __u32 mcastgrp){	int line=MFC_HASH(mcastgrp,origin);	struct mfc_cache *cache;	cache=mfc_cache_array[line];	while(cache!=NULL)	{		if(cache->mfc_origin==origin && cache->mfc_mcastgrp==mcastgrp)			return cache;		cache=cache->next;	}	return NULL;}/* *	Allocate a multicast cache entry */ static struct mfc_cache *ipmr_cache_alloc(int priority){	struct mfc_cache *c=(struct mfc_cache *)kmalloc(sizeof(struct mfc_cache), priority);	if(c==NULL)		return NULL;	memset(c, 0, sizeof(*c));	skb_queue_head_init(&c->mfc_unresolved);	init_timer(&c->mfc_timer);	c->mfc_timer.data=(long)c;	c->mfc_timer.function=ipmr_cache_timer;	c->mfc_minvif = MAXVIFS;	return c;} /* *	A cache entry has gone into a resolved state from queued */ static void ipmr_cache_resolve(struct mfc_cache *cache){	struct sk_buff *skb;	start_bh_atomic();	/*	 *	Kill the queue entry timer.	 */	del_timer(&cache->mfc_timer);	if (cache->mfc_flags&MFC_QUEUED) {		cache->mfc_flags&=~MFC_QUEUED;		cache_resolve_queue_len--;	}	end_bh_atomic();	/*	 *	Play the pending entries through our router	 */	while((skb=skb_dequeue(&cache->mfc_unresolved))) {#ifdef CONFIG_RTNETLINK		if (skb->nh.iph->version == 0) {			int err;			struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));			if (ipmr_fill_mroute(skb, cache, NLMSG_DATA(nlh)) > 0) {				nlh->nlmsg_len = skb->tail - (u8*)nlh;			} else {				nlh->nlmsg_type = NLMSG_ERROR;				nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));				skb_trim(skb, nlh->nlmsg_len);				((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -EMSGSIZE;			}			err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);		} else#endif			ip_mr_forward(skb, cache, 0);	}}/* *	Bounce a cache query up to mrouted. We could use netlink for this but mrouted *	expects the following bizarre scheme.. */ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert){	struct sk_buff *skb;	int ihl = pkt->nh.iph->ihl<<2;	struct igmphdr *igmp;	struct igmpmsg *msg;	int ret;	if (mroute_socket==NULL)		return -EINVAL;#ifdef CONFIG_IP_PIMSM	if (assert == IGMPMSG_WHOLEPKT)		skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));	else#endif		skb = alloc_skb(128, GFP_ATOMIC);	if(!skb)		return -ENOBUFS;#ifdef CONFIG_IP_PIMSM	if (assert == IGMPMSG_WHOLEPKT) {		/* Ugly, but we have no choice with this interface.		   Duplicate old header, fix ihl, length etc.		   And all this only to mangle msg->im_msgtype and		   to set msg->im_mbz to "mbz" :-)		 */		msg = (struct igmpmsg*)skb_push(skb, sizeof(struct iphdr));		skb->nh.raw = skb->h.raw = (u8*)msg;		memcpy(msg, pkt->nh.raw, sizeof(struct iphdr));		msg->im_msgtype = IGMPMSG_WHOLEPKT;		msg->im_mbz = 0; 		msg->im_vif = reg_vif_num;		skb->nh.iph->ihl = sizeof(struct iphdr) >> 2;		skb->nh.iph->tot_len = htons(ntohs(pkt->nh.iph->tot_len) + sizeof(struct iphdr));	} else #endif	{				/*	 *	Copy the IP header	 */	skb->nh.iph = (struct iphdr *)skb_put(skb, ihl);	memcpy(skb->data,pkt->data,ihl);	skb->nh.iph->protocol = 0;			/* Flag to the kernel this is a route add */	msg = (struct igmpmsg*)skb->nh.iph;	msg->im_vif = vifi;	skb->dst = dst_clone(pkt->dst);	/*	 *	Add our header	 */	igmp=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));	igmp->type	=	msg->im_msgtype = assert;	igmp->code 	=	0;	skb->nh.iph->tot_len=htons(skb->len);			/* Fix the length */	skb->h.raw = skb->nh.raw;        }		/*	 *	Deliver to mrouted	 */	if ((ret=sock_queue_rcv_skb(mroute_socket,skb))<0) {		if (net_ratelimit())			printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");		kfree_skb(skb);	}	return ret;}/* *	Queue a packet for resolution */ static int ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct sk_buff *skb){	if(cache==NULL)	{			/*		 *	Create a new entry if allowable		 */		if(cache_resolve_queue_len>=10 || (cache=ipmr_cache_alloc(GFP_ATOMIC))==NULL)		{			kfree_skb(skb);			return -ENOBUFS;

⌨️ 快捷键说明

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