全方位揭秘 Linux 虚拟网络设备 TUN/TAP:提升你的网络技术能力
小编之前发过一篇关于 Open vpn 中 TUN 虚拟设备的文章,讲的不够精细,这次讲精细点,顺便一起介绍下它的表兄弟:虚拟网络设备 TAP。让小伙伴彻底了解虚拟网络设备 TUN/TAP。咱们进入正题~引言:为何虚拟网络设备——TUN/TAP至关重要?在Linux网络虚拟化的领域中,TUN/TAP设备发挥着不可或缺的作用。它们是容器网络、VPN技术与虚拟机网络的核心组成部分。深入理解TUN/TAP设备不仅是掌握Linux网络的基础,也是进一步探索网络插件(例如flannel)的关键所在。TUN/TAP设备是Linux内核中用于网络虚拟化的两种虚拟网络设备,主要用于创建和管理虚拟网络接口。- TUN 设备模拟的是一个点对点的网络接口,通常用于实现路由功能。它将网络包从用户空间发送到内核,并支持IP层的协议。
- TUN 设备常用于 VPN(虚拟私人网络)解决方案,能够将数据包封装在安全的隧道中进行传输。
TAP(Terminal Access Point)- TAP 设备则模拟以太网设备,允许以太网帧在用户空间与内核之间传输。它工作在数据链路层,适合处理以太网协议。
- TAP 设备通常用于虚拟机和容器之间的网络通信,可以让不同的虚拟网络设备共享同一物理网络接口。
- tun 设备:虚拟化的是点对点网络接口,工作在网络层(第三层),专门处理IP数据包。由于tun设备没有 MAC 地址,因此无法处理 ARP 请求或以太网广播。
- tap 设备:虚拟化的是以太网接口,工作在数据链路层(第二层),负责处理完整的以太网帧。tap 设备拥有 MAC 地址,可以参与桥接网络,类似于一块虚拟的以太网卡。
- 物理网卡:一端连接物理网络,另一端连接网络协议栈。
- TUN/TAP设备:在操作系统内部将内核网络协议栈与用户空间的应用程序相连接。一端连接应用程序,另一端连接网络协议栈。
在讲解 TUN/TAP 设备之前,我们先简单介绍物理设备上的数据是如何通过 Linux 网络协议栈传递到用户态程序的(参见小面章节中 TUN 设备的工作模式图,涉及应用进程B和应用进程C)。物理网卡接收到网络数据后,将其传送至内核的网络协议栈进行处理。用户态程序通过创建的 Socket 套接字从协议栈中读取数据;同样,在发送数据时,用户程序也通过 Socket 将数据交给协议栈,最终由物理网卡发出。
从网络协议栈的角度来看,TUN/TAP 设备与物理网卡并无区别。不同之处在于,TUN/TAP 设备的数据源并非来自物理链路,而是来自用户态,这也是其最大价值所在。TUN/TAP 设备通过 Linux 的设备文件实现内核态与用户态之间的数据交互。在访问设备文件时,会调用相应的设备驱动例程,因此设备驱动可以视为内核态与用户态之间的一个接口。以下是 TUN 设备的工作模式示意图。普通的物理网卡通过网线收发数据包,而 tun 设备通过一个设备文件(/dev/net/tun)收发数据包。应用的数据收发过程:数据发送:应用进程 A 通过打开 /dev/net/tun 字符设备,并使用 ioctl 调用创建虚拟接口(tunx或tapx)。ioctl调用返回一个文件描述符 fd,应用进程A可以通过该描述符写入格式化的数据。这些数据通过虚拟网卡驱动到达协议栈,对于协议栈而言,这些数据就像是从真实网卡接收到的一样。
数据接收:当网络协议栈将数据发送到虚拟接口(tunx或tapx)时,应用进程 A 可以通过上述创建的设备文件描述符 fd 读取接口发送的数据,然后进行相应的处理。
TAP 设备与TUN 设备的工作原理完全相同,主要区别在于:- TUN 设备的 /dev/net/tun 文件传输的是 IP 包,因此只能工作在网络层(L3),无法与物理网卡进行桥接,但可以通过三层交换(如ip_forward)与物理网卡连接。
- 而 TAP 设备的 /dev/net/tun 文件则处理链路层数据包。从这一点来看,TAP 虚拟设备与真实物理网卡的功能更为接近,可以实现与物理网卡的桥接。
- 无论是 TUN 设备还是 TAP 设备,均通过打开 /dev/net/tun 字符设备文件,并使用 ioctl 系统调用在内核中创建新的TUN或TAP设备。创建的设备不会以文件形式显示在 /dev/ 目录下,而是在 sys/class/net/ 下可以找到对应的网络接口(tunx或tapx)。
- 设备 /dev/net/tun 必须以读写模式打开,且被称为克隆设备,是创建任何 TUN/TAP 虚拟接口的起点。在执行 open 系统调用时,虚拟文件系统(VFS)会为此次操作分配一个独立的内核态文件结构。也就是说,每次打开时,内核为该操作分配的文件结构实例都是不同的,代表不同的字符设备。
TUN/TAP 驱动程序由两个部分组成:字符设备驱动和网卡驱动。- 网卡驱动部分负责接收来自 TCP/IP 协议栈的网络分包,并将其发送出去,或反过来将接收到的网络分包传递给协议栈进行处理。
- 字符设备驱动部分则在内核与用户态之间传输网络分包,模拟物理链路的数据接收和发送。用户态程序通过 ioctl、read 和 write 系统调用与字符设备 /dev/net/tun 进行数据交互。
VPN 客户端程序会创建一个 TUN 设备(如tun0)。当操作系统需要访问远程公司内网时,目标地址为内网的IP数据包会被路由到 tun0 设备。随后,VPN 程序从 tun0 的文件描述符中读取该原始IP数据包,对其进行加密和封装(例如,封装成 UDP 包),最终通过真实的物理网卡发送给 VPN 服务器。服务器在解密后,将原始 IP 包注入其网络栈,确保数据最终送达目标内网。这一过程为用户创建了一条安全的“网络隧道”。在虚拟化环境中,TAP 设备发挥着关键作用。当 QEMU 等虚拟机管理器启动虚拟机时,通常会在宿主机上创建一个 TAP 设备(如tap0),作为虚拟机的“网卡接口”。该 tap0 设备会被添加到宿主机的 Linux 网桥(如br0)中,同时物理网卡也可能加入此网桥。这样,虚拟机发出的以太网数据帧通过 TAP 设备被宿主机上的网络程序(如QEMU)读取,并转发到网桥,从而实现与外部网络或其他虚拟机的通信,仿佛是在使用宿主机上的真实设备。这种机制也是许多容器网络方案的基础。TUN/TAP 设备巧妙地利用了 Linux “一切皆文件”的哲学,通过字符设备文件 /dev/net/tun 在内核网络协议栈与用户空间程序之间架起了一座桥梁。尽管它们完全由软件实现,但在内核看来,其行为与物理网卡无异。其核心价值在于将网络数据包的处理能力开放给用户空间。这种设计带来了极大的灵活性,催生了众多强大应用,如VPN、虚拟化网络和云原生基础设施等。