虚拟网卡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驱动很巧妙的将字符驱动和网卡驱动糅合在一起,本文重点分析两种驱动之间的衔接,具体的驱动处理细节没有一一列出,请读者参考相关文档。
参考资料
关于作者
|
|
|
麻利辉 电子科技大学计算机学院2003级研究生,研究方向为网络安全,对网络安全的各种技术都有着浓厚的兴趣,主要研究方向为防火墙技术和攻击技术。长期在linux下从事软件开发,您可以通过电子邮件
mlhdyx@21cn.com和他取得联系。
|
分享到:
相关推荐
tun/tap虚拟网卡驱动
源代码 : tun/tap应用测试代码 包括原理介绍 设备创建代码及udp icmp包发送及接收代码 描述tun/tap的一个典型应用
TUN/TAP设备是Linux内核提供的一种虚拟网络接口,允许用户空间程序直接参与网络数据包的处理。TUN设备工作在网络层,处理IP级别的数据包;而TAP设备则工作在数据链路层,处理以太网或PPP等帧。这两种设备为开发者...
关于Linux的TUN/TAP编程.pdf
软件介绍: tuntap虚拟网卡官方原版32位和64位驱动程序,安装时要根据你的系统类型选择不同的驱动文件夹。驱动文件列表:oemwin2k.infoemwin2k.PNFtap0901.cattap0901.sys
易语言TUN/TAP虚拟网卡使用例子
用Tun/tap虚拟设备实现数据安全传送的方法的研究与实现,刘敏,蒋砚军,本文以Tun/tap为理论基础和技术背景,实现了一种安全传送数据的方法。Tun/tap驱动不仅实现了网卡驱动而且还实现了字符设备驱动部分,�
易语言伪造ip地址
TUN/TAP虚拟网卡设备在Ad-hoc组网中的应用与实现,蔡佳翌,李绍胜,极限场景下的通信是工程师们最容易忽略,而关键时刻却又至关重要的研究重点之一。因自然灾害或战争形成的重灾区,基础通信设施往
虚拟网卡,全称为“虚拟网络适配器”,是一种由操作系统创建并管理的网络接口,它并不实际对应物理硬件,而是模拟出一个网络接口供软件使用。在IT领域,虚拟网卡有着重要的应用,特别是在服务器环境和分布式系统中。...
在linux系统中实现TUN/TAP编程,既可以当作点对点设备(TUN),也可以当作以太网设备(TAP)。
一个基于tun/tap设备编写的简易的tcp/ip协议栈 实现一个协议栈其实并不是很难的一件事情,arp, icmp,udp这些不带任何状态的协议实现起来没有太大难度,当然,tcp协议的实现还是很有难度的,它的难度不在于编程的技巧,...
### Linux代码分析:深入理解Tun/Tap驱动 #### 功能说明 Tun/Tap驱动是Linux内核...通过对Tun/Tap驱动的深入分析,我们能更好地理解Linux内核中网络虚拟化的实现原理,这对于开发定制化的网络解决方案具有重要意义。
虚拟网卡驱动安装失败. 基于虚拟网卡的代理模式将无法使用.
通常的socket编程,面对的都是物理网卡,Linux下其实很容易创建...Linux通过TUN/TAP设备向绑定该设备的用户空间的应用程序发送数据;同样,用户空间的应用程序也可以像操作硬件网络设备那样,通过TUN/TAP设备发送数据。
网络驱动源码, 虚拟网卡驱动源码, 亲测可用。 编译环境winddk7,7600.
这个主要是针对我写的博文:http://blog.csdn.net/jayxujia123/article/details/37767189给出的tun.ko,希望对有相关问题的朋友有用
同时,由于TAP网卡是用户空间驱动,它相比传统的TUN( Tunneling Interface)网卡在某些场景下更易于管理和配置。了解并熟练掌握如何在RT-Thread和QEMU中配置和使用TAP网卡驱动,对于进行嵌入式系统的网络开发工作...
tuntap官方原版驱动,分32位64位,附带教程,没有任何捆绑