Linux虚拟网络之 tun/tap、ipvlan/macvlan、ipvtap/macvtap
Last updated
Was this helpful?
Last updated
Was this helpful?
了解云原生的同学应该都了解过 flannel 的 overlay 网络模型吧,overlay 模型中有一种是 UDP 模式,虽然因为性能差被弃用了,但也是最典型的容器跨主机网络方案。
这个图大家都不陌生吧,这个是张磊的《深入剖析 Kubernetes》中的网络解析,可以看到 Flannel 进行 UDP 的封包与解包都是在用户态通过 tun 设备(flannel0)来实现的,这个 flannel0 就是今天咱们要讲的主角之一,tun 设备。
tun/tap 设备是操作系统内核中的虚拟网络设备,是用软件模拟的网络设备,提供与硬件网络设备完全相同的功能。主要用于用户空间和内核空间传递报文。
如上图所示:
对于硬件网络设备而言,一端连接的是物理网络,一端连接的是网络协议栈。
对于 tun/tap 设备而言,一端连接的是应用程序(通过 文件 /net/dev/tun
),一端连接的是网络协议栈
从上图可以更直观的看出 tun/tap 设备和物理设备的区别:虽然它们的一端都是连着网络协议栈,但是物理网卡另一端连接的是物理网络,而 tun/tap
设备另一端连接的是一个 应用层程序,这样协议栈发送给 tun/tap
的数据包就可以被这个应用程序读取到,此时这个应用程序可以对数据包进行一些自定义的修改(比如封装成 UDP
),然后又通过网络协议栈发送出去——其实这就是目前大多数 代理 的工作原理。
Tun/tap 设备提供的虚拟网卡驱动,从tcp/ip协议栈的角度而言,它与真实网卡驱动并没有区别。
tun/tap 有两种模式,tun 模式 与 tap 模式。tun 设备与 tap 设备工作方式完全相同,区别在于:
Tun 设备是一个三层设备,从/dev/net/tun字符设备上读取的是IP数据包,写入的也只能是IP数据包,因此不能进行二层操作,如发送ARP请求和以太网广播。
Tap设备是二层设备,处理的是二层 MAC 层数据帧,从/dev/net/tun字符设备上读取的是MAC 层数据帧,写入的也只能是 MAC 层数据帧。从这点来看,Tap虚拟设备和真实的物理网卡的能力更接近,可以与物理网卡做 bridge。
注意,无论是 tun 还是 tap 设备,都是通过 open
/dev/net/tun
这个字符设备文件,通过 ioctl 系统调用在内核创建新的 tun 、tap 设备,创建的设备并不会以文件的形式出现在/dev/
下,可以在sys/class/net/
下看到对应的网络接口 tunx 或者 tapx。设备/dev/net/tun必须以read/write的方式打开。 该设备也被称为克隆设备,它是创建任何tun/tap虚拟接口的起点
open系统调用执行的时候,VFS会为这次open分配一个独立的内核态file结构,也就是说,每次打开执行时,内核为此次打开分配的file结构实例不同,代表不同的字符设备。
应用的数据收发过程:
数据发送:应用进程 A open/dev/net/tun
字符设备,通过 ioctl 调用创建虚拟接口 tunx 或者 tapx, ioctl 调用返回表示对应 tunx 或者 tapx 设备的文件描述符 fd ,应用 A 通过这个文件描述符 fd 写入格式化的数据,数据通过虚拟网卡驱动到达协议栈,对于协议栈来说,这个数据就像从真实网卡接收的一样
数据接收:当网络协议栈发送数据到虚拟接口 tunx 或者 tapx时,应用进程 A 通过上述创建的设备文件描述符 fd,从中读取接口发送的数据,然后进行处理
除了在应用中通过克隆设备 /dev/net/tun
和 ioctl 系统调用创建虚拟设备,还可以通过 ip tuntap
命令创建。
Tun/tap驱动程序中包含两个部分,一部分是字符设备驱动,还有一部分是网卡驱动。
利用网卡驱动部分接收来自TCP/IP协议栈的网络分包并发送或者反过来将接收到的网络分包传给协议栈处理。
字符驱动部分则将网络分包在内核与用户态之间传送,模拟物理链路的数据接收和发送。用户态程序通过 ioctl
read write 系统调用 与字符设备/dev/net/tun
进行数据交互。
通过 modinfo tun
和 modinfo tap
查看 tun/tap 设备驱动
接收数据流程
接收机制如上图所示,黑线是公网IP,红线是解密后的内网IP包。vpn 进程监听的是公网 ip + 端口,数据包经过网卡到达协议栈,到达 vpn 进程,vpn 进程解密解包后,将数据通过 字符设备文件发送给虚拟设备,再次经过协议栈的路由,最终将数据发到用户程序。
发送数据流程
发送机制如上图所示,红线是内网IP,黑色线是加密过后的公网IP包。应用程序发送目的 ip 为 内网 ip 的数据包,到达虚拟网卡,转到字符设备文件,被 vpn进程读取到。经过封包加密后,通过协议栈路由到网卡,最终通过公网 网卡发送出去。
以 qemu 为例,qemu 启动时通过 qemu -netdev tap
指定 tap 设备
如图中所示,红色箭头表示数据报文的入方向,步骤:
网络数据从 Host 上的物理网卡接收,到达网桥;
由于 eth0 与 tap1 均加入网桥中,根据二层转发原则,br0 将数据从 tap1 口转发出去,即数据由 Tap设备接收;
Tap 设备通知对应的 fd 数据可读;
fd 的读动作通过 tap 设备的字符设备驱动将数据拷贝到用户空间,完成数据报文的前端接收。
macvlan 本身是 linux kernel 模块,允许在主机的一个网络接口上配置多个虚拟的网络接口,即多个 interface, 每个 interface 有自己独立的 MAC 地址,也可以配置自己的 IP。
macvlan 这种技术听起来有点像 VLAN,但它们的实现机制是完全不一样的。macvlan 子接口和原来的主接口是完全独立的,可以单独配置 MAC 地址和 IP 地址,而 VLAN 子接口和主接口共用相同的 MAC 地址。VLAN 用来划分广播域,而 macvlan 共享同一个广播域。
MACVLAN 会根据收到包的目的 MAC 地址判断这个包需要交给哪个虚拟网卡。单独使用 MACVLAN 好像毫无意义,但是配合之前介绍的 network namespace 使用,我们可以构建这样的网络:
根据 macvlan 子接口之间的通信模式,macvlan 有四种网络模式:
private 模式
vepa(virtual ethernet port aggregator) 模式
bridge 模式
passthru 模式
默认使用的是 vepa 模式。
Private: 即使交换机支持内部转发,也不允许同一物理接口上的MACVLAN实例之间进行通信。
VEPA: 允许在同一物理接口上从一个MACVLAN实例到另一个MACVLAN实例的数据通过该物理接口传输。需要连接的交换机支持,或者必须有TCP / IP路由器转发数据包才能进行通信
Bridge: 所有端点都通过物理接口通过简单的桥接器直接相互连接。
Passthru: 允许将单个VM直接连接到物理接口。
IPVlan 和 macvlan 类似,都是从一个主机接口虚拟出多个虚拟网络接口。区别就是,所有的虚拟接口共享相同的 MAC 地址,而拥有不同的 ip 地址。如图所示,左边是 macvlan,右边是ipvlan
IPVLAN 有以下几种模式:
L2 模式
IPVLAN L2 模式 ,有点类似于 macvlan,虚拟设备可以接受和响应 ARP 请求。流量在虚拟设备所在的 network namespce (容器 network namespace)才会经过完整的内核协议栈。流量(入口流量与出口流量)在宿主机 network namespce 命名空间中没有执行 netfilter
链,只处理数据包在网络设备上的收发过程。父接口扮演的是交换机的角色,同一个网络的子接口可以通过父接口来转发数据。使用L2 模式会提供良好的性能,但对网络流量的控制要小。限制是子接口与父接口需配置相同网段的ip。
L3 模式
在 L3 模式 中,虚拟设备只处理 L3 以上的流量。虚拟设备不响应 ARP 请求,用户必须手动为相关点上的 IPVLAN IP 地址配置邻居条目。容器的出口流量会经过宿主机network namespce 命名空间的 netfilter
POSTROUTING 和 OUTPUT 链上,入口流量方式与 L2 模式 相同。使用L3 模式会提供很好的控制,但可能会降低网络流量性能。
L3S 模式
在 L3S 模式 中,虚拟设备处理方式与 L3 模式 相同,唯一的不同是,相关容器的出口和入口流量都经过 宿主机 network namespce 命名空间的 netfilter
chain 。L3S 模式 的行为方式和 L3 模式 相似,但提供了对网络的更大控制。但是性能也最差
MACVTAP 是 MACVLAN 的改进,把 MACVLAN 与 TAP 设备的特点综合一下,使用 MACVLAN 的方式收发数据包,但是收到的包不交给 network stack 处理,而是生成一个 /dev/tapX 文件,交给这个文件:
跟macvtap设备类似 ipvtap设备 是将ipvlan设备和tap字符设备结合一起。
具体可参考