随着网络技术,虚拟化技术的发展,更多的高级网络设备被加入了到了 Linux 中,使得情况变得越来越复杂。在下面的内容中,将一一分析在虚拟化技术中经常使用的几种 Linux 网络设备抽象类型:Bridge、802.1.q VLAN device、VETH、TAP,详细解释如何用它们配合 Linux 中的 Route table、IP table 简单的创建出本地虚拟网络。
(1)Bridge工作原理
Bridge(桥)是 Linux 上用来做 TCP/IP 二层协议交换的设备,与现实世界中的交换机功能相似。Bridge 设备实例可以和 Linux 上其他网络设备实例连接,既 attach 一个从设备,类似于在现实世界中的交换机和一个用户终端之间连接一根网线。当有数据到达时,Bridge 会根据报文中的 MAC 信息进行广播、转发、丢弃处理。
Bridge是这样工作的:
当一个设备链接到Bridge时,就相当于物理网络中从交换机截图了一条网先到物理设备,此时在内核会调用一个netdev_rx_handler_register()的函数,当数据从设备流入时,系统会调用这个函数将数据转发到Bridge上。当 Bridge 接收到此数据时,br_handle_frame()被调用,进行一个和现实世界中的交换机类似的处理过程:判断包的类别(广播/单点),查找内部 MAC 端口映射表,定位目标端口号,将数据转发到目标端口或丢弃,自动更新内部 MAC 端口映射表以自我学习。
Bridge 和现实世界中的二层交换机有一个区别,从设备过来的数据会直接发到 Bridge 上,而不是从一个端口接受。这和现实的物理网络有区别 。
Linux 里 Bridge 是可以设置IP地址的,Bridge是通用网络设备抽象的一种,只要是网络设备就能够设定 IP 地址。当一个 bridge0 拥有 IP 后,Linux 便可以通过路由表或者 IP 表规则在三层定位 bridge0,当有符合此 IP 的数据到达 bridge0 时,内核协议栈认为收到了目标为本机的数据,此时应用程序可以通过 Socket 接收到它。
Bridge 的实现当前有一个限制:当一个设备被 attach 到 Bridge 上时,那个设备的 IP 会变的无效,Linux 不再使用那个 IP 在三层接受数据。举例如下:如果 eth0 本来的 IP 是 192.168.1.2,此时如果收到一个目标地址是 192.168.1.2 的数据,Linux 的应用程序能通过 Socket 操作接受到它。而当 eth0 被 attach 到一个 bridge0 时,尽管 eth0 的 IP 还在,但应用程序是无法接受到上述数据的。此时应该把 IP 192.168.1.2 赋予 bridge0。而此时我们通过ifconfig去查看网络信息时,eth0上的IP地址是空的,被Bridge所取代。
另外需要注意的是数据流的方向。对于一个被 attach 到 Bridge 上的设备来说,只有它收到数据时,此包数据才会被转发到 Bridge 上,进而完成查表广播等后续操作。当请求是发送类型时,数据是不会被转发到 Bridge 上的,它会寻找下一个发送出口。用户在配置网络时经常忽略这一点从而造成网络故障。
(2)VLAN device for 802.1.q
VLAN 又称虚拟网络,是一个被广泛使用的概念,有些应用程序把自己的内部网络也称为 VLAN。按照协议原理一般分为:MACVLAN、802.1.q VLAN、802.1.qbg VLAN、802.1.qbh VLAN。其中出现较早,应用广泛并且比较成熟的是 802.1.q VLAN,其基本原理是在二层协议里插入额外的 VLAN 协议数据(称为 802.1.q VLAN Tag),同时保持和传统二层设备的兼容性。Linux 里的 VLAN 设备是对 802.1.q 协议的一种内部软件实现,模拟现实世界中的 802.1.q 交换机。
Linux 里 802.1.q VLAN 设备是以母子关系成对出现的,母设备相当于现实世界中的交换机 TRUNK 口,用于连接上级网络,子设备相当于普通接口用于连接下级网络。当数据在母子设备间传递时,内核将会根据 802.1.q VLAN Tag 进行对应操作。母子设备之间是一对多的关系,一个母设备可以有多个子设备,一个子设备只有一个母设备。当一个子设备有一包数据需要发送时,数据将被加入 VLAN Tag 然后从母设备发送出去。当母设备收到一包数据时,它将会分析其中的 VLAN Tag,如果有对应的子设备存在,则把数据转发到那个子设备上并根据设置移除 VLAN Tag,否则丢弃该数据。在某些设置下,VLAN Tag 可以不被移除以满足某些监听程序的需要,如 DHCP 服务程序。
对于寄主 Linux 系统来说,母设备只能用来收数据,子设备只能用来发送数据。和 Bridge 一样,母子设备的数据也是有方向的,子设备收到后的数据不会再进入母设备,同样母设备上请求发送出去的数据不会被转到子设备上。可以把 VLAN 母子设备作为一个整体想象为现实世界中的 802.1.q 交换机,下级接口通过子设备连接到寄主 Linux 系统网络里,上级接口同过主设备连接到上级网络,当母设备是物理网卡时上级网络是外界真实网络,当母设备是另外一个 Linux 虚拟网络设备时上级网络仍然是寄主 Linux 系统网络。
需要注意的是母子 VLAN 设备拥有相同的 MAC 地址,因此多个 VLAN 设备会共享一个 MAC。当一个母设备拥有多个 VLAN 子设备时,子设备之间是隔离的,不存在 Bridge 那样的交换转发关系,原因如下:802.1.q VLAN 协议的主要目的是从逻辑上隔离子网。现实世界中的 802.1.q 交换机存在多个 VLAN,每个 VLAN 拥有多个端口,同一 VLAN 端口之间可以交换转发,不同 VLAN 端口之间隔离,所以其包含两层功能:交换与隔离。Linux VLAN device 实现的是隔离功能,没有交换功能。一个 VLAN 母设备不可能拥有两个相同 ID 的 VLAN 子设备,因此也就不可能出现数据交换情况。如果想让一个 VLAN 里接多个设备,就需要交换功能。在 Linux 里 Bridge 专门实现交换功能,因此将 VLAN 子设备 attach 到一个 Bridge 上就能完成后续的交换功能。总结起来,Bridge 加 VLAN device 能在功能层面完整模拟现实世界里的 802.1.q 交换机。
(3)TAP 设备与 VETH 设备
TUN/TAP 设备是一种让用户态程序向内核协议栈注入数据的设备,一个工作在三层,一个工作在二层,使用较多的是 TAP 设备。
VETH 设备出现较早,它的作用是反转通讯数据的方向,将需要发送的数据转换成需要收到的数据重新送入内核网络层进行处理,从而间接的完成数据的注入。
当一个 TAP 设备被创建时,在 Linux 设备文件目录下将会生成一个对应 char 设备,用户程序可以像打开普通文件一样打开这个文件进行读写。当执行 write()操作时,数据进入 TAP 设备,此时对于 Linux 网络层来说,相当于 TAP 设备收到了一包数据,请求内核接受它,如同普通的物理网卡从外界收到一包数据一样,不同的是其实数据来自 Linux 上的一个用户程序。Linux 收到此数据后将根据网络配置进行后续处理,从而完成了用户程序向 Linux 内核网络层注入数据的功能。当用户程序执行 read()请求时,相当于向内核查询 TAP 设备上是否有需要被发送出去的数据,有的话取出到用户程序里,完成 TAP 设备的发送数据功能。TAP实现了用户态和内核态的数据交换功能,应用程序通过 read()/write()操作,和本机网络核心进行通讯。
VETH 设备总是成对出现,送到一端请求发送的数据总是从另一端以请求接受的形式出现。该设备不能被用户程序直接操作,但使用起来比较简单。创建并配置正确后,向其一端输入数据,VETH 会改变数据的方向并将其送入内核网络核心,完成数据的注入。在另一端能读到此数据。