Linux中的tap/tun设备

Linux虚拟网络设备tap/tun

tap/tun常用于隧道通讯,通过一个字符设备来实现用户态和内核态的通讯,字符设备一端连接着用户空间,一端连接着内核空间。

与物理网卡的最大不同是,tap/tun的数据源来自于用户态的程序,而物理网卡的数据源来自于物理链路。

对应的字符设备文件位置:

  • tap: /dev/tap0
  • tun: /dev/net/tun

当应用程序打开字符设备文件时,驱动程序会创建并注册相应的虚拟设备接口,以tunX或tapX命名。应用程序关闭设备文件时,驱动程序会删除tunX和tapX网络虚拟设备,并删除建立起来的路由信息。

两个设备的不同点:

  • tap是一个二层网络设备,只能处理二层的以太网帧,可以与物理网卡做桥接
  • tun是一个点对点的三层网络设备,只能处理处理三层的IP数据包,无法与物理网卡做桥接,可以通过三层交换方式与物理网卡连通。Linux下的隧道协议基于该tun设备实现,如ipip、gre。
 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
                ┌──────────────┐
                │              │
                │     APP      │
                │              │
                └───────┬──────┘
           ┌────────────▼──────────┐
           │                       │
─ ─ ─ ─ ─ ─│     /dev/net/tun      ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
           │                       │
           └────────────┬──────────┘
                ┌───────▼──────┐                 ┌──────────────┐
                │              │                 │              │
                │     tunX     ├────────────────▶│Network Stack │
                │              │                 │              │
                └──────────────┘                 └──────────────┘

tun设备应用举例

 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
          ┌──────────────┐         ┌──────────────┐
          │              │         │              │
          │    APP A     │         │    APP B     │◀┐
          │              │         │              │ │
          └───────┬──────┘         └───────┬──────┘ │
                  │                        │        │
                  │                        │        │
                 1│                        │        │
                  │                       5│        │
                  │                        │        │
  ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ┼ ─ ─ ─ ─ ─
                  │                        │        │
                  │                        │       4│
                  │                        │        │
     ┌────────────▼────────────────────────▼─────┐  │
     │                                           │  │
     │               Network Stack               │  │
     │                                           │  │
     └────────────┬───────────────────────┬──────┘  │
                  │                       │         │
                 6│                      3│         │
                  │                       │         │
          ┌───────▼──────┐              ┌─▼─────────┴──┐
          │              │              │              │
10.1.1.11 │     eth0     │              │     tun0     │ 192.168.1.11
          │              │              │              │
          └───────┬──────┘              └──────────────┘
                 7│

             10.1.1.100 / 192.168.1.100

应用程序A要发送数据到其他物理机192.168.1.100,由于物理网络环境下只有10.1.1.11和10.1.1.100是相互连通的,192.168.1.11和192.168.1.100是不通的,为了192.168.1.11和192.168.1.100能够进行通讯,需要将数据包进行一次封装。

应用程序B是通过打开字符设备文件/dev/net/tun0的方式来打开网络设备

流程如下:

  1. A构造数据包,目的ip为192.168.1.100,并发送给协议栈
  2. 协议栈根据数据包中的ip地址,匹配路由规则,要从tun0出去
  3. 内核协议栈将数据包发送给tun0网络设备
  4. tun0发送应用程序B打开,于是将数据发送给应用程序B
  5. B收到数据包后,在用户态构造一个新的数据包,源IP为eth0的IP 10.1.1.11,目的IP为配置的对端10.1.1.100,并封装原来的数据包
  6. 协议栈根据当前数据包的IP地址选择路由,将数据包发送给eth0

reference