之前有尝试使用过packetdrill
进行内核网络协议栈的测试,处于好奇想了解其底层是如何实现的,顺藤摸瓜,最终了解到 Linux 虚拟网络设备 TUN/TAP。
TUN / TAP
TUN/TAP
是 Linux 操作系统内核中的虚拟网络设备,为用户空间应用提供数据包的接收和传输能力,可以使应用实现简单的点到点数据包传输,与普通的物理网络传输不同,数据包的发送者和接收者都是用户空间应用。
简单理解,TUN/TAP
是虚拟化的网络设备,与物理网络设备的不同点在于,其一端是内核网络协议栈,另一端是用户空间应用。其中,TUN
与TAP
的区别在于,TUN
工作在三层,可以操作 IP 数据包;TAP
工作在二层,可以操作以太网帧。
我们可以简单对比物理设备、TUN/TAP
在数据传输过程中的不同:
设备配置
一般 Linux 系统会创建默认tun
字符设备文件,在目录/dev/net
下。如果没有的话也没关系,我们可以使用mknod
手动创建一个。
1 |
|
主次设备号必须是
10
和200
吗?是的,和内核的设备码定义有关。内核定义设备码中, TUN/TAP 设备的主设备号就是
10
,次设备号是200
。详细信息可以参考内核源码附带的文档,在admin-guide/devices.txt
中,记录了所有设备码的定义。
紧接着,我们需要创建tun
虚拟网络设备。
1 |
|
此时,通过ifconfig
就可以看到新的网络设备tun
。
tun
字符设备文件或虚拟网络设备的名字是固定的吗?并不是,字符设备文件和虚拟网络设备都可以使用其他名字。我做了一个简单测试,修改了
packetdrill
源码(platforms.h)中定义的tun
设备路径和名字。重新编译后,使用新名字的tun
设备依然有效。下面的代码例子也证明了虚拟网络设备可以使用其他名字代替。
接口开发
我们可以通过mknod
创建属于自己的字符设备文件,并通过ip tuntap
创建虚拟网络tun
设备,并通过正常的设备读写接口进行操作。
举个栗子,我们来通过tun
设备实现 ICMP 请求应答过程,同时我们换个方式,通过代码来创建设备,先来看看整个过程。
首先,需要创建了新的设备描述文件/dev/net/mmq
1 |
|
并通过代码创建虚拟网络设备mmq-eth
,并与设备描述文件/dev/net/mmq
进行关联。
1 |
|
通过得到的tun
设备文件描述符,我们可以进行基础的读写,完成整个 ICMP 请求和应答过程。
1 |
|
程序成功运行后,我们可以在网络设备列表中找到对应的设备。
1 |
|
此时,我们需要往mmq-eth
发送ICMP请求报文,需要先开启设备,并将在 IP 路由表中创建一条到该设备的 IP路由记录。
1 |
|
再通过ping
命令发送报文,就可以看到整个 ICMP 请求应答过程。
(GitHub 代码地址:Taaang/tun_tap_test · GitHub)
总结
TUN/TAP
为用户空间应用,提供了网络协议栈中不同层的数据包传输,tun
提供简单的点对点数据包传输,tap
则在以太网层进行数据帧传输,其主要应用于 VPN、 协议加密和压缩、隧道等。