虚拟机交换机发展历程可以总结为: Linux Bridge -> OVS -> OVS-DPDK 阶段.
1、Linux Bridge
同 tap/tun、veth-pair 一样,Bridge也是一种虚拟网络设备,具备虚拟网络设备的所有特性,比如配置IP、MAC等,除此之外,Bridge与普通的网络设备相比,有个重要的特点:Bridge有多个端口,数据可以从多个端口进,从多个端口出,普通设备就像一个管道,只有两端,一端进一端出.
Bridge这个特性让它可以接入其他的网络设备,比如物理设备、虚拟设备、VLAN设备等.Bridge通常充当主设备,其他设备为从设备,这样的效果就等同于物理交换机的端口连接了一根网线.如下图通过Bridge连接两个VM的tap虚拟网卡和物理网卡eth0.

Bridge 常用语虚拟机和容器网络中,这两种网络在数据传输流程中有一些不同,如下图所示.
1.1、虚拟机网络
虚拟机一般通过tap/tun 设备将虚拟机网卡同宿主机里的Bridge连接起来,完成同主机和跨主机的通信,具体步骤如下:
-
创建一个bridge设备br0
cmd# 有两种实现方式 # 第一种 ip link add br0 type bridge ip link set br0 up # 第二种 brctl addr br0 ifconfig br0 up brctl addif br0 eth0 -
虚拟机网络使用桥接设备br0连接到宿主机网络,如下配置就会让VM使用br0网桥作为其网络连接
xml# 配置 虚拟机xml中 <interface> 节点 <interface type='bridge'> <mac address='52:54:00:bd:be:2b'/> <source bridge='br0'/> <model type='virtio'/> <address type='pci' slot='0x3'/> </interface>配置如上后,当执行
virsh start vm1.xml 拉起虚机时,libvirt首先解析xml,并构建qemu命令行,在这一步骤就会open /dev/net/tun 创建tun/tap设备,并获取该设备对应的文件描述符fd,并将其信息传入到qemu命令行中powershell# -netdev tap,fd=36 表示的就是连接主机上的tap设备,fd=36为读写/dev/net/tun 的文件描述符 /usr/bin/qemu-kvm -name vm1 .... -netdev tap,fd=36,id=hostnet0,vhost=on,vhostfd=37 -device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:bd:be:2b,bus=pci.2,addr=0x0 .....这条命令启动了一个虚机,并为其配备了:高性能半虚拟化网络接口virtio-net device,该接口通过TAP设备连接到主机,并启用了vhost-net内核加速.该网络配置分为两部分;后端在主机host上做的事情,前端guest虚机内部看到什么.
后端
-netdev tap,fd=36,id=hostnet0,vhost=on,vhostfd=37这部分定义了虚拟机网络在主机端的实现方式:-netdev tap指定网络后端类型为TAP,Qemu发送的网络包会直接出现在主机的TAP设备上;fd=36表示了通知Qemu不需要自己创建TAP设备,直接使用已经打开并传入的文件描述符fd,编号为36id=hostnet0为这个网络后端设置一个唯一的ID,该ID将用于在 -device 参数中引用这个后端,将虚拟网卡与它连接起来vhost=on启用vhost-net内核加速vhostfd=37这是一个指向/dev/vhost-net设备的已经打开文件描述符,表示已经为Qemu准备好了vhost-net的句柄
前端
-device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:bd:be:2b,bus=pci.2,addr=0x0 定义了虚拟机内部能看到的虚拟硬件信息:-device virtio-net-pci告诉Qemu在虚拟机中插入一个设备,类型为virtio-net-pcinetdev=hostnet0这是连接前后端的关键,他告诉这个虚拟网卡,应该使用ID为hostnet0的后端数据通道,这样虚拟机里的virtio-net设备就和主机上的TAP设备连接起来了mac=52:54:00:bd:be:2b为虚拟网卡设备指定一个固定的MAC地址,52:54:00是QEMU/KVM常用的私有MAC地址前缀bus=pci.2,addr=0x0指定这个设备在虚拟PCI总线上的位置
-
可以配置上ip, ping包试试
两个VM通信如下图所示:

虚拟机发出的数据包通过tap设备先到达br0,然后经过eth0发送到物理网络中,数据包不需要经过主机的协议栈,效率较高.
1.2、容器网络

一般是使用 veth-pair 来连接容器和主机,因为在主机看来,容器就是一个个被隔离的 namespace,用 veth-pair 更有优势.
容器的 Bridge 网络通常配置成内网形式,要出外网需要走 NAT,所以它的数据传输不像虚拟机的桥接形式可以直接跨过协议栈,而是必须经过协议栈,通过 NAT 和 ip_forward 功能从物理网卡转发出去,因此,从性能上看,Bridge 网络虚拟机要优于容器.
1.3 Bridge VLAN
物理交换机具有虚拟局域网(VLAN),是对局域网LAN的软件化升级,LAN的缺点是LAN中任何节点发的数据包,其他节点都能收到,这就会很容易导致:
- 容易形成广播风暴
- 广播包无法割离,比如节点B不想接收节点A的包,但是无法割离
因此解决如上问题就是VLAN,VLAN可以对广播包进行有效的隔离,将交换机的端口虚拟出多个子端口,用 tag 来标记,相当于将交换机的端口划分多个LAN,同一个LAN的节点发出的数据包打上LAN的tag,这样LAN中其他的节点就无法收到包,达到隔离效果.
Bridge 本身也支持VLAN功能,如下图所示,Bridge将一个物理网卡设备eth0划分成两个字设备eth0.10和eth0.20,分别挂到 Bridge 虚拟出的两个 VLAN 上,VLAN id 分别为 VLAN 10 和 VLAN 20。同样,两个 VM 的虚拟网卡设备 vnet0 和 vnet 1 也分别挂到相应的 VLAN 上。这样配好的最终效果就是 VM1 不能和 VM2 通信了,达到了隔离:

