1909 lines · 42.3 KB · C
RawDownload
/* *	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.65 2001/10/31 21:55:54 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 <asm/system.h>#include <asm/uaccess.h>#include <linux/types.h>#include <linux/capability.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/seq_file.h>#include <linux/mroute.h>#include <linux/init.h>#include <linux/if_ether.h>#include <net/ip.h>#include <net/protocol.h>#include <linux/skbuff.h>#include <net/route.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/netfilter_ipv4.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 static struct sock *mroute_socket;  /* Big lock, protecting vif table, mrt cache and mroute socket state.   Note that the changes are semaphored via rtnl_lock. */ static DEFINE_RWLOCK(mrt_lock); /* *	Multicast router control variables */ static struct vif_device vif_table[MAXVIFS];		/* Devices 		*/static int maxvif; #define VIF_EXISTS(idx) (vif_table[idx].dev != NULL) static int mroute_do_assert;				/* Set in PIM assert	*/static int mroute_do_pim; static struct mfc_cache *mfc_cache_array[MFC_LINES];	/* Forwarding cache	*/ static struct mfc_cache *mfc_unres_queue;		/* Queue of unresolved entries */static atomic_t cache_resolve_queue_len;		/* Size of unresolved	*/ /* Special spinlock for queue of unresolved entries */static DEFINE_SPINLOCK(mfc_unres_lock); /* We return to original Alan's scheme. Hash table of resolved   entries is changed only in process context and protected   with weak lock mrt_lock. Queue of unresolved entries is protected   with strong spinlock mfc_unres_lock.    In this case data path is free of exclusive locks at all. */ static struct kmem_cache *mrt_cachep __read_mostly; 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); #ifdef CONFIG_IP_PIMSM_V2static struct net_protocol pim_protocol;#endif static struct timer_list ipmr_expire_timer; /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ staticstruct net_device *ipmr_new_tunnel(struct vifctl *v){	struct net_device  *dev; 	dev = __dev_get_by_name("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); 		dev = NULL; 		if (err == 0 && (dev = __dev_get_by_name(p.name)) != NULL) {			dev->flags |= IFF_MULTICAST; 			in_dev = __in_dev_get_rtnl(dev);			if (in_dev == NULL && (in_dev = inetdev_init(dev)) == NULL)				goto failure;			in_dev->cnf.rp_filter = 0; 			if (dev_open(dev))				goto failure;		}	}	return dev; failure:	/* allow the register to be completed before unregistering. */	rtnl_unlock();	rtnl_lock(); 	unregister_netdevice(dev);	return NULL;} #ifdef CONFIG_IP_PIMSM static int reg_vif_num = -1; static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev){	read_lock(&mrt_lock);	((struct net_device_stats*)netdev_priv(dev))->tx_bytes += skb->len;	((struct net_device_stats*)netdev_priv(dev))->tx_packets++;	ipmr_cache_report(skb, reg_vif_num, IGMPMSG_WHOLEPKT);	read_unlock(&mrt_lock);	kfree_skb(skb);	return 0;} static struct net_device_stats *reg_vif_get_stats(struct net_device *dev){	return (struct net_device_stats*)netdev_priv(dev);} static void reg_vif_setup(struct net_device *dev){	dev->type		= ARPHRD_PIMREG;	dev->mtu		= ETH_DATA_LEN - sizeof(struct iphdr) - 8;	dev->flags		= IFF_NOARP;	dev->hard_start_xmit	= reg_vif_xmit;	dev->get_stats		= reg_vif_get_stats;	dev->destructor		= free_netdev;} static struct net_device *ipmr_reg_vif(void){	struct net_device *dev;	struct in_device *in_dev; 	dev = alloc_netdev(sizeof(struct net_device_stats), "pimreg",			   reg_vif_setup); 	if (dev == NULL)		return NULL; 	if (register_netdevice(dev)) {		free_netdev(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; 	return dev; failure:	/* allow the register to be completed before unregistering. */	rtnl_unlock();	rtnl_lock(); 	unregister_netdevice(dev);	return NULL;}#endif /* *	Delete a VIF entry */ static int vif_delete(int vifi){	struct vif_device *v;	struct net_device *dev;	struct in_device *in_dev; 	if (vifi < 0 || vifi >= maxvif)		return -EADDRNOTAVAIL; 	v = &vif_table[vifi]; 	write_lock_bh(&mrt_lock);	dev = v->dev;	v->dev = NULL; 	if (!dev) {		write_unlock_bh(&mrt_lock);		return -EADDRNOTAVAIL;	} #ifdef CONFIG_IP_PIMSM	if (vifi == reg_vif_num)		reg_vif_num = -1;#endif 	if (vifi+1 == maxvif) {		int tmp;		for (tmp=vifi-1; tmp>=0; tmp--) {			if (VIF_EXISTS(tmp))				break;		}		maxvif = tmp+1;	} 	write_unlock_bh(&mrt_lock); 	dev_set_allmulti(dev, -1); 	if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {		in_dev->cnf.mc_forwarding--;		ip_rt_multicast_event(in_dev);	} 	if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))		unregister_netdevice(dev); 	dev_put(dev);	return 0;} /* Destroy an unresolved cache entry, killing queued skbs   and reporting error to netlink readers. */ static void ipmr_destroy_unres(struct mfc_cache *c){	struct sk_buff *skb;	struct nlmsgerr *e; 	atomic_dec(&cache_resolve_queue_len); 	while((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) {		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);			e = NLMSG_DATA(nlh);			e->error = -ETIMEDOUT;			memset(&e->msg, 0, sizeof(e->msg)); 			rtnl_unicast(skb, NETLINK_CB(skb).pid);		} else			kfree_skb(skb);	} 	kmem_cache_free(mrt_cachep, c);}  /* Single timer process for all the unresolved queue. */ static void ipmr_expire_process(unsigned long dummy){	unsigned long now;	unsigned long expires;	struct mfc_cache *c, **cp; 	if (!spin_trylock(&mfc_unres_lock)) {		mod_timer(&ipmr_expire_timer, jiffies+HZ/10);		return;	} 	if (atomic_read(&cache_resolve_queue_len) == 0)		goto out; 	now = jiffies;	expires = 10*HZ;	cp = &mfc_unres_queue; 	while ((c=*cp) != NULL) {		if (time_after(c->mfc_un.unres.expires, now)) {			unsigned long interval = c->mfc_un.unres.expires - now;			if (interval < expires)				expires = interval;			cp = &c->next;			continue;		} 		*cp = c->next; 		ipmr_destroy_unres(c);	} 	if (atomic_read(&cache_resolve_queue_len))		mod_timer(&ipmr_expire_timer, jiffies + expires); out:	spin_unlock(&mfc_unres_lock);} /* Fill oifs list. It is called under write locked mrt_lock. */ static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls){	int vifi; 	cache->mfc_un.res.minvif = MAXVIFS;	cache->mfc_un.res.maxvif = 0;	memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 	for (vifi=0; vifi<maxvif; vifi++) {		if (VIF_EXISTS(vifi) && ttls[vifi] && ttls[vifi] < 255) {			cache->mfc_un.res.ttls[vifi] = ttls[vifi];			if (cache->mfc_un.res.minvif > vifi)				cache->mfc_un.res.minvif = vifi;			if (cache->mfc_un.res.maxvif <= vifi)				cache->mfc_un.res.maxvif = vifi + 1;		}	}} static int vif_add(struct vifctl *vifc, int mrtsock){	int vifi = vifc->vifc_vifi;	struct vif_device *v = &vif_table[vifi];	struct net_device *dev;	struct in_device *in_dev; 	/* Is vif busy ? */	if (VIF_EXISTS(vifi))		return -EADDRINUSE; 	switch (vifc->vifc_flags) {#ifdef CONFIG_IP_PIMSM	case VIFF_REGISTER:		/*		 * Special Purpose VIF in PIM		 * All the packets will be sent to the daemon		 */		if (reg_vif_num >= 0)			return -EADDRINUSE;		dev = ipmr_reg_vif();		if (!dev)			return -ENOBUFS;		break;#endif	case VIFF_TUNNEL:		dev = ipmr_new_tunnel(vifc);		if (!dev)			return -ENOBUFS;		break;	case 0:		dev = ip_dev_find(vifc->vifc_lcl_addr.s_addr);		if (!dev)			return -EADDRNOTAVAIL;		dev_put(dev);		break;	default:		return -EINVAL;	} 	if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)		return -EADDRNOTAVAIL;	in_dev->cnf.mc_forwarding++;	dev_set_allmulti(dev, +1);	ip_rt_multicast_event(in_dev); 	/*	 *	Fill in the VIF structures	 */	v->rate_limit=vifc->vifc_rate_limit;	v->local=vifc->vifc_lcl_addr.s_addr;	v->remote=vifc->vifc_rmt_addr.s_addr;	v->flags=vifc->vifc_flags;	if (!mrtsock)		v->flags |= VIFF_STATIC;	v->threshold=vifc->vifc_threshold;	v->bytes_in = 0;	v->bytes_out = 0;	v->pkt_in = 0;	v->pkt_out = 0;	v->link = dev->ifindex;	if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))		v->link = dev->iflink; 	/* And finish update writing critical data */	write_lock_bh(&mrt_lock);	dev_hold(dev);	v->dev=dev;#ifdef CONFIG_IP_PIMSM	if (v->flags&VIFF_REGISTER)		reg_vif_num = vifi;#endif	if (vifi+1 > maxvif)		maxvif = vifi+1;	write_unlock_bh(&mrt_lock);	return 0;} static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp){	int line=MFC_HASH(mcastgrp,origin);	struct mfc_cache *c; 	for (c=mfc_cache_array[line]; c; c = c->next) {		if (c->mfc_origin==origin && c->mfc_mcastgrp==mcastgrp)			break;	}	return c;} /* *	Allocate a multicast cache entry */static struct mfc_cache *ipmr_cache_alloc(void){	struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);	if(c==NULL)		return NULL;	c->mfc_un.res.minvif = MAXVIFS;	return c;} static struct mfc_cache *ipmr_cache_alloc_unres(void){	struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);	if(c==NULL)		return NULL;	skb_queue_head_init(&c->mfc_un.unres.unresolved);	c->mfc_un.unres.expires = jiffies + 10*HZ;	return c;} /* *	A cache entry has gone into a resolved state from queued */

Lines 1–500 of 1909.