搜索
bottom↓
回复: 6

如何在linux中虚拟网卡

[复制链接]

出0入0汤圆

发表于 2010-11-12 10:43:24 | 显示全部楼层 |阅读模式
先转载一篇文章:

在linux中虚拟网卡的方法

        在实验环境中,一直想在一台机器上模拟出多个客户端,这样在搭环境的时候就可以不用使用那么多的机器。但是,仅仅能在一个网卡上模拟出一个IP有时是不行的,需要mac地址也不一样。

  

      对于这个需求,我一直想咋整,一直不知道,在CSDN和Chinaunix上也发贴问,也没见着回的。而我知道,virtualBox和WM等虚拟机是可以实现的,它的虚拟机里发出数据包的mac就是和系统的mac不一样,也就是说,这个是可以实现的,只是我不知道方法而已。

      后来,终于google到了一个叫mac-vlan的补丁(http://www.candelatech.com/~greear/vlan.html),mac-vlan代码在内核2.6.29版本及以后已经加入内核。它可以被ip工具集操作。

       方法:

      1.加载8021q模块:

               #sudo modprobe 8021q

      2. 启用目标网卡,在这块网卡上配置一个vlan。

                #sudo ifconfig eth0 up

      3.配置出一个vlan:

               #sudo vconfig add eth0 2

              Added VLAN with VID == 2 to IF -:eth0:-

         系统会生成一个属于vlan2的网卡,可以用ifconfig -a来查看:

             [moldao@moldao python]$ ifconfig eth0.2

eth0.2    Link encap:Ethernet  HWaddr 00:1F:16:2E:28:C7  

          BROADCAST MULTICAST  MTU:1500  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:0

          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

        4.之后就可以用ipconfig或者ip 命令来进行配置,配置其ip,mac等:
       $ sudo ifconfig -s eth0.2 10.0.1.1 broadcast 10.0.1.0 netmask 255.255.255.0 up
        5.按照官网上说明,mac-vlan的特性有:
         
•Implements 802.1Q VLAN spec.
•Can support up to 4094 VLANs per ethernet interface.
•Scales well in critical paths: O(n), where n is the number of PHYSICAL ethernet interfaces, and that is only on ingress. O(1) in every other critical path, as far as I know.
•Supports MULTICAST
•Can change MAC address of VLAN.
•Multiple naming conventions supported, and adjustable at runtime.
•Optional header-reordering, to make the VLAN interface look JUST LIKE an Ethernet interface. This fixes some problems with DHCPd and anything else that uses a SOCK_PACKET socket. Default setting is off, which works for every other protocol I know about, and is slightly faster.


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/moldao/archive/2010/07/08/5721522.aspx

出0入0汤圆

 楼主| 发表于 2010-11-12 10:45:20 | 显示全部楼层
请教一个问题:

对于虚拟出的网卡,能否通过SOCKET编程,收发数据?

出0入0汤圆

 楼主| 发表于 2010-11-12 10:49:24 | 显示全部楼层
虚拟网卡驱动
时间:2010-08-19 10:17:44  来源:  作者:

学习了linux驱动程式,模仿tun写了一个虚拟网卡驱动,很简陋

[虚拟网卡设备]  [字符设备]  [NIC]
设备虚拟网络卡设备,和字符设备,通过socket向虚拟网卡发送和接受的数据,能够通过字符设备进行读写.
通过一个用户空间程式,能够将字符设备和真实的网卡连接起来,并实现虚拟网卡和真实网卡的桥接

#include linux/module.h>
#include linux/errno.h>
#include linux/kernel.h>
#include linux/miscdevice.h>
#include linux/init.h>
#include linux/major.h>
#include linux/slab.h>
#include linux/fcntl.h>
#include linux/skbuff.h>
#include linux/netdevice.h>
#include linux/etherdevice.h>
#include linux/if.h>
#include linux/if_arp.h>
#include linux/if_ether.h>
#define DBG(x...) printk(x)
//#define DBG(x...)
#define VNIC_MINOR 200
#define READQ_SIZE 500
#define SETIFUP 0xff0001
#define SETIFDN 0xff0002
#define SETIFRUN 0xff0003
static LIST_HEAD(vnic_net_list);
struct vnic_struct{
    struct list_head list; //设备链表
    wait_queue_head_t read_head; //等待队列
    spinlock_t lock; //琐
    struct sk_buff_head readq; //收包队列
    struct net_device *dev; //net_device
    struct net_device_stats stats; //设备状态
     
     
};
loff_t no_chr_llseek(struct file *file, loff_t offset, int origin){
    return -ESPIPE;
}
static int vnic_chr_open(struct inode *inode, struct file *file){
    file->private_data = NULL;
    return 0;
}
static int vnic_chr_close(struct inode *inode, struct file *file){
    return 0;
}
static ssize_t vnic_chr_read(struct file * file, char __user * buf,
                size_t count, loff_t *pos){
    struct sk_buff *skb;
    struct vnic_struct *vnic = file->private_data;
    int len;
    DECLARE_WAITQUEUE(wait, current); //声明并初始化等待队列
    add_wait_queue(&vnic->read_head, &wait); //加入等待队列
     
    wait_event_interruptible(vnic->read_head, skb_queue_len(&vnic->readq)>0);
    if(vnic == NULL){
        DBG(KERN_INFO"vnic data wrong/n");
        return -1;
    }
    skb = skb_dequeue(&vnic->readq); //从收包队列中解包
    if(skb == NULL){
        DBG(KERN_INFO"no packet/n");
        return 0;
    }
    netif_wake_queue(vnic->dev); //重新唤醒收包队列(当队列满时,将停止收包,这边来唤醒)
    len = skb->len;
    if(copy_to_user(buf, skb->data, len)){ //提交给用户空间
        DBG(KERN_ERR"copy to use failed/n");
    }
    /*
        统计状态维护
    */
    vnic->stats.tx_packets++;
    vnic->stats.tx_bytes += len;
     
    kfree_skb(skb); //释放skb
    remove_wait_queue(&vnic->read_head, &wait); //移出等待队列
    schedule(); //放弃cpu
    return len;
}
static ssize_t vnic_chr_write(struct file * file, const char __user * buf,
                 size_t count, loff_t *pos){
    struct vnic_struct *vnic;
    struct sk_buff *skb;
    struct net_device *dev;
    int len;
    int align = 2;
    vnic = file->private_data;
    if(vnic == NULL){
        DBG(KERN_DEBUG"write data wrong/n");
        return -1;
    }
    dev = vnic->dev;
    len = count;
    if(len > dev->mtu + ETH_HLEN){ //包过大
        vnic->stats.rx_dropped++;
        DBG(KERN_INFO"packet too long/n");
        return -1;
    }
     
    skb = alloc_skb(len + align, GFP_KERNEL); //分配skb
    if(skb == NULL){
        vnic->stats.rx_dropped++;
        DBG(KERN_ERR"alloc skb failed/n");
        return -1;
    }
    skb_reserve(skb, align); //16bytes对齐
    skb_put(skb, len); //移动指针,准备数据copy
    if(copy_from_user(skb->data, buf, len)){ //用户空间copy数据
        kfree_skb(skb); //失败,释放skb
        DBG(KERN_INFO"copy from user failed/n");
        vnic->stats.rx_dropped++;
        return -1;
    }
    skb->dev = dev;
    skb->protocol = eth_type_trans(skb, dev); //解析以太协议
    netif_rx_ni(skb); //提交给上层协议栈
    dev->last_rx = jiffies; //last_rx维护
    /*
        设备状态维护
    */
    vnic->stats.rx_packets++;
    vnic->stats.rx_bytes += len;
     
    return len;
}
int vnic_net_open(struct net_device *dev){
    netif_start_queue(dev); //启动队列
    return 0;
}
int vnic_net_stop(struct net_device *dev){
    netif_stop_queue(dev); //停止队列
    return 0;
}
int vnic_net_tx(struct sk_buff *skb,
                            struct net_device *dev){
    struct vnic_struct *vnic = netdev_priv(dev);
    if(skb_queue_len(&vnic->readq) >= dev->tx_queue_len){ //队列是否满
        vnic->stats.tx_dropped++; //丢包
        kfree_skb(skb);
        netif_stop_queue(dev); //停止队列
        DBG(KERN_INFO"read queue is full/n");
    }else{
        skb_queue_tail(&vnic->readq, skb); //加入收包队列,等待处理
        dev->trans_start = jiffies;
    }
    wake_up_interruptible(&vnic->read_head); //唤醒等待队列
    DBG(KERN_DEBUG"tx queue: %d/n", skb_queue_len(&vnic->readq));
    return 0;
}
static struct net_device_stats *vnic_net_stats(struct net_device *dev)
{
    struct vnic_struct *vnic = netdev_priv(dev);
    return &vnic->stats;
}
void vnic_setup(struct net_device *dev){
    struct vnic_struct *vnic = netdev_priv(dev); //获取net_device私有数据
     
    ether_setup(dev); //配置设备为以太网设备,定义部分以太网络的参数
    /*
        以下为配置网络设备的操作函数
    */
    dev->open = vnic_net_open;
    dev->stop = vnic_net_stop;
    dev->hard_start_xmit = vnic_net_tx;
    dev->get_stats = vnic_net_stats;
     
    random_ether_addr(dev->dev_addr); //随机源地址
    dev->tx_queue_len = READQ_SIZE; //收包队列长度
    vnic->dev = dev; //配置私有数据的net_device成员
    memset(&vnic->stats, 0, sizeof(struct net_device_stats));
    spin_lock_init(&vnic->lock);
    init_waitqueue_head(&vnic->read_head); //初始化等待队列
    skb_queue_head_init(&vnic->readq); //初始化收包队列
    spin_lock(&vnic->lock);
    list_add(&vnic->list, &vnic_net_list); //将新建vnic_struct加入到设备链表
    spin_unlock(&vnic->lock);
}
struct net_device *setup_dev(void){
    struct net_device *dev;
    dev = alloc_netdev(sizeof(struct vnic_struct), "vnic%d", vnic_setup); //分配net_device,初始化函数vnic_setup
    if(dev == NULL){
        DBG(KERN_ERR"alloc net device failed/n");
        return NULL;
    }
    if(register_netdev(dev) != 0){ //注_册网络设备
        DBG(KERN_ERR"register net device failed/n");
        return NULL;
    }
     
    return dev;
}
int shutdown_dev(void){
    struct vnic_struct *vnic, *next;
     
    list_for_each_entry_safe(vnic, next, &vnic_net_list, list){ //遍历任何vnic_struct
        spin_lock(&vnic->lock);
        skb_queue_purge(&vnic->readq); //清空收包队列
        list_del(&vnic->list); //将当前vnic_struct从队列中删除
        unregister_netdev(vnic->dev); //注销网络设备
        free_netdev(vnic->dev); //释放网络设备
        spin_unlock(&vnic->lock);
    }
    return 0;
}
static int vnic_chr_ioctl(struct inode *inode, struct file *file,
             unsigned int cmd, unsigned long arg){
    int ret = 0;
    struct net_device *dev;
    struct vnic_struct *vnic;
     
     
    switch(cmd){
        case SETIFUP: //建立虚拟网卡
            if((dev = setup_dev()) == NULL){
                DBG(KERN_DEBUG"setup net device failed/n");
                ret = -EFAULT;
            }else{
                file->private_data = netdev_priv(dev);
            }
            break;
        case SETIFDN: //关闭虚拟网卡
            if(shutdown_dev() == -1){
                DBG(KERN_DEBUG"shutdown net device failed/n");
                ret = -EFAULT;
            }
            break;
    }
     
    return ret;
}
struct file_operations vnic_ops = {
    .owner = THIS_MODULE,
    .llseek = no_chr_llseek,
    .open = vnic_chr_open,
    .release = vnic_chr_close,
    .read = vnic_chr_read,
    .write = vnic_chr_write,
    .ioctl = vnic_chr_ioctl
     
};
static struct miscdevice vnic_misc = {
    .minor = VNIC_MINOR, //混合设备minor号
    .name = "vnic", //设备名称
    .fops = &vnic_ops //操作函数定义
     
};
int vnic_chr_init(void){
    int ret = 0;
    ret = misc_register(&vnic_misc); //注_册为混合设备,major = 10
    if(ret){
        DBG(KERN_INFO"misc register failed, device name:%s/n", vnic_misc.name);
    }else{
        DBG(KERN_INFO"misc register OK, device: %s, minor: %d/n", vnic_misc.name, vnic_misc.minor);
    }
    return ret;
     
}
void vnic_chr_exit(void){
    misc_deregister(&vnic_misc); //注销混合设备
    shutdown_dev(); //关闭网络设备
    DBG(KERN_INFO"misc deregister OK/n");
     
}
MODULE_AUTHOR("wzq");
MODULE_LICENSE("GPL");
module_init(vnic_chr_init);
module_exit(vnic_chr_exit);


本文来自ChinaUnix博客,假如查看原文请点:http://blog.chinaunix.net/u3/104553/showart_2065154.html
-

出0入0汤圆

 楼主| 发表于 2010-11-12 10:50:13 | 显示全部楼层
linux下实现虚拟网卡 收藏
linux下实现虚拟网卡
我们在使用VMWARE的虚拟化软件时经常会发现它们能都能虚拟出一个网卡,貌似很神奇的技术,其实在Linux下很简单,有两种虚拟设

备,TUN时点对点的设备,tap表示以太网设备的,做为虚拟网卡驱动,Tun/tap驱动程序的数据接收和发送并不直接和真实网卡打交道,而是通

过用户态来转交。在linux下,要实现核心态和用户态数据的交互,有多种方式:可以通用socket创建特殊套接字,利用套接字实现数据交

互;通过proc文件系统创建文件来进行数据交互;还可以使用设备文件的方式,访问设备文件会调用设备驱动相应的例程,设备驱动本身就

是核心态和用户态的一个接口,Tun/tap驱动就是利用设备文件实现用户态和核心态的数据交互。

//All right revsered by yoki2009
//mailto:imj040144@tom.com
//Welcome to my blog:    http://blog.csdn.net/yoki2009

#include <unistd.h>
#include <stdio.h>
#include <curses.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <unistd.h>
#include <linux/if_tun.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>  
int tun_creat(char *dev,int flags)
{
struct ifreq ifr;
int fd,err;
assert(dev != NULL);
if((fd = open ("/dev/net/tun",O_RDWR))<0) //you can replace it to tap to create tap device.
  return fd;
memset(&ifr,0,sizeof (ifr));
ifr.ifr_flags|=flags;
if(*dev != '\0')
  strncpy(ifr.ifr_name,dev,IFNAMSIZ);
if((err = ioctl(fd,TUNSETIFF,(void *)&ifr))<0)
{
  close (fd);
  return err;
}
strcpy(dev,ifr.ifr_name);
return fd;
}

int main()
{
int tun,ret;
char tun_name[IFNAMSIZ];
unsigned char buf[4096];
tun_name[0]='\0';
tun = tun_creat(tun_name,IFF_TAP|IFF_NO_PI);
if(tun<0)
{
  perror("tun_create");
  return 1;
}
printf("TUN name is %s\n",tun_name);
        while (1) {
                unsigned char ip[4];

                 ret = read(tun, buf, sizeof(buf));
                if (ret < 0)
                        break;
                memcpy(ip, &buf[12], 4);
                memcpy(&buf[12], &buf[16], 4);
                memcpy(&buf[16], ip, 4);
                 buf[20] = 0;
                *((unsigned short*)&buf[22]) += 8;
                printf("read %d bytes\n", ret);
                 ret = write(tun, buf, ret);
                printf("write %d bytes\n", ret);
        }
return 0;
}



本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yoki2009/archive/2009/06/29/4306524.aspx

出0入0汤圆

 楼主| 发表于 2010-11-12 10:52:21 | 显示全部楼层
虚拟网卡 TUN/TAP 驱动程序设计原理
麻利辉 (mlhdyx@21cn.com), 研究生, 电子科技大学计算机学院研究生部
麻利辉 电子科技大学计算机学院2003级研究生,研究方向为网络安全,对网络安全的各种技术都有着浓厚的兴趣,主要研究方向为防火墙技术和攻击技术。长期在linux下从事软件开发,您可以通过电子邮件 mlhdyx@21cn.com和他取得联系。

简介: 本文将介绍 TUN/TAP 驱动的使用并分析虚拟网卡 TUN/TAP 驱动程序在 Linux 环境下的设计思路。


标记本文!
发布日期: 2004 年 11 月 01 日
级别: 初级
访问情况 249 次浏览
建议: 0 (添加评论)

  平均分 (共 1 个评分 )
.简介

虚拟网卡Tun/tap驱动是一个开源项目,支持很多的类UNIX平台,OpenVPN和Vtun都是基于它实现隧道包封装。本文将介绍tun/tap驱动的使用并分析虚拟网卡tun/tap驱动程序在linux环境下的设计思路。

tun/tap驱动程序实现了虚拟网卡的功能,tun表示虚拟的是点对点设备,tap表示虚拟的是以太网设备,这两种设备针对网络包实施不同的封装。利用tun/tap驱动,可以将tcp/ip协议栈处理好的网络分包传给任何一个使用tun/tap驱动的进程,由进程重新处理后再发到物理链路中。开源项目openvpn( http://openvpn.sourceforge.net)和Vtun( http://vtun.sourceforge.net)都是利用tun/tap驱动实现的隧道封装。


--------------------------------------------------------------------------------
回页首
使用tun/tap驱动

在linux 2.4内核版本及以后版本中,tun/tap驱动是作为系统默认预先编译进内核中的。在使用之前,确保已经装载了tun/tap模块并建立设备文件:

#modprobe tun
#mknod /dev/net/tun c 10 200



参数c表示是字符设备, 10和200分别是主设备号和次设备号。

这样,我们就可以在程序中使用该驱动了。

使用tun/tap设备的示例程序(摘自openvpn开源项目 http://openvpn.sourceforge.net,tun.c文件)

int open_tun (const char *dev, char *actual, int size)
{
  struct ifreq ifr;
  int fd;
  char *device = "/dev/net/tun";
  if ((fd = open (device, O_RDWR)) < 0) //创建描述符
    msg (M_ERR, "Cannot open TUN/TAP dev %s", device);
  memset (&ifr, 0, sizeof (ifr));
  ifr.ifr_flags = IFF_NO_PI;
  if (!strncmp (dev, "tun", 3)) {  
      ifr.ifr_flags |= IFF_TUN;
   }
  else if (!strncmp (dev, "tap", 3)) {
      ifr.ifr_flags |= IFF_TAP;
    }
  else {
    msg (M_FATAL, "I don't recognize device %s as a TUN or TAP device",dev);
    }
  if (strlen (dev) > 3)                /* unit number specified? */
    strncpy (ifr.ifr_name, dev, IFNAMSIZ);
  if (ioctl (fd, TUNSETIFF, (void *) &ifr) < 0) //打开虚拟网卡
    msg (M_ERR, "Cannot ioctl TUNSETIFF %s", dev);
  set_nonblock (fd);
  msg (M_INFO, "TUN/TAP device %s opened", ifr.ifr_name);
  strncpynt (actual, ifr.ifr_name, size);
  return fd;
}



调用上述函数后,就可以在shell命令行下使用ifconfig 命令配置虚拟网卡了,通过生成的字符设备描述符,在程序中使用read和write函数就可以读取或者发送给虚拟的网卡数据了。


--------------------------------------------------------------------------------
回页首
Tun/tap驱动程序工作原理

做为虚拟网卡驱动,Tun/tap驱动程序的数据接收和发送并不直接和真实网卡打交道,而是通过用户态来转交。在linux下,要实现核心态和用户态数据的交互,有多种方式:可以通用socket创建特殊套接字,利用套接字实现数据交互;通过proc文件系统创建文件来进行数据交互;还可以使用设备文件的方式,访问设备文件会调用设备驱动相应的例程,设备驱动本身就是核心态和用户态的一个接口,Tun/tap驱动就是利用设备文件实现用户态和核心态的数据交互。

从结构上来说,Tun/tap驱动并不单纯是实现网卡驱动,同时它还实现了字符设备驱动部分。以字符设备的方式连接用户态和核心态。下面是示意图:




Tun/tap驱动程序中包含两个部分,一部分是字符设备驱动,还有一部分是网卡驱动部分。利用网卡驱动部分接收来自TCP/IP协议栈的网络分包并发送或者反过来将接收到的网络分包传给协议栈处理,而字符驱动部分则将网络分包在内核与用户态之间传送,模拟物理链路的数据接收和发送。Tun/tap驱动很好的实现了两种驱动的结合。

下面是定义的tun/tap设备结构:

struct tun_struct {
        char                         name[8]; //设备名
        unsigned long                 flags; //区分tun和tap设备
        struct fasync_struct    *fasync; //文件异步通知结构
        wait_queue_head_t         read_wait; //等待队列
        struct net_device        dev;  //linux 抽象网络设备结构
        struct sk_buff_head         txq; //网络缓冲区队列
    struct net_device_stats        stats; //网卡状态信息结构
};



struct net_device结构是linux内核提供的统一网络设备结构,定义了系统统一的访问接口。

Tun/tap驱动中实现的网卡驱动的处理例程:

static int tun_net_open(struct net_device *dev);
static int tun_net_close(struct net_device *dev);
static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev);//数据包发送例程
static void tun_net_mclist(struct net_device *dev);//设置多点传输的地址链表
static struct net_device_stats *tun_net_stats(struct net_device *dev);//当一个应用程序需要知道网络接口的一些统计数据时,可调用该函数,如ifconfig、netstat等。
int tun_net_init(struct net_device *dev);//网络设备初始例程

字符设备部分:

在Linux中,字符设备和块设备统一以文件的方式访问,访问它们的接口是统一的,都是使用open()函数打开设备文件或普通文件,用read()和write()函数实现读写文件等等。Tun/tap驱动定义的字符设备的访问接口如下:

static struct file_operations tun_fops = {
owner: THIS_MODULE,
llseek: tun_chr_lseek,
read tun_chr_read,
write: tun_chr_write,
poll: tun_chr_poll,
ioctl: tun_chr_ioctl,
open: tun_chr_open,
release: tun_chr_close,
fasync: tun_chr_fasync
};

在内核中利用misc_register() 函数将该驱动注_册为非标准字符设备驱动,提供字符设备具有的各种程序接口。代码摘自linux-2.4.20\linux-2.4.20\drivers\net\tun.c

static struct miscdevice tun_miscdev=
{
        TUN_MINOR,
        "net/tun",
        &tun_fops
};
int __init tun_init(void)
{
        …
        if (misc_register(&tun_miscdev)) {
                printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR);
                return -EIO;
        }
        return 0;
}



当打开一个tun/tap设备时,open 函数将调用tun_chr_open()函数,其中将完成一些重要的初始化过程,包括设置网卡驱动部分的初始化函数以及网络缓冲区链表的初始化和等待队列的初始化。Tun/tap驱动中网卡的注_册被嵌入了字符驱动的ioctl例程中,它是通过对字符设备文件描述符利用自定义的ioctl设置标志TUNSETIFF完成网卡的注_册的。下面是函数调用关系示意图:




使用ioctl()函数操作字符设备文件描述符,将调用字符设备中tun_chr_ioctl 来设置已经open好的tun/tap设备,如果设置标志为TUNSETIFF,则调用tun_set_iff() 函数,此函数将完成很重要的一步操作,就是对网卡驱动进行注_册register_netdev(&tun->dev),网卡驱动的各个处理例程的挂接在open操作时由tun_chr_open()函数初始化好了。

Tun/tap设备的工作过程:

Tun/tap设备提供的虚拟网卡驱动,从tcp/ip协议栈的角度而言,它与真实网卡驱动并没有区别。从驱动程序的角度来说,它与真实网卡的不同表现在tun/tap设备获取的数据不是来自物理链路,而是来自用户区,Tun/tap设备驱动通过字符设备文件来实现数据从用户区的获取。发送数据时tun/tap设备也不是发送到物理链路,而是通过字符设备发送至用户区,再由用户区程序通过其他渠道发送。

发送过程:

使用tun/tap网卡的程序经过协议栈把数据传送给驱动程序,驱动程序调用注_册好的hard_start_xmit函数发送,hard_start_xmit函数又会调用tun_net_xmit函数,其中skb将会被加入skb链表,然后唤醒被阻塞的使用tun/tap设备字符驱动读数据的进程,接着tun/tap设备的字符驱动部分调用其tun_chr_read()过程读取skb链表,并将每一个读到的skb发往用户区,完成虚拟网卡的数据发送。

接收数据的过程:

当我们使用write()系统调用向tun/tap设备的字符设备文件写入数据时,tun_chr_write函数将被调用,它使用tun_get_user从用户区接受数据,其中将数据存入skb中,然后调用关键的函数netif_rx(skb) 将skb送给tcp/ip协议栈处理,完成虚拟网卡的数据接收。




--------------------------------------------------------------------------------
回页首
小结

tun/tap驱动很巧妙的将字符驱动和网卡驱动糅合在一起,本文重点分析两种驱动之间的衔接,具体的驱动处理细节没有一一列出,请读者参考相关文档。



参考资料

&#8226;《Linux 设备驱动程序 第二版》,(美)Alessando Rubini


&#8226;http://openvpn.sourceforge.net


&#8226;Linux串口上网的简单实现


&#8226;linux 源代码


关于作者

麻利辉 电子科技大学计算机学院2003级研究生,研究方向为网络安全,对网络安全的各种技术都有着浓厚的兴趣,主要研究方向为防火墙技术和攻击技术。长期在linux下从事软件开发,您可以通过电子邮件 mlhdyx@21cn.com和他取得联系。
.

出0入0汤圆

发表于 2010-11-12 15:15:51 | 显示全部楼层
mark

出0入0汤圆

发表于 2013-7-24 22:14:57 | 显示全部楼层
MARK一下
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-9 18:10

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表