📄 ipmr.c
字号:
/* * 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 + -