Linux的TUN/TAP編程

TUN/TAP虛擬網絡設備爲用戶空間程序提供了網絡數據包的發送和接收能力。他既可以當做點對點設備(TUN),也可以當做以太網設備(TAP)。實際上,不僅Linux支持TUN/TAP虛擬網絡設備,其他UNIX也是支持的,他們之間只有少許差別。

原理簡介

TUN/TAP 虛擬網絡設備的原理比較簡單,他在Linux內核中添加了一個TUN/TAP虛擬網絡設備的驅動程序和一個與之相關連的字符設備 /dev/net/tun,字符設備tun作爲用戶空間和內核空間交換數據的接口。當內核將數據包發送到虛擬網絡設備時,數據包被保存在設備相關的一個隊 列中,直到用戶空間程序通過打開的字符設備tun的描述符讀取時,它纔會被拷貝到用戶空間的緩衝區中,其效果就相當於,數據包直接發送到了用戶空間。通過 系統調用write發送數據包時其原理與此類似。

值得注意的是:一次read系統調用,有且只有一個數據包被傳送到用戶空間,並且當用戶空間的緩衝區比較小時,數據包將被截斷,剩餘部分將永久地消失,write系統調用與read類似,每次只發送一個數據包。所以在編寫此類程序的時候,請用足夠大的緩衝區,直接調用系統調用read/write,避免採用C語言的帶緩存的IO函數。

準備工作

首先你需要一個能工作的Linux操作系統,並且內核支持TUN/TAP虛擬網絡設備,如果沒有,請在內核中選中:

Device Drivers => Network device support => Universal TUN/TAP device driver support 
你可以選擇編譯進內核或者是編譯成模塊,然後重新編譯內核並用新內核啓動。如果你編譯的是模塊,那麼在下步開始之前,你需要手工加載它。

root@gentux ~ # modprobe tun 
開始編程

從代碼開始,

12 #include <linux/if_tun.h>
13
14 int tun_create(char *dev, int flags)
15 {
16     struct ifreq ifr;
17     int fd, err;
18
19     assert(dev != NULL);
20
21     if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
22         return fd;
23
24     memset(&ifr, 0, sizeof(ifr));
25     ifr.ifr_flags |= flags;
26     if (*dev != '\0')
27         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
28     if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
29         close(fd);
30         return err;
31     }
32     strcpy(dev, ifr.ifr_name);
33
34     return fd;
35 }


爲了使用TUN/TAP設備,我們必須包含特定的頭 文件linux/if_tun.h,如12行所示。在21行,我們打開了字符設備/dev/net/tun。接下來我們需要爲ioctl的 TUNSETIFF命令初始化一個結構體ifr,一般的時候我們只需要關心其中的兩個成員ifr_name, ifr_flags。ifr_name定義了要創建或者是打開的虛擬網絡設備的名字,如果它爲空或者是此網絡設備不存在,內核將新建一個虛擬網絡設備,並 返回新建的虛擬網絡設備的名字,同時文件描述符fd也將和此網絡設備建立起關聯。如果並沒有指定網絡設備的名字,內核將根據其類型自動選擇tunXX和 tapXX作爲其名字。ifr_flags用來描述網絡設備的一些屬性,比如說是點對點設備還是以太網設備。詳細的選項解釋如下:
  • IFF_TUN: 創建一個點對點設備
  • IFF_TAP: 創建一個以太網設備
  • IFF_NO_PI: 不包含包信息,默認的每個數據包當傳到用戶空間時,都將包含一個附加的包頭來保存包信息
  • IFF_ONE_QUEUE: 採用單一隊列模式,即當數據包隊列滿的時候,由虛擬網絡設備自已丟棄以後的數據包直到數據包隊列再有空閒。
配置的時候,IFF_TUN和IFF_TAP必須擇一,其他選項則可任意組合。其中IFF_NO_PI沒有開啓時所附加的包信息頭如下:

struct tun_pi {
    unsigned short flags;
    unsigned short proto;
};


目前,flags只在收取數據包的時候有效,當它的TUN_PKT_STRIP標誌被置時,表示當前的用戶空間緩衝區太小,以致數據包被截斷。proto成員表示發送/接收的數據包的協議。

上面代碼中的文件描述符fd除了支持TUN_SETIFF和其他的常規ioctl命令外,還支持以下命令:
  • TUNSETNOCSUM: 不做校驗和校驗。參數爲int型的bool值。
  • TUNSETPERSIST: 把對應網絡設備設置成持續模式,默認的虛擬網絡設備,當其相關的文件符被關閉時,也將會伴隨着與之相關的路由等信息同時消失。如果設置成持續模式,那麼它將會被保留供以後使用。參數爲int型的bool值。
  • TUNSETOWNER: 設置網絡設備的屬主。參數類型爲uid_t。
  • TUNSETLINK: 設置網絡設備的鏈路類型,此命令只有在虛擬網絡設備關閉的情況下有效。參數爲int型。
一個小實例

int main(int argc, char *argv[])
{
        int tun, ret;
        char tun_name[IFNAMSIZ];
        unsigned char buf[4096];

         tun_name[0] = '\0';
         tun = tun_create(tun_name, IFF_TUN | IFF_NO_PI);
        if (tun < 0) {
                perror("tun_create");
                return 1;
        }
        printf("TUN name is %s\n", tun_name);

        while (1) {
                unsigned char ip[4];

                 ret = read(tun, buf, sizeof(buf));
                if (ret < 0)
                        break;
                memcpy(ip, &buf[12], 4);
                memcpy(&buf[12], &buf[16], 4);
                memcpy(&buf[16], ip, 4);
                 buf[20] = 0;
                *((unsigned short*)&buf[22]) += 8;
                printf("read %d bytes\n", ret);
                 ret = write(tun, buf, ret);
                printf("write %d bytes\n", ret);
        }

        return 0;
}


以上代碼簡答地處理了ICMP的ECHO包,並回應以ECHO REPLY。

首先運行這個程序:

root@gentux test # ./a.out
TUN name is tun0 
接着在另外一個終端運行如下命令:

root@gentux linux-2.6.15-gentoo # ifconfig tun0 0.0.0.0 up
root@gentux linux-2.6.15-gentoo # route add 10.10.10.1 dev tun0
root@gentux linux-2.6.15-gentoo # ping 10.10.10.1
PING 10.10.10.1 (10.10.10.1) 56(84) bytes of data.
64 bytes from 10.10.10.1: icmp_seq=1 ttl=64 time=1.09 ms
64 bytes from 10.10.10.1: icmp_seq=2 ttl=64 time=5.18 ms
64 bytes from 10.10.10.1: icmp_seq=3 ttl=64 time=3.37 ms

--- 10.10.10.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2011ms
rtt min/avg/max/mdev = 1.097/3.218/5.181/1.671 ms 
可見,我們順利地接受到了迴應包,這時,切回到前一個終端下:

read 84 bytes
write 84 bytes
read 84 bytes
write 84 bytes
read 84 bytes
write 84 bytes 
一切正如我們所預想的那樣。

TUN/TAP能做什麼?

hoho,問這個問題似乎有些傻,你說一個網卡能做什麼?我可以告訴你兩個基於此的開源項目:vtunopenvpn,至於其他的應用,請自由發揮你的想像力吧!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章