通用 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 - 基于异步 io 和 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 驱动程序支持以太网桥接。