/* * 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. Load more
/* * 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_V2 static struct net_protocol pim_protocol; #endif static struct timer_list ipmr_expire_timer; /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ static struct 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 */