linux網絡程序設計——tun&tap虛擬網卡使用

基本概念

A gateway to userspace。

     TUN和TAP設備是Linux內核虛擬網絡設備,純軟件實現。 
     OS向連接到TUN/TAP設備的用戶空間程序發送報文;用戶空間程序可像物理口發送報文那像向TUN/TAP口發送報文,在這種情況下,TUN/TAP設備發送(或注入)報文到OS協議棧,就像報文從物理端口收到一樣。 

 

鏈接:

TUN/TAP: The user-space application/VM can read or write an ethernet frame to the tap interface and it would reach the host kernel, where it would be handled like any other ethernet frame that reached the kernel via physical (e.g. eth0) ports. You can potentially add it to a software-bridge (e.g. linux-bridge)

如何工作

     TUN/TAP 爲簡單的點對點或以太網設備,不是從物理介質接收數據包,而是從用戶空間程序接收;不是通過物理介質發送數據包,而是將它們發送到用戶空間程序。 
     假設您在 tap0 上配置 IPX,那麼每當內核向 tap0 發送一個 IPX 數據包時,它將傳遞給應用程序(例如 VTun)。應用程序加密、壓縮數據包,並通過 TCP/UDP 發送到對端。對端的應用程序解壓縮、解密接收的數據包,並將數據包寫入 TAP 設備,然後內核處理數據包,就像該數據包來自真實的物理設備。 
     在Linux內核中添加了一個TUN/TAP虛擬網絡設備的驅動程序和一個與之相關的字符設備/dev/net/tun,字符設備tun作爲用戶空間和內核空間交換數據的接口。 
     用戶空間的應用程序可以通過這個設備文件來和內核中的驅動程序進行交互,其他操作方式和普通文件操作無異。當內核將數據包發送到虛擬網絡設備時,數據包被保存在設備相關的一個隊列中,直到用戶空間程序通過打開的字符設備tun的描述符讀取時,它纔會被拷貝到用戶空間的緩衝區中,其效果就相當於,數據包直接發送到了用戶空間。通過系統調用write發送數據包時其原理與此類似。 
     tun/tap驅動程序中包含兩部分:字符設備驅動和網卡驅動。利用網卡驅動部分接受來自tcp/ip協議棧的網絡分包併發送或者反過來將接收到的網絡分包傳給協議棧處理。而字符設備驅動部門將網絡分包在內核與用戶態之間傳送,模擬物理鏈路的數據接受和發送。tun/tap驅動很好的實現了兩種驅動的結合。

用途

     用於加密、VPN、隧道、虛擬機等等(encryption, VPN, tunneling,virtual machines)。 
     tun/tap設備的用處是將協議棧中的部分數據包轉發給用戶空間的應用程序,給用戶空間的程序一個處理數據包的機會。於是比較常用的數據壓縮、加密等功能就可以在應用程序中實現。tun/tap設備最常用的場景是VPN,包括tunnel以及應用層的IPSec等。

tap/tun在libvirt中的應用


VPN


其他

常用命令

root@ubuntu:~# ip tuntap help

Usage: ip tuntap { add | del } [ dev PHYS_DEV ]

          [ mode { tun | tap } ] [ user USER ] [ group GROUP ]

          [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ]

 

Where: USER  := { STRING | NUMBER }

       GROUP := { STRING | NUMBER }

 

veth、tun、tap比對



      tun 是點對點的設備 , 而 tap 是一個普通的以太網卡設備 。 也就是說 ,tun 設備其實完全不需要有物理地址的 。 它收到和發出的包不需要 arp, 也不需要有數據鏈路層的頭 。 而 tap 設備則是有完整的物理地址和完整的以太網幀 。

TAP (network tap) operates much like TUN however instead of only being able to write and receive layer 3 packets to/from the file descriptor it can do so with raw ethernet packets. You will typically see tap devices used by KVM/Qemu virtualization, where a TAP device is assigned to a virtual guests interface during creation.


