通用TUN/TAP設(shè)備驅(qū)動

原文Universal TUN/TAP device driver

1. 描述

TUN/TAP為用戶空間提供分組接收和傳輸。既可以看作一個Point-to-Point設(shè)備,也可以看做一個Ethernet設(shè)備。它從用戶空間程序接收分組,而不是從物理媒介;將分組寫入用戶空間程序而不是通過物理媒介。

一個程序打開/dev/net/tun字符型文件,并向內(nèi)核發(fā)出ioctl()來注冊一個網(wǎng)絡(luò)設(shè)備。根據(jù)選項,這個網(wǎng)絡(luò)設(shè)備將顯示為tunXX或者tapXX。當程序關(guān)閉文件描述符時,這個網(wǎng)絡(luò)設(shè)備和所有對應(yīng)的路由都將關(guān)閉。

用戶空間程序read/write IP數(shù)據(jù)包(通過tun)還是以太網(wǎng)幀(通過tap),取決于選擇的設(shè)備類型。正在使用哪一個取決于ioctl()設(shè)置的flag。

http://vtun.sourceforge.net/tun下的軟件包包含兩個簡單的示例,介紹如何使用tun和tap設(shè)備。兩個示例的工作原理都像在兩個網(wǎng)絡(luò)接口之間的橋一樣。

  • br_select.c - 基于選擇系統(tǒng)調(diào)用的橋
  • br_sigio.c - 基于async io和SIGIO信號的橋

另外,最好的例子來源于VTun(http://vtun.sourceforge.net) :))

2. 配置

創(chuàng)建設(shè)備節(jié)點:

mkdir /dev/net
mknod /dev/net/tun c 10 200

設(shè)置權(quán)限:

chmod 0666 /dev/net/tun

驅(qū)動程序模塊自動加載:

確保內(nèi)核啟用了“內(nèi)核模塊加載器” - 模塊自動加載支持。 內(nèi)核應(yīng)該在第一次訪問時加載它。

手動加載:

insert the module by hand:
  modprobe tun

后一種方法,每次當你需要使用模塊的時候,你都需要加載它。另一種方法將在你打開/dev/net/tun的時候自動加載。

3. 編程接口

3.1 網(wǎng)絡(luò)設(shè)備分配

設(shè)備的名稱char *dev是具有格式的字符串(e.g. "tun%d"),也可以是任何有效的網(wǎng)絡(luò)設(shè)備名稱。注意,字符指針將會被真實網(wǎng)絡(luò)設(shè)備名覆蓋。

#include <linux/if.h>
#include <linux/if_tun.h>

int tun_alloc(char *dev)
{
    struct ifreq ifr;
    int fd, err;
    
    //獲取/dev/net/tun的文件描述符
    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 )
        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;
}

3.2 幀格式

如果IFF_NO_PI標志沒有被設(shè)置,每一幀格式如下:

Flags [2 bytes]
Proto [2 bytes]
Raw protocol(IP, IPv6, etc) frame.

3.3 多隊列tuntap接口

從3.8版開始,Linux支持多隊列tuntap,它可以使用多個文件描述符(隊列)來并行發(fā)送或接收數(shù)據(jù)包。 設(shè)備分配與以前相同,如果用戶想要創(chuàng)建多個隊列,則必須使用IFF_MULTI_QUEUE標志多次調(diào)用具有相同設(shè)備名稱的TUNSETIFF。

char *dev是設(shè)備的名稱,queues是要創(chuàng)建的隊列數(shù),fds用于存儲和返回創(chuàng)建給調(diào)用者的文件描述符(隊列)。 每個文件描述符都作為一個隊列的接口,可以被用戶空間訪問。

#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 er;
        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標志調(diào)用它時,隊列被禁用。 當用IFF_ATTACH_QUEUE標志調(diào)用它時,隊列被啟用。 通過TUNSETIFF創(chuàng)建隊列后,隊列默認啟用。

fd是我們想要啟用或禁用的文件描述符(隊列),當enable為真時,我們啟用它,否則我們禁用它

#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);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容