ethertap.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
- /*
- * Ethertap: A network device for bouncing packets via user space
- *
- * This is a very simple ethernet driver. It bounces ethernet frames
- * to user space on /dev/tap0->/dev/tap15 and expects ethernet frames
- * to be written back to it. By default it does not ARP. If you turn ARP
- * on it will attempt to ARP the user space and reply to ARPS from the
- * user space.
- *
- * As this is an ethernet device you can use it for appletalk, IPX etc
- * even for building bridging tunnels.
- */
-
- #include <linux/config.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #include <linux/errno.h>
- #include <linux/netdevice.h>
- #include <linux/inetdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/skbuff.h>
- #include <linux/init.h>
- #include <net/sock.h>
- #include <linux/netlink.h>
- /*
- * Index to functions.
- */
- int ethertap_probe(struct net_device *dev);
- static int ethertap_open(struct net_device *dev);
- static int ethertap_start_xmit(struct sk_buff *skb, struct net_device *dev);
- static int ethertap_close(struct net_device *dev);
- static struct net_device_stats *ethertap_get_stats(struct net_device *dev);
- static void ethertap_rx(struct sock *sk, int len);
- #ifdef CONFIG_ETHERTAP_MC
- static void set_multicast_list(struct net_device *dev);
- #endif
- static int ethertap_debug;
- static struct net_device *tap_map[32]; /* Returns the tap device for a given netlink */
- /*
- * Board-specific info in dev->priv.
- */
- struct net_local
- {
- struct sock *nl;
- #ifdef CONFIG_ETHERTAP_MC
- __u32 groups;
- #endif
- struct net_device_stats stats;
- };
- /*
- * To call this a probe is a bit misleading, however for real
- * hardware it would have to check what was present.
- */
-
- int __init ethertap_probe(struct net_device *dev)
- {
- SET_MODULE_OWNER(dev);
- memcpy(dev->dev_addr, "xFExFDx00x00x00x00", 6);
- if (dev->mem_start & 0xf)
- ethertap_debug = dev->mem_start & 0x7;
- /*
- * Initialize the device structure.
- */
- dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct net_local));
- /*
- * The tap specific entries in the device structure.
- */
- dev->open = ethertap_open;
- dev->hard_start_xmit = ethertap_start_xmit;
- dev->stop = ethertap_close;
- dev->get_stats = ethertap_get_stats;
- #ifdef CONFIG_ETHERTAP_MC
- dev->set_multicast_list = set_multicast_list;
- #endif
- /*
- * Setup the generic properties
- */
- ether_setup(dev);
- dev->tx_queue_len = 0;
- dev->flags|=IFF_NOARP;
- tap_map[dev->base_addr]=dev;
- return 0;
- }
- /*
- * Open/initialize the board.
- */
- static int ethertap_open(struct net_device *dev)
- {
- struct net_local *lp = (struct net_local*)dev->priv;
- if (ethertap_debug > 2)
- printk("%s: Doing ethertap_open()...", dev->name);
- lp->nl = netlink_kernel_create(dev->base_addr, ethertap_rx);
- if (lp->nl == NULL)
- return -ENOBUFS;
- netif_start_queue(dev);
- return 0;
- }
- #ifdef CONFIG_ETHERTAP_MC
- static unsigned ethertap_mc_hash(__u8 *dest)
- {
- unsigned idx = 0;
- idx ^= dest[0];
- idx ^= dest[1];
- idx ^= dest[2];
- idx ^= dest[3];
- idx ^= dest[4];
- idx ^= dest[5];
- return 1U << (idx&0x1F);
- }
- static void set_multicast_list(struct net_device *dev)
- {
- unsigned groups = ~0;
- struct net_local *lp = (struct net_local *)dev->priv;
- if (!(dev->flags&(IFF_NOARP|IFF_PROMISC|IFF_ALLMULTI))) {
- struct dev_mc_list *dmi;
- groups = ethertap_mc_hash(dev->broadcast);
- for (dmi=dev->mc_list; dmi; dmi=dmi->next) {
- if (dmi->dmi_addrlen != 6)
- continue;
- groups |= ethertap_mc_hash(dmi->dmi_addr);
- }
- }
- lp->groups = groups;
- if (lp->nl)
- lp->nl->protinfo.af_netlink.groups = groups;
- }
- #endif
- /*
- * We transmit by throwing the packet at netlink. We have to clone
- * it for 2.0 so that we dev_kfree_skb() the locked original.
- */
-
- static int ethertap_start_xmit(struct sk_buff *skb, struct net_device *dev)
- {
- struct net_local *lp = (struct net_local *)dev->priv;
- #ifdef CONFIG_ETHERTAP_MC
- struct ethhdr *eth = (struct ethhdr*)skb->data;
- #endif
- if (skb_headroom(skb) < 2) {
- static int once;
- struct sk_buff *skb2;
- if (!once) {
- once = 1;
- printk(KERN_DEBUG "%s: not aligned xmit by protocol %04xn", dev->name, skb->protocol);
- }
- skb2 = skb_realloc_headroom(skb, 2);
- dev_kfree_skb(skb);
- if (skb2 == NULL)
- return 0;
- skb = skb2;
- }
- __skb_push(skb, 2);
- /* Make the same thing, which loopback does. */
- if (skb_shared(skb)) {
- struct sk_buff *skb2 = skb;
- skb = skb_clone(skb, GFP_ATOMIC); /* Clone the buffer */
- if (skb==NULL) {
- dev_kfree_skb(skb2);
- return 0;
- }
- dev_kfree_skb(skb2);
- }
- /* ... but do not orphan it here, netlink does it in any case. */
- lp->stats.tx_bytes+=skb->len;
- lp->stats.tx_packets++;
- #ifndef CONFIG_ETHERTAP_MC
- netlink_broadcast(lp->nl, skb, 0, ~0, GFP_ATOMIC);
- #else
- if (dev->flags&IFF_NOARP) {
- netlink_broadcast(lp->nl, skb, 0, ~0, GFP_ATOMIC);
- return 0;
- }
- if (!(eth->h_dest[0]&1)) {
- /* Unicast packet */
- __u32 pid;
- memcpy(&pid, eth->h_dest+2, 4);
- netlink_unicast(lp->nl, skb, ntohl(pid), MSG_DONTWAIT);
- } else
- netlink_broadcast(lp->nl, skb, 0, ethertap_mc_hash(eth->h_dest), GFP_ATOMIC);
- #endif
- return 0;
- }
- static __inline__ int ethertap_rx_skb(struct sk_buff *skb, struct net_device *dev)
- {
- struct net_local *lp = (struct net_local *)dev->priv;
- #ifdef CONFIG_ETHERTAP_MC
- struct ethhdr *eth = (struct ethhdr*)(skb->data + 2);
- #endif
- int len = skb->len;
- if (len < 16) {
- printk(KERN_DEBUG "%s : rx len = %dn", dev->name, len);
- kfree_skb(skb);
- return -EINVAL;
- }
- if (NETLINK_CREDS(skb)->uid) {
- printk(KERN_INFO "%s : user %dn", dev->name, NETLINK_CREDS(skb)->uid);
- kfree_skb(skb);
- return -EPERM;
- }
- #ifdef CONFIG_ETHERTAP_MC
- if (!(dev->flags&(IFF_NOARP|IFF_PROMISC))) {
- int drop = 0;
- if (eth->h_dest[0]&1) {
- if (!(ethertap_mc_hash(eth->h_dest)&lp->groups))
- drop = 1;
- } else if (memcmp(eth->h_dest, dev->dev_addr, 6) != 0)
- drop = 1;
- if (drop) {
- if (ethertap_debug > 3)
- printk(KERN_DEBUG "%s : not for usn", dev->name);
- kfree_skb(skb);
- return -EINVAL;
- }
- }
- #endif
- if (skb_shared(skb)) {
- struct sk_buff *skb2 = skb;
- skb = skb_clone(skb, GFP_KERNEL); /* Clone the buffer */
- if (skb==NULL) {
- kfree_skb(skb2);
- return -ENOBUFS;
- }
- kfree_skb(skb2);
- } else
- skb_orphan(skb);
- skb_pull(skb, 2);
- skb->dev = dev;
- skb->protocol=eth_type_trans(skb,dev);
- memset(skb->cb, 0, sizeof(skb->cb));
- lp->stats.rx_packets++;
- lp->stats.rx_bytes+=len;
- netif_rx(skb);
- dev->last_rx = jiffies;
- return len;
- }
- /*
- * The typical workload of the driver:
- * Handle the ether interface interrupts.
- *
- * (In this case handle the packets posted from user space..)
- */
- static void ethertap_rx(struct sock *sk, int len)
- {
- struct net_device *dev = tap_map[sk->protocol];
- struct sk_buff *skb;
- if (dev==NULL) {
- printk(KERN_CRIT "ethertap: bad unit!n");
- skb_queue_purge(&sk->receive_queue);
- return;
- }
- if (ethertap_debug > 3)
- printk("%s: ethertap_rx()n", dev->name);
- while ((skb = skb_dequeue(&sk->receive_queue)) != NULL)
- ethertap_rx_skb(skb, dev);
- }
- static int ethertap_close(struct net_device *dev)
- {
- struct net_local *lp = (struct net_local *)dev->priv;
- struct sock *sk = lp->nl;
- if (ethertap_debug > 2)
- printk("%s: Shutting down.n", dev->name);
- netif_stop_queue(dev);
- if (sk) {
- lp->nl = NULL;
- sock_release(sk->socket);
- }
- return 0;
- }
- static struct net_device_stats *ethertap_get_stats(struct net_device *dev)
- {
- struct net_local *lp = (struct net_local *)dev->priv;
- return &lp->stats;
- }
- #ifdef MODULE
- static int unit;
- MODULE_PARM(unit,"i");
- MODULE_PARM_DESC(unit,"Ethertap device number");
- static struct net_device dev_ethertap =
- {
- " ",
- 0, 0, 0, 0,
- 1, 5,
- 0, 0, 0, NULL, ethertap_probe
- };
- int init_module(void)
- {
- dev_ethertap.base_addr=unit+NETLINK_TAPBASE;
- sprintf(dev_ethertap.name,"tap%d",unit);
- if (dev_get(dev_ethertap.name))
- {
- printk(KERN_INFO "%s already loaded.n", dev_ethertap.name);
- return -EBUSY;
- }
- if (register_netdev(&dev_ethertap) != 0)
- return -EIO;
- return 0;
- }
- void cleanup_module(void)
- {
- tap_map[dev_ethertap.base_addr]=NULL;
- unregister_netdev(&dev_ethertap);
- /*
- * Free up the private structure.
- */
- kfree(dev_ethertap.priv);
- dev_ethertap.priv = NULL; /* gets re-allocated by ethertap_probe */
- }
- #endif /* MODULE */
- MODULE_LICENSE("GPL");