TUN(Tunel)設備模擬網絡層設備,處理三層報文,如IP報文。TAP設備模型鏈路層設備,處理二層報文,比如以太網幀。TUN用於路由,而TAP用於創建網橋。 

示例(來源於網絡)

示例程序

收到tun設備的數據包之後,只打印出收到了多少字節的數據包,其它的什麼都不做

#include <net/if.h>

#include <sys/ioctl.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <string.h>

#include <sys/types.h>

#include <linux/if_tun.h>

#include <stdlib.h>

#include <stdio.h>

 

int tun_alloc(int flags)

{

    struct ifreq ifr;

    int fd, err;

    char *clonedev = "/dev/net/tun";

 

    if ((fd = open(clonedev, O_RDWR)) < 0) {

        return fd;

    }

 

    memset(&ifr, 0, sizeof(ifr));

    ifr.ifr_flags = flags;

 

    if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {

        close(fd);

        return err;

    }

 

    printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name);

 

    return fd;

}

 

int main()

{

    int tun_fd, nread;

    char buffer[1500];

 

    /* Flags: IFF_TUN   - TUN device (no Ethernet headers)

     *        IFF_TAP   - TAP device

     *        IFF_NO_PI - Do not provide packet information

     */

    tun_fd = tun_alloc(IFF_TUN | IFF_NO_PI);

 

    if (tun_fd < 0) {

        perror("Allocating interface");

        exit(1);

    }

 

    while (1) {

        nread = read(tun_fd, buffer, sizeof(buffer));

        if (nread < 0) {

            perror("Reading from interface");

            close(tun_fd);

            exit(1);

        }

 

        printf("Read %d bytes from tun/tap device\n", nread);

    }

    return 0;

}

 

演示

#--------------------------第一個shell窗口----------------------

#將上面的程序保存成tun.c,然後編譯

root@ubuntu:~# gcc tun.c -o tun

#啓動tun程序,程序會創建一個新的tun設備,

#程序會阻塞在這裏,等着數據包過來

root@ubuntu:~# ./tun

Open tun/tap device: tun0 for reading...

Read 84 bytes from tun/tap device

Read 84 bytes from tun/tap device

Read 84 bytes from tun/tap device

Read 84 bytes from tun/tap device

 

 

#--------------------------第二個shell窗口---------------------

-#啓動抓包程序,抓經過tun0的包

root@ubuntu:/home/sunld# tcpdump -i tun0

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes

02:53:08.840817 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 1, length 64

02:53:09.839871 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 2, length 64

02:53:10.850205 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 3, length 64

02:53:11.851285 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 4, length 64

 

#--------------------------第三個shell窗口----------------------

#./tun啓動之後,通過ip link命令就會發現系統多了一個tun設備,27: tun0: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 500

    link/none #新的設備沒有ip,我們先給tun0配上IP地址

root@ubuntu:/home/sunld# ip addr add 192.168.209.138/24 dev tun0#默認情況下,tun0沒有起來,用下面的命令將tun0啓動起來

root@ubuntu:/home/sunld# ip link set dev tun0 up

#嘗試ping一下192.168.209.0/24網段的IP,

#由於我們的程序中收到數據包後,啥都沒幹,相當於把數據包丟棄了,

#所以這裏的ping根本收不到返回包,

#但在前兩個窗口中可以看到這裏發出去的四個icmp echo請求包,

#說明數據包正確的發送到了應用程序裏面,只是應用程序沒有處理該包

root@ubuntu:/home/sunld# ping -c 4 192.168.209.139 -I tun0

PING 192.168.209.139 (192.168.209.139) from 192.168.209.138 tun0: 56(84) bytes of data.

參考資料

kernel doc tuntap

virtual networking devices in linux

Linux Networking Explained

Tun/Tap interface tutorial

TUN, TAP and Veth - Virtual Networking Devices Explained

虛擬機網卡和linux bridge上tap設備的關係

Linux虛擬網絡設備之tun/tap

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章