问题导读:
1.什么是MTU?
2.怎样VxLAN封包和GRE封包?
3.Linux vxlan + bridge 环境中的网络栈是什么?
1. 基础知识
1.1 MTU
一个网络接口的 MTU 是它一次所能传输的最大数据块的大小。任何超过MTU的数据块都会在传输前分成小的传输单元。MTU 有两个测量层次:网络层和链路层。比如,网络层上标准的因特网 MTU 是 1500 bytes,而在连接层上是 1518 字节。没有特别说的时候,往往指的是网络层的MTU。
要增加一个网络接口 MTU 的常见原因是增加高速因特网的吞吐量。标准因特网 MTU 使用 1500byte是为了和 10M 和 100M 网络后向兼容,但是,在目前1G和 10G网络中远远不够。新式的网络设备可以处理更大的MTU,但是,MTU需要显式设置。这种更大MTU的帧叫做“巨帧”,通常 9000 byte 是比较普遍的。
相对地,一些可能得需要减少MTU的原因:
- 满足另一个网络的MTU的需要(为了消除UDP分包,以及需要TCP PMTU discover )
- 满足 ATM cell 的要求
- 在搞出错率线路上提高吞吐量
MTU 不能和目前任何 Internet 网络协议混在一起,但是,可以使用一个路由器将不同 MTU 的网段连在一起。
修改配置网络接口 MTU 的方式:
- 自动的:通过 DHCP MTU 广告。注意,只有 DHCP 租期被续了以后,MTU 设置才会生效。你可以将网络接口 down 再 up 强制它马上生效。
- 手动临时性的:ifconfig eth0 mtu 9000
- 永久性的:修改 network interface 定义文件。以 Debian/Ubuntu 为例,
auto eth0iface eth0 inet static address 192.168.0.2 netmask 255.255.255.0 mtu 9000
1.2 VxLAN 封包(Mac-in-UDP,以 ping 产生的 ICMP 包为例)和 MTU
步骤 | 操作/封包 | 协议 | 长度 | MTU | 1 | ping -s 1422 | ICMP | 1430 = 1422 + 8 (ICMP header) | | 2 | L3 | IP | 1450 = 1430 + 20 (IP header) | VxLAN Interface 的 MTU | 3 | L2 | Ethernet | 1464 = 1450 + 14 (Ethernet header) | | 4 | VxLAN | UDP | 1480 = 1464 + 8 (VxLAN header) + 8 (UDP header) | | 5 | L3 | IP | 1500 = 1480 + 20 (IP header) | 物理网卡的(IP)MTU,它不包括 Ethernet header 的长度 | 6 | L2 | Ethernet | 1514 = 1500 + 14 (Ethernet header) | 最大可传输帧大小 |
因此,VxLAN 的 overhead 是1514- 1464 = 50 byte。
1.3 GRE 封包(IP-in-IP,以 ping 产生的 ICMP 包为例)和 MTU
步骤 | 操作/封包 | 协议 | 长度 | 备注 | 1 | ping -s 1448 | ICMP | 1456 = 1448 + 8 (ICMP header) | ICMP MSS | 2 | L3 | IP | 1476 = 1456 + 20 (IP header) | GRE Tunnel MTU | 3 | L2 | Ethernet | 1490 = 1476 + 14 (Ethernet header) | 经过 bridge 到达 GRE | 4 | GRE | IP | 1500 = 1476 + 4 (GRE header)+ 20 (IP header) | 物理网卡 (IP)MTU | 5 | L2 | Ethernet | 1514 = 1500 + 14 (Ethernet header) | 最大可传输帧大小 |
因此,GRE 的 overhead 是 1514 - 1490 = 24 byte(为啥 Neutron 中的 GRE overhead 是 42?)。
可见,使用 GRE 可以比使用 VxLAN 每次可以多传输 1448 - 1422 = 26 byte 的数据。
1.4 IP Fragmentation (分包) 和 Path Maximum Transmission Unit Discovery (PMTUD)
1.4.1 IP 分包
先谈几个要点:
- 分包是指将一个IP包分成多个传输,在接收端 IP 层重新组装
- 一个 IP 包能否分包,取决于它的 DF 标志位:DF bit (0 = "may fragment," 1 = "don't fragment")
- 分包后,每个分段有 MF 标志位:MF bit (0 = "last fragment," 1 = "more fragments")
分包示例:
第一个表格中:
- IP 包长度 5140,包括 5120 bytes 的 payload
- DF = 0, 允许分包
- MF = 0, 这是未分包
第二个表格中:
- 0-0 第一个分包: 长度 1500 = 1480 (payload) + 20 (IP Header). Offset(起始偏移量): 0
- 0-1 第二个分包: 长度 1500 = 1480 (payload) + 20 (IP Header). Offset: 185 = 1480 / 8
- 0-2 第三个分包: 长度 1500 = 1480 (payload) + 20 (IP Header). Offset: 370 = 185 + 1480/8
- 0-3 第四个分包: 长度 700 = 680 (payload, = (5140 - 20) - 1480 * 3) + 20 (IP Header) . Offset: 555 = 370 + 1480/8
需要注意的是,只有第一个包带有原始包的完整 IPv4 + TCP/UDP 信息,后续的分包只有 IPv4 信息。
分包带来的问题:
- sender overhead:需要消耗 CPU 去分包,包括计算和数据拷贝。
- receiver overhead:重新组装多个分包。在路由器上组装非常低效率,因此组装往往在接收主机上进行。
- 重发 overhead:一个分包丢失,则整个包需要重传。
- 在多个分包出现顺序错开时,防火墙可能将分到当无效包处理而丢弃。
1.4.2 TCP MSS:TCP Maximum Segment Size (TCP 最大段长)
TCP MSS,是指一个接收方期望在一个 TCP/IP 报文里面接收的数据(payload)的长度。
MSS 的值受两个值的约束:发送方和接收方的用于存储一个 TCP/IP 报文中的TCP数据的buffer 的长度的小者,以及该值和整个传输路径的最小 MTU 减去40 的小者。而最小MTU 由 PMTU 确定。
1.4.3 PMTU 路径MTU发现
PMTU 的用途是动态的确定从发送端到接收端整个路径上的最小 MTU,从而避免分包。注意,PMTU 只支持 TCP,对其他协议比如 UDP 无效。而且,如果发送方已经开启了 PMTU,那么它发送的所有 TCP/IP 包的 DF 标志都被设置为 1 即不再允许分包。当网络路径上某个路由器发现发送者的包因为超过前面转发路径的 MTU 而无法发送时,它向发送者返回一个 ICMP "Destination Unreachable" 消息,其中包含了那个 MTU,然后发送者就会在它的路由表中将该mtu值保存下来,再使用较小的 MTU 重新发出新的较小的包。
在 Linux 系统上,可以打开或者关闭 PMTU:
/proc/sys/net/ipv4/ip_no_pmtu_disc #0,打开,默认值;1,关闭 (sysctl -w net.ipv4.ip_no_pmtu_disc=1)Set this if you want to disable Path MTU discovery - a technique to determine the largest Maximum Transfer Unit possible on your path. See also the section on Path MTU discovery in the Cookbook chapter.
一个路由器作为主机包的转发者的通常做法:
- 检查 IP 包的 DF 标志位
- 检查 IP 包的 大小是否超过带转发端口的 MTU
- 如果超过,而且 DF = 0 ,则将它分成多个包再发送 (分包)
- 如果超过,而且 DF = 1,则向发生着返回一个 ICMP 消息,并将包丢弃 (PMTU)
- 如果不超过,则转发 (转发)
例子1:超过 MTU,DF = 0 => 路由器分包、发送,接收主机组装
例子2:超过,DF = 1 => PMTU,发送者重新以小包发送
2. Linux vxlan + bridge 环境中的网络栈和 MTU
2.1 QEMU/KVM 计算节点上的网络栈
2.1.1 协议栈
- 红线:QEMU/KVM 主机网卡接收外部网络包并发给虚机
- 黑线:虚机上的应用发出网络包经过 QEMU/KVM 主机网卡发出
补充说明一下使用 virtio_net 时客户机网卡 eth0 和 主机上的 tap 设备之间的关系:
- 虚机的网络流量是通过 virtio_net queue 发到 QEMU/KVM 主机上的 QEMU 的
- QEMU 再交给主机上的 tap 设备
可见,eth0 和 tap 设备是不直接连接的,而是需要经过 QEMU。
2.1.2 各个 network interface 的 MTU
每个 network interface 都可以设置不同的 MTU,用来限制经过它的最大 IP payload 的长度。这些 network interface 包括:
- 虚机 eth0
- 主机 tap 设备
- 主机 vxlan interface
- 主机 网卡 eth1
网络接口 | MTU | 来源 | 说明 | 物理网卡 eth0 | 1500 | 安装系统时指定,未指定则使用默认值 1500 | 用于虚机的数据网络的物理网卡 | vxlan interface | 1450 | 由 linux vxlan 内核模块创建该interface对应的 ip 设备时指定,其 MTU 为绑定的网卡的MTU 值减去 50 | | tap 设备 | 1450 | 由 linux bridge 在添加 tap 到 linux bridge 时指定为该 bridge 所有 port 的最小 MTU。 dev_set_mtu(br->dev, br_min_mtu(br)) | 和虚机 eth0 通过 QEMU virtio 连接 | 虚机 eth0 | 1450 | 当 Neutron DHCP 的 advertise_mtu 配置项有设置值时,由该值决定;否则,使用默认的 1500 | neutron 代码 |
从上图也可以看出,
- 主机网卡 eth1 的默认 MTU 是 1500 bytes,当然你可以修改
- 网络包经过 vxlan interface 到达 eth1 的过程中,Linux vxlan 内核模块会将网络包二层帧封装成 UDP 包,因此,vxlan interface 必须设置适当的 MTU 来限制通过它的网络包的大小(从 1.2 章节可以看出,vxlan interface 的 MTU 需要比它所绑定的物理网卡的 MTU 小 50),否则,封装后的包会被 eth1 丢弃。
- tap 设备和虚机 eth0 的 MTU 是相同的,因为 QEUM 只是转发,而没做封装之类的处理。而因为 vxlan interface MTU 需要缩小,因此它们的 MTU 也要相应缩小。因为 eth0 到 vxlan interface 之间没什么包封装,因此,它们的 MTU 可以一致。
2.2 Neutron 网络节点上的协议栈
网络接口 | MTU | 来源 | 说明 | 物理网卡 eth0 | 1500 | 安装系统时指定,未指定则使用默认值 | 用于虚机的数据网络的物理网卡 | vxlan interface | 1450 | 由 linux vxlan 内核模块创建该interface对应的 ip 设备时指定,其 MTU 为绑定的网卡的MTU 值减去 50 | tap 和 qr/ns/qg 是 veth 的两端 | tap 设备 | 1450 | 由 linux bridge 在添加 tap 到 linux bridge 时指定为该 bridge 所有 port 的最小 MTU。因为 vxlan interface 的 MTU 是 1450, 因此tap 设备的 MTU 也是 1450. dev_set_mtu(br->dev, br_min_mtu(br)) | qr/ns/qg network interface 及对应的tap设备 | 1500 | 由 Neutorn 根据其配置项 conf.network_device_mtu 来设置:设置了其值时,设置MTU为该值;否则,不指定时,则使用默认值1500。 可见这里有个问题,这个 MTU 和 tap 的 MTU 应该一致,但是 conf.network_device_mtu 没有默认值,因此不配置该值的时候,其 MTU 为 1500,这将导致和 tap 的 MTU 不一致。后面会分析该不一致导致的问题。 | neutron 代码 | 物理网卡 eth1 | 1500 | neutorn 有配置项时使用配置的值,具体见下面的分析;没有时,使用默认值 1500. | 用于访问外网的物理网卡 |
|