tun_tap设备

Linux 虚拟网络设备 tun/tap

物理设备和虚拟设备的区别

Linux 内核中有一个网络设备管理层,处于网络设备驱动和协议栈之间,负责衔接它们之间的数据交互。

假设一个物理网卡 eth0,它的两端分别是内核协议栈和外部物理网络。从物理网络接收到的数据,会转发给内核协议栈,而应用程序从协议栈发过来的数据会通过物理网络发送出去。

如果对于一个虚拟网络设备,和物理网络类似都是网络设备,分配有 IP地址,从外部网络发送来的数据都会转发给协议栈,从协议栈过来的数据都会转发给外部网络的相关网络设备。所以说虚拟网络设备的一端是协议栈,另一端却决于网络设备的类型。

tun/tap 工作原理

+----------------------------------------------------------------+
|                                                                |
|  +--------------------+      +--------------------+            |
|  | User Application A |      | User Application B |<-----+     |
|  +--------------------+      +--------------------+      |     |
|               | 1                    | 5                 |     |
|...............|......................|...................|.....|
|               ↓                      ↓                   |     |
|         +----------+           +----------+              |     |
|         | socket A |           | socket B |              |     |
|         +----------+           +----------+              |     |
|                 | 2               | 6                    |     |
|.................|.................|......................|.....|
|                 ↓                 ↓                      |     |
|             +------------------------+                 4 |     |
|             | Newwork Protocol Stack |                   |     |
|             +------------------------+                   |     |
|                | 7                 | 3                   |     |
|................|...................|.....................|.....|
|                ↓                   ↓                     |     |
|        +----------------+    +----------------+          |     |
|        |      eth0      |    |      tun0      |          |     |
|        +----------------+    +----------------+          |     |
|    10.32.0.11  |                   |   192.168.3.11      |     |
|                | 8                 +---------------------+     |
|                |                                               |
+----------------|-----------------------------------------------+
                 ↓
         Physical Network

上图两个应用程序 A、B 通过网络协议栈、物理网卡 eth0、虚拟网络设备 tun0 来交互通信,这里 tun0 是一个虚拟网络设备,可以看出它和物理设备的区别。物理网卡 eth0 的两端分别是物理网络和网络协议栈,虚拟网络设备 tun0 的两端分别是用户层应用程序和网络协议栈。协议栈发送给 tun0 的数据包能够被这个应用程序读取,并且应用程序能够向 tun0 写数据。

假设 eth0 的Ip为 10.32.0.11tun0 的 IP为 192.168.3.11,数据包流通过程如下:

  • 应用程序A 通过 socketA 发送数据包,数据包的目的 IP 地址为 192.168.3.11
  • 网络协议栈根据数据包的目的 IP 地址,匹配本地路由规则,知道这个数据包应该由 tun0 发送出去,转发数据包到 tun0
  • tun0 接收到数据包后执行一些业务操作,然后生成新的数据包,将原数据包内容嵌入到新数据包中,最后通过 socket 转发出去,这个时候新数据包的源地址变为 eth0 地址,而目的地址变为 10.33.0.1
  • 数据包经过 socketB 发送给网络协议栈,网络协议栈根据本地路由将数据包通过 eth0 发送出去,将数据包发送给外部网络。

图中 tun0 接收应用程序的数据包,执行相关业务操作(压缩/加密)后重新发送到外部网络。10.33.0.1 接收到数据包后会打开数据包并读取里面的原始数据包,转发给本地的 192.168.3.1 然后等收到应答后再构造新的应答数据包,并将原始应答封装由原路径返回给应用程序B,应用程序B 取出里面的原始应答包,最后返回给应用程序A。

tun/tap 设备的作用

tun/tap 设备的作用是将协议栈的部分转发给用户空间应用程序,给用户空间一个处理数据包的机会。于是常规的数据压缩、数据加密功能可以在应用程序中执行。比如常用的场景是VPN,包括tunnel以及应用层的IPSec等,比较有名的项目是VTun。

VPN 示例

示例说明

现有北京总部服务器(IP地址:192.168.3.2/24、公网IP:10.1.1.2)、天津总部服务器(IP地址:192.168.2.1、公网IP:20.1.1.2),现在天津公司需要访问北京公司服务器,但是出于安全考虑,天津公司只能以 192.168.3.2 这个 IP 地址来访问北京公司。

但是两个部门之间没有网线连接,无法以内网IP 相互访问,那么我们可以虚拟出一条网线出来,这个技术就是 VPN(Virtual Private Network)。该技术的核心在于如何构建该线路,这条线路被称为隧道(Tunnel)技术,形象地来说就是在复杂的网络环境中打通了一条隧道,将两个公司连接起来。隧道的两个出口对应着两个公司的公网 IP 地址。

详细配置

# 配置北京公司
ip address 192.168.5.1 255.255.255.0
tunnel source 10.1.1.2
tunnel destination 20.1.1.12

# 配置天津公司
ip address 192.168.5.2 255.255.255.0
tunnel source 20.1.1.12
tunnel destination 10.1.1.2

按上述配置隧道,但是两地的私网还是无法互通。查看路由表信息如下:

ip route

Gateway of last resort is 20.1.1.1 to network 0.0.0.0
   20.1.1.0 is directly connected, FastEthernet0/0
   192.168.5.0/24 is directly connected, Tunnel0
   192.168.2.0/24 is directly connected, FastEthernet1/0
   0.0.0.0/0 [1/0] via 20.1.1.1

分析路由表得知出口路由表上没有去往北京的路由,也就是说需要在路由表上添加相应的路由。

# 天津机器
# 发往 192.168.3.0 的数据包都由 tunnel0 转发
ip route 192.168.3.0 255.255.255.0 tunnel 0

# 北京机器
# 发往 192.168.2.0 的数据包都由 tunnel0 转发
ip route 192.168.2.0 255.255.255.0 tunnel 0

最后测试连接发现通过,天津PC已经能够访问北京的服务器了,以上即为VPN的雏形,因为我们知道数据物理上依然还是走的公网,我们耳熟能详的 IPsec VPN,其中一中形式就是基于以上的隧道,对数据进行加密保护的一种VPN。而我们通常把这种通过两边的路由器建立隧道然后实现两个私网网段互通的VPN的形态称为端到端(site-to-site)VPN。总的来说 VPN 的实现就是 建立隧道+建立路由 的过程。


示例程序

#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;
}

测试 tun/tap 设备:

#--------------------------第一个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

参考

https://baijiahao.baidu.com/s?id=1637664009162027887&wfr=spider&for=pc

https://blog.csdn.net/a369189453/article/details/81193661

https://zhuanlan.zhihu.com/p/388742230

posted @ 2024-02-17 15:54  Stitches  阅读(131)  评论(0)    收藏  举报