Linux Bridge + VLAN 便可以构成一个和物理交换机具备相同功能的虚拟交换机了。对于网络虚拟化来说,Bridge 已经能够很好地充当交换设备的角色了。
2、OVS(Open vSwitch)
OVS 作为一种网络虚拟化的方式,在虚拟机的虚拟网卡vNIC和服务器的物理网卡之间通过软件模拟网桥的方式进行映射,并依赖精确流表中匹配源/目的IP、源/目的端口、三层协议、VLAN等字段进行报文转发.
OVS的引入和发展主要有一些几个原因:
- 方便网络管理与监控.可以帮助管理员对整套云环境中网络状态和数据流量进行监控,比如分析网路中流淌的数据包来自哪个VM、哪个OS及哪个用户
- 加速数据包的寻路与转发,OVS引入流缓存的机制,加速数据包转发效率
- 隧道协议支持.OVS相较于Bridge支持更多的协议,包括VXLAN、gre、iIPsec

从如上模块图所示,OVS可以划分为管理面、数据面、控制面三块:
- 数据面:以用户态的 ovs-vswitchd 和内核态的 datapath 为主的转发模块,以及与之相关的数据库模块ovsdb-server(主要保存了整个OVS的配置信息)
- 控制面:主要由 ovs-ofctl模块负责,给予OpenFlow 协议与数据面进行交互
- 管理面 :主要是由OVS提供的各种工具来负责,方便用户对底层各个模块的控制管理

- 数据包达到虚拟机网卡后(比如TAP),上传给datapath
- datapath 首先进行报文头信息的获取,根据报文头信息头生成匹配流表项的key值,得到key值后进行内核态流表匹配(Flow Table),如果缓存中没有找到该记录,内核通过netlink upcall给用户空间的vswitchd
- vswitchd 检查数据库数据包的目的端口在哪里,涉及对OpenFlow流表的操作,需要和ovsdb以及ovs-ofctl的交互
- 刷新内核态流表
- reinject 给 datapath,重发数据包
- 再次查询流表,获取数据包精确转发规则后,按照规则转发
- 转发数据包
2.1 OVS 交换原理及瓶颈点分析
TODO:
3、OVS-DPDK
DPDK是Intel开发的一款高性能数据平面的开发工具包,如下图可以显著看出来DPDK的作用:

从传统的OVS交换原理分析中看网络IO实现方式中,内核是导致性能瓶颈的原因所在,要解决性能问题就需要绕开内核,直接在用户态首发包.
OVS- DPDK架构图如下:

OVS-DPDK会创建一堆PMD线程(绑定CPU核),每个线程负责从某个Physical NIC或vhost-user queue轮训手包,可以做流弊哦啊查询、action转发、最终发包,这在过程中没有中断,没有内核参与,纯轮训模式.
网卡收包流程:
- 当网卡收到数据包后,DMA到DRAM的 rx ring buffer
- PMD线程轮询使用
rte_eth_rx_burst将包读出来,每个包是rte_mbuf - 在OVS-DPDK中进行Flow匹配查询(先从
EMC超高速缓存中查,查不到再去大规模流缓存Megaflow cache中查找0,如果可以找到直接执行action(比如输出到某个NIC、输出vhost-user(发给VM、修改MAC、IP等),转发 - 如果没有查询得到,则把报文送给
ovs-vswitchd用户态主线程,找到匹配流表,将该流表下发到EMC、Megaflow - 报文reinject再转发
全是用户态,没有内核upcall.
Guest VM发包流程:
- Guest virtio-net driver 处理发包,Guest应用写socket->Linux内核->virtio-net driver,最终kick触发通知,此时packet还在Guest DRAM(如果前端是virtio-pmd driver,应该是可以绕过Linux内核的)
- Qemu + vhost-user 数据面初始化阶段,Qemu会通过vhost-user协议将ring的物理地址(GPA)又一个GPA->HVA的映射
- OVS-DPDK vhost-user PMD线程直接从Guest共享内存的报文读取变成DPDK mbuf,这会有一次拷贝
- 在OVS-DPDK中进行Flow匹配查询(先从
EMC超高速缓存中查,查不到再去大规模流缓存Megaflow cache中查找0,如果可以找到直接执行action(比如输出到某个NIC、输出vhost-user(发给VM、修改MAC、IP等),转发 - 如果没有查询得到,则把报文送给
ovs-vswitchd用户态主线程,找到匹配流表,将该流表下发到EMC、Megaflow - 发包:发给物理网卡 tx ring中,nic tx ring 只方一个指向mbuf data的描述符(DMA地址+长度)
OVS-DPDK datapath 简化图对比:
传统OVS依然需要在kernel态和用户态进行切换,datapath如下图所示:

OVS-DPDK datapath 如下图所示:

参考文献
The amazing new observability features of Open vSwitch
OpenvSwitch virtio-net与vhost-net通信