通用 TUN/TAP 设备驱动¶
版权所有 © 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
Linux、Solaris 驱动程序版权所有 © 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
FreeBSD TAP 驱动程序版权所有 © 1999-2000 Maksim Yevmenkin <m_evmenkin@yahoo.com>
本文档修订版 2002 Florian Thiel <florian.thiel@gmx.net>
1. 描述¶
TUN/TAP 为用户空间程序提供数据包的接收和传输。它可以被看作是一个简单的点对点或以太网设备,但它不是从物理介质接收数据包,而是从用户空间程序接收;它也不是通过物理介质发送数据包,而是将数据包写入用户空间程序。
为了使用该驱动程序,程序必须打开 /dev/net/tun 并发出相应的 ioctl() 来向内核注册网络设备。根据选择的选项,网络设备将显示为 tunXX 或 tapXX。当程序关闭文件描述符时,网络设备和所有相应的路由都将消失。
根据所选设备的类型,用户空间程序必须读取/写入 IP 数据包(使用 tun)或以太网帧(使用 tap)。具体使用哪种取决于 ioctl() 中给出的标志。
http://vtun.sourceforge.net/tun 上的软件包包含两个简单的示例,说明如何使用 tun 和 tap 设备。这两个程序都像两个网络接口之间的桥梁。br_select.c - 基于 select 系统调用的桥梁。br_sigio.c - 基于异步 I/O 和 SIGIO 信号的桥梁。然而,最好的例子是 VTun http://vtun.sourceforge.net :))
2. 配置¶
创建设备节点
mkdir /dev/net (if it doesn't exist already) mknod /dev/net/tun c 10 200设置权限
e.g. chmod 0666 /dev/net/tun允许非 root 用户访问该设备没有坏处,因为创建网络设备或连接到不属于该用户的网络设备需要 CAP_NET_ADMIN 权限。如果要创建持久设备并将它们的所有权赋予非特权用户,则需要使这些用户可以使用 /dev/net/tun 设备。
驱动程序模块自动加载
确保在内核中启用了“内核模块加载器”——模块自动加载支持。内核应该在第一次访问时加载它。
手动加载
手动插入模块
modprobe tun如果您采用后一种方式,则每次需要时都必须加载该模块;如果您采用前一种方式,则在打开 /dev/net/tun 时会自动加载该模块。
3. 程序接口¶
3.1 网络设备分配¶
char *dev
应该是带有格式字符串的设备名称(例如“tun%d”),但(据我所知)这可以是任何有效的网络设备名称。请注意,字符指针会被真实设备名称覆盖(例如“tun0”)。
#include <linux/if.h>
#include <linux/if_tun.h>
int tun_alloc(char *dev)
{
struct ifreq ifr;
int fd, err;
if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
return tun_alloc_old(dev);
memset(&ifr, 0, sizeof(ifr));
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
*
* IFF_NO_PI - Do not provide packet information
*/
ifr.ifr_flags = IFF_TUN;
if( *dev )
strscpy_pad(ifr.ifr_name, dev, IFNAMSIZ);
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
close(fd);
return err;
}
strcpy(dev, ifr.ifr_name);
return fd;
}
3.2 帧格式¶
如果未设置标志 IFF_NO_PI,则每个帧格式为
Flags [2 bytes]
Proto [2 bytes]
Raw protocol(IP, IPv6, etc) frame.
3.3 多队列 tuntap 接口¶
从 3.8 版本开始,Linux 支持多队列 tuntap,它可以使用多个文件描述符(队列)来并行发送或接收数据包。设备分配与以前相同,如果用户想要创建多个队列,则必须多次使用 IFF_MULTI_QUEUE 标志调用 TUNSETIFF 并使用相同的设备名称。
char *dev
应该是设备的名称,queues 是要创建的队列的数量,fds 用于存储并将创建的文件描述符(队列)返回给调用者。每个文件描述符都充当用户空间可以访问的队列接口。
#include <linux/if.h>
#include <linux/if_tun.h>
int tun_alloc_mq(char *dev, int queues, int *fds)
{
struct ifreq ifr;
int fd, err, i;
if (!dev)
return -1;
memset(&ifr, 0, sizeof(ifr));
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
*
* IFF_NO_PI - Do not provide packet information
* IFF_MULTI_QUEUE - Create a queue of multiqueue device
*/
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_MULTI_QUEUE;
strcpy(ifr.ifr_name, dev);
for (i = 0; i < queues; i++) {
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
goto err;
err = ioctl(fd, TUNSETIFF, (void *)&ifr);
if (err) {
close(fd);
goto err;
}
fds[i] = fd;
}
return 0;
err:
for (--i; i >= 0; i--)
close(fds[i]);
return err;
}
引入了一个新的 ioctl(TUNSETQUEUE) 来启用或禁用队列。使用 IFF_DETACH_QUEUE 标志调用它时,队列将被禁用。当使用 IFF_ATTACH_QUEUE 标志调用它时,队列将被启用。在通过 TUNSETIFF 创建队列后,默认情况下会启用队列。
fd 是我们要启用或禁用的文件描述符(队列),当 enable 为 true 时,我们启用它,否则禁用它。
#include <linux/if.h>
#include <linux/if_tun.h>
int tun_set_queue(int fd, int enable)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
if (enable)
ifr.ifr_flags = IFF_ATTACH_QUEUE;
else
ifr.ifr_flags = IFF_DETACH_QUEUE;
return ioctl(fd, TUNSETQUEUE, (void *)&ifr);
}
通用 TUN/TAP 设备驱动程序常见问题解答¶
TUN/TAP 驱动程序支持哪些平台?
目前,该驱动程序已为 3 个 Unix 系统编写
Linux 内核 2.2.x、2.4.x
FreeBSD 3.x、4.x、5.x
Solaris 2.6、7.0、8.0
TUN/TAP 驱动程序用于什么?
如上所述,TUN/TAP 驱动程序的主要目的是隧道传输。它被 VTun (http://vtun.sourceforge.net) 使用。
另一个使用 TUN/TAP 的有趣应用程序是 pipsecd (http://perso.enst.fr/~beyssac/pipsec/),这是一个用户空间 IPSec 实现,可以使用完整的内核路由(与 FreeS/WAN 不同)。
虚拟网络设备实际上是如何工作的?
虚拟网络设备可以被看作是一个简单的点对点或以太网设备,但它不是从物理介质接收数据包,而是从用户空间程序接收;它也不是通过物理介质发送数据包,而是将数据包发送到用户空间程序。
假设您在 tap0 上配置了 IPv6,那么每当内核向 tap0 发送 IPv6 数据包时,它都会传递给应用程序(例如 VTun)。该应用程序加密、压缩数据包并通过 TCP 或 UDP 将其发送到另一端。另一端的应用程序解压缩并解密接收到的数据,然后将数据包写入 TAP 设备,内核像处理来自真实物理设备的数据包一样处理该数据包。
TUN 驱动程序和 TAP 驱动程序有什么区别?
TUN 处理 IP 帧。TAP 处理以太网帧。
这意味着当您使用 tun 时,您必须读取/写入 IP 数据包;当您使用 tap 时,必须读取/写入以太网帧。
BPF 和 TUN/TAP 驱动程序有什么区别?
BPF 是一种高级数据包过滤器。它可以附加到现有的网络接口。它不提供虚拟网络接口。TUN/TAP 驱动程序确实提供了虚拟网络接口,并且可以将 BPF 附加到此接口。
TAP 驱动程序是否支持内核以太网桥接?
是的。Linux 和 FreeBSD 驱动程序支持以太网桥接。