Linux 用户想要使用网络功能,不能通过直接操作硬件完成,而需要直接或间接的操作一个 Linux 为我们抽象出来的虚拟网络设备来完成。
一个常见的情况是,系统里装有一个硬件网卡,Linux 会在系统里为其生成一个网络设备实例,如 eth0,用户需要对 eth0 发出命令以配置或使用它了。
随着网络技术,虚拟化技术的发展,更多的虚拟网络设备被加入了到了 Linux 中。这些虚拟网络设备作为云计算的底层技术,支撑着现有云计算的发展。
Bridge
Bridge是Linux上工作在内核协议栈二层的虚拟交换机,虽然是软件实现的,但它与普通的二层物理交换机功能一样。可以添加若干个网络设备(em1,eth0,tap,…)到Bridge上(brctl addif)作为其接口,添加到Bridge上的设备被设置为只接受二层数据帧并且转发所有收到的数据包到Bridge中(bridge内核模块),在Bridge中会进行一个类似物理交换机的查MAC端口映射表,转发,更新MAC端口映射表这样的处理逻辑,从而数据包可以被转发到另一个接口/丢弃/广播/发往上层协议栈,由此Bridge实现了数据转发的功能。如果使用tcpdump在Bridge接口上抓包,是可以抓到桥上所有接口进出的包
跟物理交换机不同的是,运行Bridge的是一个Linux主机,Linux主机本身也需要IP地址与其它设备通信。但被添加到Bridge上的网卡是不能配置IP地址的,他们工作在数据链路层,对路由系统不可见。不过Bridge本身可以设置IP地址,可以认为当使用brctl addbr br0新建一个br0网桥时,系统自动创建了一个同名的隐藏br0网络设备。br0一旦设置IP地址,就意味着br0可以作为路由接口设备,参与IP层的路由选择(可以使用route -n查看最后一列Iface)。因此只有当br0设置IP地址时,Bridge才有可能将数据包发往上层协议栈。
VLAN设备
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 设备拥有相同的 MAC 地址,可以把它当成现实世界中 802.1.q 交换机的 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 交换机。
TAP 设备
我们知道KVM虚拟化中单个虚拟机是主机上的一个普通qemu-kvm进程,虚拟机当然也需要网卡,最常见的虚拟网卡就是使用主机上的tap设备。
TUN/TAP 设备是一种让用户态程序向内核协议栈注入数据的设备,一个工作在三层,一个工作在二层,使用较多的是 TAP 设备。
当一个 TAP 设备被创建时,在 Linux 设备文件目录下将会生成一个对应 char 设备,用户程序可以像打开普通文件一样打开这个文件进行读写。
当执行 write()操作时,数据进入 TAP 设备,此时对于 Linux 网络层来说,相当于 TAP 设备收到了一包数据,请求内核接受它,如同普通的物理网卡从外界收到一包数据一样,不同的是其实数据来自 Linux 上的一个用户程序。Linux 收到此数据后将根据网络配置进行后续处理,从而完成了用户程序向 Linux 内核网络层注入数据的功能。
当用户程序执行 read()请求时,相当于向内核查询 TAP 设备上是否有需要被发送出去的数据,有的话取出到用户程序里,完成 TAP 设备的发送数据功能。
针对 TAP 设备的一个形象的比喻是:使用 TAP 设备的应用程序相当于另外一台计算机,TAP 设备是本机的一个网卡,他们之间相互连接。应用程序通过 read()/write()操作,和本机网络核心进行通讯。
TUN设备
TUN设备与上面介绍的TAP设备很类似,只是TUN设备连接的是主机内核协议栈IP层。openvpn是使用TUN设备的一个常见例子。
客户端使用openvpn访问web服务流程:
1.客户端启动openvpn client进程连接openvpn server
2.server下发路由条目到客户端机器路由表中,同时生成虚拟网卡tun1(tun设备,openvpn client进程与openvpn server一样会注册tun虚拟网卡)
3.客户端通过浏览器访问web服务
4.浏览器生成的数据包在协议栈IP层进行路由选择,决定通过虚拟网卡tun1发出
5.虚拟网卡tun1另一端连接用户层openvpn client进程
6.openvpn client进程收到原始请求数据包
7.openvpn client封装原始请求数据包,通过udp协议发送vpn封包到openvpn server上的9112端口 A – > T --> K --> R --> P1
8.openvpn server上的openvpn进程收到vpn封包,解包,使用文件描述符6写数据到/dev/net/tun P1 --> write(fd,…) --> N
9.文件描述符6与虚拟网卡tun0关联,主机从tun0网卡收到数据包 N --> H —> I
10.主机进行Routing decision,根据数据包目的IP(用户访问web网站IP地址)从相应网卡发出 I --> K --> T --> M --> A
VETH设备
veth也是Linux实现的虚拟网络设备,veth设备总是成对出现,其作用是反转数据流方向。例如如果veth-a和veth-b是一对veth设备,veth-a收到的数据会从veth-b发出。相反,veth-b收到的数据会从veth-a发出。
拿dhcp agent来说,openstack中每个网络都需要起一个dhcp服务器用于给此网络虚拟机分配ip地址,每个openstack网络都使用一个单独的network namespace,每个network namespace和网络节点上Bridge通过veth设备对连接,这样多个openstack网络才不会引起冲突。理解veth设备对和network namespace是解决openstack中虚拟机网络故障问题的关键。
OVS
在基于Linux内核的系统上,应用最广泛的还是系统自带的虚拟交换机Linux Bridge,它是一个单纯的基于MAC地址学习的二层交换机,简单高效,但同时缺乏一些高级特性,比如OpenFlow,VLAN tag,QOS,ACL,Flow等,而且在隧道协议支持上,Linux Bridge只支持vxlan,OVS支持gre/vxlan/IPsec等,这也决定了OVS更适用于实现SDN技术
用户空间ovs-vswitchd和内核模块datapath决定了数据包的转发,首先,datapath内核模块收到进入数据包(物理网卡或虚拟网卡),然后查找其缓存(datapath flows),当有一个匹配的flow时它执行对应的操作,否则datapath会把该数据包送入用户空间由ovs-vswitchd负责在其OpenFlow flows中查询(图1中的First Packet),ovs-vswitchd查询后把匹配的actions返回给datapath并设置一条datapath flows到datapath中,这样后续进入的同类型的数据包(图1中的Subsequent Packets)因为缓存匹配会被datapath直接处理,不用再次进入用户空间。
datapath专注于数据交换,它不需要知道OpenFlow的存在。与OpenFlow打交道的是ovs-vswitchd,ovs-vswitchd存储所有Flow规则供datapath查询或缓存.
OVS中支持添加隧道(Tunnel)端口,常见隧道技术有两种gre或vxlan。隧道技术是在现有的物理网络之上构建一层虚拟网络,上层应用只与虚拟网络相关,以此实现的虚拟网络比物理网络配置更加灵活,并能够实现跨主机的L2通信以及必要的租户隔离。不同隧道技术其大体思路均是将以太网报文使用隧道协议封装,然后使用底层IP网络转发封装后的数据包,其差异性在于选择和构造隧道的协议不同。Tunnel在OpenStack中用作实现大二层网络以及租户隔离,以应对公有云大规模,多租户的复杂网络环境。