1. 测试环境
1.1 总体环境
- 宿主机:Ubuntu Linux/KVM + VxLAN + Linux bridge,网卡 MTU 9000
- 客户机:Ubuntu Linux + Virtio-net NIC,网卡 MTU 1500,使用 OpenStack Kilo 管理虚机
在发送端,虚机要把一个数据包发出去,需要首先把 packet 经过 virtio 设备发到宿主机的某个 linux bridge,网桥再转发到 xvlan 的虚拟网卡,虚拟网卡把它变成 VxLAN UDP frame,然后发往UDP 层,再通过TCP/IP层再次进行路由,数据包这次会被发往物理网络,并最终抵达接收端端。
1.2 客户机中使用的 virtio-net 虚拟网卡
客户机的 virtio-net 超虚拟化网卡其实是一个使用中断和 DMA 技术实现的 PCI 设备,因此可以使用 lspci 命令查看它的信息:
00:03.0 Ethernet controller [0200]: Red Hat, Inc Virtio network device [1af4:1000]
默认情况下,该网卡的 Segmentation Offloading 全部是打开的:
root@sammyubuntu1:~# ethtool -k eth0
Features for eth0:
rx-checksumming: on [fixed]
tx-checksumming: on
scatter-gather: on
tcp-segmentation-offload: on
udp-fragmentation-offload: on
generic-segmentation-offload: on
generic-receive-offload: on
large-receive-offload: off [fixed]
2. 发送端的实验和实现
2.1 实验
2.1.1 在客户机和宿主机网卡的 GSO/TSO/UFO 全部打开情况下的实验
(1)iperf 的 MSS 是 1448 bytes
root@sammyubuntu1:~# iperf -c 20.0.0.103 -l 65550 -m -M 400000
WARNING: attempt to set TCP maxmimum segment size to 400000 failed.
Setting the MSS may not be implemented on this OS.
------------------------------------------------------------
Client connecting to 20.0.0.103, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[ 3] local 20.0.0.150 port 56228 connected with 20.0.0.103 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 1.06 GBytes 908 Mbits/sec
[ 3] MSS size 1448 bytes (MTU 1500 bytes, ethernet)
实验表明,客户机中的TCP MSS 只和客户机网卡的 MTU 有关,和其它因素比如宿主机网卡 MTU 没有关系。而且,一个 TCP 连接的两个方向上的 MSS 是可以不同的。正是因为 MSS 的独立性,它可能会产生不同的后果,下文会有阐述。
另外一个有趣的结果是,客户机网卡的 MTU 的大小对网络性能的影响不大。在下面的测试中,客户机网卡 MTU 由 1500 提高到 8000,性能只提高了 6.8%。
<客户机网卡 MTU 1500,宿主机网卡 MTU 9000>
root@sammyubuntu1:~# iperf -c 20.0.0.103 -m
------------------------------------------------------------
Client connecting to 20.0.0.103, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[ 3] local 20.0.0.150 port 56275 connected with 20.0.0.103 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 1.06 GBytes 909 Mbits/sec
[ 3] MSS size 1448 bytes (MTU 1500 bytes, ethernet)
<客户机网卡 MTU 8000,宿主机网卡 MTU 9000>
root@sammyubuntu1:~# iperf -c 20.0.0.103 -m
------------------------------------------------------------
Client connecting to 20.0.0.103, TCP port 5001
TCP window size: 325 KByte (default)
------------------------------------------------------------
[ 3] local 20.0.0.150 port 56274 connected with 20.0.0.103 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 1.14 GBytes 977 Mbits/sec
[ 3] MSS size 7948 bytes (MTU 7988 bytes, unknown interface)
(2)客户机网卡:Frame 的 size 明显超过了 MSS,说明在启用了 GSO/TSO 的情况下,只要每个数据包不超过 IP 包的最大大小 64k,virtio-net 网卡就可以直接经过 virtqueue 发给 QEMU 中的backend。检验码报错,说明校验和计算被卸载到了网卡上,但是可能网卡计算错误。
07:56:04.857427 IP (tos 0x0, ttl 64, id 43384, offset 0, flags [DF], proto TCP (6), length 1500)
20.0.0.150.56230 > 20.0.0.103.5001: Flags [.], cksum 0x2ecb (incorrect -> 0xc064), seq 438742392:438743840, ack 1, win 229, options [nop,nop,TS val 7353870 ecr 564507], length 1448
07:56:10.861709 IP (tos 0x0, ttl 64, id 56589, offset 0, flags [DF], proto TCP (6), length 65212)
20.0.0.150.56230 > 20.0.0.103.5001: Flags [.], cksum 0x27ac (incorrect -> 0x74c2), seq 1119980056:1120045216, ack 1, win 229, options [nop,nop,TS val 7355371 ecr 566008], length 65160
(3)宿主机上客户机网卡对应的 tap 设备:跟客户机网卡中看到的一样,说明 QEMU 中的 virtio-queue(backend)和 宿主机中的 tap 网络设备都是对这些 packets 直接发送的,没有做任何分包等操作。
08:12:23.443278 fa:16:3e:a3:a0:55 > fa:16:3e:1e:d9:f4, ethertype IPv4 (0x0800), length 61322: (tos 0x0, ttl 64, id 52865, offset 0, flags [DF], proto TCP (6), length 61308)
20.0.0.150