云原生虚拟网络 tun/tap & veth-pair

云原生虚拟网络 tun/tap & veth-pair

转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/684
以前在研究 k8s 网络的时候,很多东西都看不太懂,只是蜻蜓点水过一下,这段时间打算恶补一下虚拟网络方面的知识,感兴趣的不妨一起探讨学习一下 。
概述目前主流的虚拟网卡方案有tun/tap和veth两种 。在时间上 tun/tap 出现得更早,在 Linux Kernel 2.4 版之后发布的内核都会默认编译 tun/tap 的驱动 。并且tun/tap 应用非常广泛 , 其中云原生虚拟网络中,flannel 的 UDP 模式中的 flannel0 就是一个 tun 设备,OpenVPN 也利用到了 tun/tap 进行数据的转发 。
veth 是另一种主流的虚拟网卡方案 , 在 Linux Kernel 2.6 版本 , Linux 开始支持网络名空间隔离的同时,也提供了专门的虚拟以太网(Virtual Ethernet,习惯简写做 veth)让两个隔离的网络名称空间之间可以互相通信 。veth 实际上不是一个设备 , 而是一对设备,因而也常被称作 Veth-Pair 。
Docker 中的 Bridge 模式就是依靠 veth-pair 连接到 docker0 网桥上与宿主机乃至外界的其他机器通信的 。
云原生虚拟网络 tun/tap & veth-pair

文章插图
tun/taptun 和 tap 是两个相对独立的虚拟网络设备,它们作为虚拟网卡,除了不具备物理网卡的硬件功能外,它们和物理网卡的功能是一样的,此外tun/tap负责在内核网络协议栈和用户空间之间传输数据 。
  • tun 设备是一个三层网络层设备,从 /dev/net/tun 字符设备上读取的是 IP 数据包,写入的也只能是 IP 数据包,因此常用于一些点对点IP隧道,例如OpenVPN,IPSec等;
  • tap 设备是二层链路层设备,等同于一个以太网设备,从 /dev/tap0 字符设备上读取 MAC 层数据?。慈氲囊仓荒苁?MAC 层数据?。虼顺S美醋魑槟饣D馔ㄊ褂茫?/li>

云原生虚拟网络 tun/tap & veth-pair

文章插图
从上面图中,我们可以看出物理网卡和 tun/tap 设备模拟的虚拟网卡的区别,虽然它们的一端都是连着网络协议栈,但是物理网卡另一端连接的是物理网络,而 tun/tap 设备另一端连接的是一个文件作为传输通道 。
根据前面的介绍,我们大约知道虚拟网卡主要有两个功能,一个是连接其它设备(虚拟网卡或物理网卡)和 Bridge 这是 tap 设备的作用;另一个是提供用户空间程序去收发虚拟网卡上的数据,这是 tun 设备的作用 。
主要区别是因为它们作用在不同的网络协议层,换句话说 tap设备是一个二层设备所以通常接入到 Bridge上作为局域网的一个节点,tun设备是一个三层设备通常用来实现 vpn 。
OpenVPN 使用 tun 设备收发数据OpenVPN 是使用 tun 设备的常见例子,它可以方便的在不同网络访问场所之间搭建类似于局域网的专用网络通道 。其核心机制就是在 OpenVPN 服务器和客户端所在的计算机上都安装一个 tun 设备,通过其虚拟 IP 实现相互访问 。
例如公网上的两个主机节点A、B , 物理网卡上配置的IP分别是 ipA_eth0 和 ipB_eth0 。然后在A、B两个节点上分别运行 openvpn 的客户端和服务端 , 它们会在自己的节点上创建 tun 设备 , 且都会读取或写入这个 tun 设备 。
假设这两个设备对应的虚拟 IP 是 ipA_tun0 和 ipB_tun0,那么节点 B 上面的应用程序想要通过虚拟 IP 对节点 A 通信,那么数据包流向就是:
云原生虚拟网络 tun/tap & veth-pair

文章插图
用户进程对 ipA_tun0 发起请求,经过路由决策后内核将数据从网络协议栈写入 tun0 设备;然后 OpenVPN 从字符设备文件中读取 tun0 设备数据,将数据请求发出去;内核网络协议栈根据路由决策将数据从本机的 eth0 接口流出发往 ipA_eth0。
同样我们来看看节点 A 是如何接受数据:
云原生虚拟网络 tun/tap & veth-pair

文章插图
当节点A 通过物理网卡 eth0 接受到数据后会将写入内核网络协议栈,因为目标端口号是OpenVPN程序所监听的,所以网络协议栈会将数据交给 OpenVPN ;
OpenVPN 程序得到数据之后,发现目标IP是tun0设备的,于是将数据从用户空间写入到字符设备文件中,然后 tun0 设备会将数据写入到协议栈中,网络协议栈最终将数据转发给应用进程 。
从上面我们知道使用 tun/tap 设备传输数据需要经过两次协议栈,不可避免地会有一定的性能损耗,如果条件允许,容器对容器的直接通信并不会把 tun/tap 作为首选方案 , 一般是基于稍后介绍的 veth 来实现的 。但是 tun/tap 没有 veth 那样要求设备成对出现、数据要原样传输的限制 , 数据包到用户态程序后,程序员就有完全掌控的权力,要进行哪些修改,要发送到什么地方,都可以编写代码去实现,因此 tun/tap 方案比起 veth 方案有更广泛的适用范围 。

推荐阅读