linux Tun/Tap 虚拟网卡
一、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
应用B是代理程序。应用A为用户端程序(如浏览器),应用A的发出的数据包由tun0接收(通过路由表拦截),tun0将其转发给应用B,应用B处理后再发给以太网卡eth0,eth0发送给物理网络。
根据应用B的处理方式不同,可以有不同的应用。
二、应用
实现点对点隧道(类似openvpn):需要两台服务器分别实现TUN 设备的接入
这里外层IP数据包用于两台TUN设备通信,内层数据包包含中原始的访问请求。从而实现端到端的数据的传输。
实现全局代理(类似leaf):
前提:应用程序可以通过 read(fd, buf, len) 从 /dev/net/tun 获得完整 IP 数据报,再通过 write(tun_fd, buf, n) 修改 IP 数据报。至此,应用程序便有了控制 IP 层数据报的能力。
问题:如何直接拦截与解析应用层的数据包?
方案一:直接从TUN 读到的原始IP数据报中提取TCP/UDP头与应用层负载(此外,可以直接从IP数据包中读取目标IP地址,如果应用层解析不到的话)。这部分可以借用一些工具库去处理,比如 libcap等。
方案二:应用从 TUN 读取原始 IP 报文,若是发往某服务的请求,就修改报文的目标 IP/端口为本地监听地址,然后 write 回 TUN。内核收到写回 TUN 的数据后,会按照原生流程拆 IP/TCP 头并将纯净的应用层负载送入对应的 socket 缓冲区。此时,应用只需基于普通的 socket(AF_INET, SOCK_STREAM) 或 recv() 接口读取即可拿到应用层数据。如 leaf,先在用户态解析 DNS 和 IP,返回一个假 IP(通常在私有网段),数据包就会经过内核程序再发送回来,再由leaf去处理应用层的数据。
总结:方案二,将网络层数据拆包的处理交给内核态的网络协议栈处理,对于只关心应用层数据包的程序来说更加方便与高效。
参考资料
https://www.zhaohuabing.com/post/2020-02-24-linux-taptun
https://segmentfault.com/a/1190000009249039