The TUN/TAP virtual network device is connected to the protocol stack at one end, and at the other end is not a physical network, but another application in user space. That is, the packets sent from the protocol stack to the TUN/TAP can be read by the application, and of course the application can send packets directly to the TUN/TAP.

A typical TUN/TAP example is shown in the following figure.

TUN/TAP example

In the above figure we have configured a physical NIC with IP 18.12.0.92 and tun0 is a TUN/TAP device with IP 10.0.0.12. The packets flow in the following directions.

  1. application A sends a packet through socket A. Assume that the destination IP address of this packet is 10.0.0.22.
  2. socket A drops the packet to the protocol stack.
  3. the protocol stack sends the packet to the tun0 device according to the local routing rules and the destination IP of the packet
  4. tun0 receives the packet and forwards it to application B in user space
  5. application B receives the packet and constructs a new packet, embedding the original packet in the new packet (IPIP packet), and finally forwards the packet through socket B.

    Note: The source address of the new packet becomes the eth0 address, while the destination IP address becomes another address 18.13.0.91.

  6. socket B sends the packet to the protocol stack
  7. The protocol stack decides that this packet is to be sent out through eth0 based on the local routing rules and the destination IP of the packet, and forwards the packet to eth0.
  8. eth0 sends the packet out over the physical network.

We see that the network packet sent to 10.0.0.22 is sent to 18.13.0.91 on the remote network via application B in user space using 18.12.0.92, and the network packet arrives at 18.13.0.91, reads the original packet inside, reads the original packet inside, and forwards it to the local 10.0.0.22 . This is the basic principle of VPN.

Using TUN/TAP devices we have the opportunity to forward some of the packets in the protocol stack to the application in user space and let the application process the packets. Common usage scenarios include data compression, encryption and other functions.

Note: The difference between TUN and TAP devices is that TUN device is a virtual end-to-end IP layer device, which means that user-space applications can only read and write IP network packets (layer 3) through TUN device, while TAP device is a virtual link layer device, which can read and write link layer packets (layer 2) through TAP device. Use -dev tun and -dev tap to differentiate when creating devices with the -ip command.

Code example

Here a program is written, it receives the packet from tun device, only print out how many bytes of packet received, nothing else is done, how to program please refer to the reference link at the back.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#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;
}

Virtual Device Demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#--------------------------第一个shell窗口----------------------
#将上面的程序保存成tun.c,然后编译
dev@debian:~$ gcc tun.c -o tun

#启动tun程序,程序会创建一个新的tun设备,
#程序会阻塞在这里,等着数据包过来
dev@debian:~$ sudo ./tun
Open tun/tap device tun1 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窗口----------------------
#启动抓包程序,抓经过tun1的包
# tcpdump -i tun1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun1, link-type RAW (Raw IP), capture size 262144 bytes
19:57:13.473101 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 1, length 64
19:57:14.480362 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 2, length 64
19:57:15.488246 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 3, length 64
19:57:16.496241 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 4, length 64

#--------------------------第三个shell窗口----------------------
#./tun启动之后,通过ip link命令就会发现系统多了一个tun设备,
#在我的测试环境中,多出来的设备名称叫tun1,在你的环境中可能叫tun0
#新的设备没有ip,我们先给tun1配上IP地址
dev@debian:~$ sudo ip addr add 192.168.3.11/24 dev tun1

#默认情况下,tun1没有起来,用下面的命令将tun1启动起来
dev@debian:~$ sudo ip link set tun1 up

#尝试ping一下192.168.3.0/24网段的IP,
#根据默认路由,该数据包会走tun1设备,
#由于我们的程序中收到数据包后,啥都没干,相当于把数据包丢弃了,
#所以这里的ping根本收不到返回包,
#但在前两个窗口中可以看到这里发出去的四个icmp echo请求包,
#说明数据包正确的发送到了应用程序里面,只是应用程序没有处理该包
dev@debian:~$ ping -c 4 192.168.3.12
PING 192.168.3.12 (192.168.3.12) 56(84) bytes of data.

--- 192.168.3.12 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3023ms

Reference


Reference https://houmin.cc/posts/c892b507/