通用 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 设备驱动程序常见问题解答

  1. 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

  1. TUN/TAP 驱动程序用于什么?

如上所述,TUN/TAP 驱动程序的主要目的是隧道传输。它被 VTun (http://vtun.sourceforge.net) 使用。

另一个使用 TUN/TAP 的有趣应用程序是 pipsecd (http://perso.enst.fr/~beyssac/pipsec/),这是一个用户空间 IPSec 实现,可以使用完整的内核路由(与 FreeS/WAN 不同)。

  1. 虚拟网络设备实际上是如何工作的?

虚拟网络设备可以被看作是一个简单的点对点或以太网设备,但它不是从物理介质接收数据包,而是从用户空间程序接收;它也不是通过物理介质发送数据包,而是将数据包发送到用户空间程序。

假设您在 tap0 上配置了 IPv6,那么每当内核向 tap0 发送 IPv6 数据包时,它都会传递给应用程序(例如 VTun)。该应用程序加密、压缩数据包并通过 TCP 或 UDP 将其发送到另一端。另一端的应用程序解压缩并解密接收到的数据,然后将数据包写入 TAP 设备,内核像处理来自真实物理设备的数据包一样处理该数据包。

  1. TUN 驱动程序和 TAP 驱动程序有什么区别?

TUN 处理 IP 帧。TAP 处理以太网帧。

这意味着当您使用 tun 时,您必须读取/写入 IP 数据包;当您使用 tap 时,必须读取/写入以太网帧。

  1. BPF 和 TUN/TAP 驱动程序有什么区别?

BPF 是一种高级数据包过滤器。它可以附加到现有的网络接口。它不提供虚拟网络接口。TUN/TAP 驱动程序确实提供了虚拟网络接口,并且可以将 BPF 附加到此接口。

  1. TAP 驱动程序是否支持内核以太网桥接?

是的。Linux 和 FreeBSD 驱动程序支持以太网桥接。