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

📄 tun.c

📁 实现vpn网关用来建立 IP虚拟隧道
💻 C
字号:
/* *  Universal TUN/TAP device driver. *  Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com> * *  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. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *  GNU General Public License for more details. * *  $Id: tun.c,v 1.4 2000/10/24 05:13:08 maxk Exp $ */#include <linux/module.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/malloc.h>#include <linux/poll.h>#include <linux/fcntl.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/if.h>#include <linux/if_arp.h>#include <linux/if_ether.h>#include <asm/system.h>#include <asm/uaccess.h>#include "if_tun.h"#ifdef TUN_DEBUGstatic int debug=0;#endif/* Open mask */char tun_open_mask[TUN_MAX_DEV/8];/* Network device part of the driver *//* Net device open. */static int tun_net_open(struct device *dev){#ifdef TUN_DEBUG     struct tun_struct *tun = (struct tun_struct *)dev->priv;   DBG(KERN_INFO "%s: tun_net_open\n", tun->name);#endif   dev->start = 1;   return 0;}/* Net device close. */static int tun_net_close(struct device *dev){#ifdef TUN_DEBUG     struct tun_struct *tun = (struct tun_struct *)dev->priv;   DBG(KERN_INFO "%s: tun_net_close\n", tun->name);#endif   dev->start = 0;   return 0;}/* Net device start xmit */static int tun_net_xmit(struct sk_buff *skb, struct device *dev){   struct tun_struct *tun = (struct tun_struct *)dev->priv;   DBG(KERN_INFO "%s: tun_net_xmit %d\n", tun->name, skb->len);   if( dev->tbusy )      return 1;   /* Queue frame */    skb_queue_tail(&tun->txq, skb);   if( skb_queue_len(&tun->txq) >= TUN_TXQ_SIZE )      dev->tbusy = 1;   if( tun->flags & TUN_FASYNC )#ifdef NEW_FASYNC       kill_fasync(&tun->fasync, SIGIO, POLL_IN);#else           kill_fasync(&tun->fasync, SIGIO);#endif   /* Wake up process */    wake_up_interruptible(&tun->read_wait);   return 0;}static void tun_net_mclist(struct device *dev){#ifdef TUN_DEBUG   struct tun_struct *tun = (struct tun_struct *)dev->priv;   DBG(KERN_INFO "%s: tun_net_mclist\n", tun->name);#endif   /* Nothing to do for multicast filters.     * We always accept all frames */   return;}static struct enet_statistics *tun_net_stats(struct device *dev){   struct tun_struct *tun = (struct tun_struct *)dev->priv;   return &tun->stats;}/* Initialize net device */int tun_net_init(struct device *dev){   struct tun_struct *tun = (struct tun_struct *)dev->priv;      DBG(KERN_INFO "%s: tun_net_init\n", tun->name);   dev->open = tun_net_open;   dev->hard_start_xmit = tun_net_xmit;   dev->stop = tun_net_close;   dev->get_stats = tun_net_stats;   switch( tun->flags & TUN_TYPE_MASK ) {      case TUN_TUN_DEV:         /* Point-to-Point TUN Device */	 dev->hard_header_len=0;	 dev->addr_len=0;	 dev->mtu = 1500; 	 /* Type PPP seems most suitable */	 dev->type = ARPHRD_PPP; 	 dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;	 dev->tx_queue_len = 10;         dev_init_buffers(dev);	 break;      case TUN_TAP_DEV:   	 /* Ethernet TAP Device */   	          dev->set_multicast_list = tun_net_mclist;	 /* Generate Ethernet address 	  * Should be random enough  */   	 *(unsigned short *)dev->dev_addr = htons(0x00FF);   	 *(unsigned long *)(dev->dev_addr + sizeof(short)) = 		htonl( (long)((jiffies & 0x00ffffff)<<8) | (dev->base_addr+1) );   	 ether_setup(dev);	 break;   }   return 0;}/* Character device part *//* Poll */static unsigned int tun_chr_poll(struct file *file, poll_table * wait){     struct tun_struct *tun = (struct tun_struct *)file->private_data;   DBG( KERN_INFO "%s: tun_chr_poll\n", tun->name);   poll_wait(file, &tun->read_wait, wait);    if( skb_queue_len(&tun->txq) )      return POLLIN | POLLRDNORM;   return POLLOUT | POLLWRNORM;}/* Write */static ssize_t tun_chr_write(struct file * file, const char * buf, 			 size_t count, loff_t *pos){   struct tun_struct *tun = (struct tun_struct *)file->private_data;   struct sk_buff* skb;   DBG(KERN_INFO "%s: tun_chr_write %d\n", tun->name, count);   /* Don't allow too large frames */   count = MIN(TUN_MAX_FRAME, count);    if( !(skb = dev_alloc_skb(count+2)) ){      printk("%s: Can't allocate memory, dropping packet.\n", tun->name);      tun->stats.rx_dropped++;      return -ENOMEM;   }    skb_reserve(skb, 2);   if( copy_from_user(skb_put(skb, count), buf, count) ) {      dev_kfree_skb(skb);      return -EFAULT;   }   skb->dev = &tun->dev;   switch( tun->flags & TUN_TYPE_MASK ) {      case TUN_TUN_DEV:         skb->mac.raw = skb->data;         skb->protocol = __constant_htons(ETH_P_IP);         break;      case TUN_TAP_DEV:         skb->protocol = eth_type_trans(skb, &tun->dev);         break;   }   if( tun->flags & TUN_NOCHECKSUM )      skb->ip_summed = CHECKSUM_UNNECESSARY;    netif_rx(skb);      tun->stats.rx_packets++;   tun->stats.rx_bytes += count;   return count;}/* Read */static ssize_t tun_chr_read(struct file * file, char * buf, 			size_t count, loff_t *pos){   struct tun_struct *tun = (struct tun_struct *)file->private_data;   struct wait_queue wait = { current, NULL };   struct sk_buff *skb;   ssize_t ret=0, len;   DBG(KERN_INFO "%s: tun_chr_read\n", tun->name);   add_wait_queue(&tun->read_wait, &wait);   while( count ){      current->state = TASK_INTERRUPTIBLE;      /* Read frames from device queue */      if( !(skb=skb_dequeue(&tun->txq)) ) {	 if( file->f_flags & O_NONBLOCK ) {	    ret = -EAGAIN; 	    break;	 }	 if( signal_pending(current) ) {	    ret = -ERESTARTSYS; 	    break;	 }	 /* Nothing to read, let's sleep */	 schedule();         continue;      }      tun->dev.tbusy = 0;      /* Notify net layer that we are ready to        * handle more frames */	      mark_bh(NET_BH);      /* Copy frame to user space buffer.        * If it doesn't fit strip it */      len = MIN(skb->len, count);       if( copy_to_user(buf, skb->data, len) )	 ret = -EFAULT;      else	 ret = len;      dev_kfree_skb(skb);      tun->stats.tx_packets++;      tun->stats.tx_bytes += len;      break;    }   current->state = TASK_RUNNING;   remove_wait_queue(&tun->read_wait, &wait);   return ret;}static loff_t tun_chr_lseek(struct file * file, loff_t offset, int origin){   return -ESPIPE;}static int tun_chr_ioctl(struct inode *inode, struct file *file, 		     unsigned int cmd, unsigned long arg){   struct tun_struct *tun = (struct tun_struct *)file->private_data;   DBG(KERN_INFO "%s: tun_chr_ioctl\n", tun->name);   switch( cmd ){      case TUNSETNOCSUM:	 /* Disable/Enable checksum on net iface */         if( arg ) 	    tun->flags |= TUN_NOCHECKSUM;	 else	    tun->flags &= ~TUN_NOCHECKSUM;	 DBG(KERN_INFO"%s: checksum %s\n",tun->name, arg?"disabled":"enabled");	 break;#ifdef TUN_DEBUG      case TUNSETDEBUG:	 tun->debug = arg;	 break;#endif      default:         return -EINVAL;   }   return 0;}static int tun_chr_fasync(int fd, struct file *file, int on){   struct tun_struct *tun = (struct tun_struct *)file->private_data;    DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->name, on);   if( on ) {      /* Enable FASYNC mode        * We don't need any fasync queue since only one process        * is allowed to open TUN device        */      if( !file->f_owner.pid ) {         file->f_owner.pid  = current->pid;         file->f_owner.uid  = current->uid;          file->f_owner.euid = current->euid;      }      tun->fasync.magic   = FASYNC_MAGIC;      tun->fasync.fa_file = file;      tun->fasync.fa_fd   = fd;      tun->fasync.fa_next = NULL;      tun->flags |= TUN_FASYNC;   } else {      /* Disable FASYNC mode */      tun->flags &= ~TUN_FASYNC;   }	   return 0;}static int tun_chr_open(struct inode *inode, struct file * file){   unsigned int minor = MINOR(inode->i_rdev);   unsigned int index = minor & TUN_MINOR_MASK;   struct tun_struct *tun = NULL;    if( minor > TUN_MAX_DEV ){      DBG1(KERN_ERR "tun: Device minor is too large %d\n", minor);      return -ENODEV;   }   DBG1(KERN_INFO "tun%d: tun_chr_open\n", index);   /* Only one process is allowed to open */	   if( test_and_set_bit(minor, tun_open_mask) )      return -EBUSY;       DBG1(KERN_INFO "tun%d: Allocating device\n", index);      if( !(tun=kmalloc(sizeof(struct tun_struct), GFP_KERNEL)) ) {      clear_bit(minor, tun_open_mask);      return -ENOMEM;   }   file->private_data = tun;   memset(tun, 0, sizeof(struct tun_struct));   /* Set device type */   if( minor < TUN_TAP_MINOR ){      /* TUN device */      tun->flags |= TUN_TUN_DEV;      sprintf(tun->name, "tun%d", index);   } else {      /* TAP device */      tun->flags |= TUN_TAP_DEV;      sprintf(tun->name, "tap%d", index);   }    /* Initialize and register net device */   skb_queue_head_init(&tun->txq);    	   tun->dev.name  = tun->name;   tun->dev.init  = tun_net_init;   tun->dev.base_addr = index;   tun->dev.priv = tun;    if( register_netdev(&tun->dev) ){      printk(KERN_ERR "%s: Can't register net device\n", tun->name);            file->private_data = NULL;      clear_bit(minor, tun_open_mask);      kfree(tun);      return -ENODEV;   }   MOD_INC_USE_COUNT;	   return 0;}static int tun_chr_close(struct inode *inode, struct file *file){   struct tun_struct *tun = (struct tun_struct *)file->private_data;   unsigned int minor = MINOR(inode->i_rdev);   register struct sk_buff *skb;   DBG(KERN_INFO "%s: tun_chr_close\n", tun->name);   dev_close(&tun->dev);   /* Drop TX queue */   while( (skb = skb_dequeue(&tun->txq)) )      dev_kfree_skb(skb);   unregister_netdev(&tun->dev);   kfree(tun);   file->private_data = NULL;   clear_bit(minor, tun_open_mask);   MOD_DEC_USE_COUNT;   return 0;}static struct file_operations tun_fops = {   tun_chr_lseek,   tun_chr_read,   tun_chr_write,   NULL,		/* readdir */   tun_chr_poll,   tun_chr_ioctl,   NULL,		/* mmap */   tun_chr_open,   NULL,		/* flush */   tun_chr_close,   NULL,		/* fsync */   tun_chr_fasync		};__initfunc(int tun_init(void)){   printk(KERN_INFO "Universal TUN/TAP device driver %s " 		    "(c) Maxim Krasnyansky\n", TUN_VER);   if( register_chrdev(TUN_MAJOR,"tun", &tun_fops) ){      printk(KERN_ERR "tun: Can't register char device %d\n", TUN_MAJOR);      return -EIO;   }#ifdef MODULE   return 0;#else   /* If driver is not module, tun_init will be called from Space.c.    * Return non-zero to not register fake device. */	   return 1;#endif}#ifdef MODULEint init_module(void){   return tun_init();}void cleanup_module(void){   unregister_chrdev(TUN_MAJOR,"tun");}#endif

⌨️ 快捷键说明

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