MTU、MSS 与封装开销
隧道究竟给真实数据留了多少空间:MTU 与 MSS 背后的首部算术、路径 MTU 发现为何会静默失败,以及如何为 GRE、VXLAN 与 GTP-U 确定 MSS。
MTU 与 MSS 不是一回事
很容易把 MTU 和 MSS 当成同一个数字的两种叫法。它们不是,而这种混淆造成了相当大一部分隧道性能问题。MTU 是链路的属性:这条链路能承载的最大帧。标准以太网是 1500 字节,这也是超大 ping 过不了一台配置不当的网络设备的原因。
MSS 是 TCP 连接的属性:任一端愿意放进单个报文段的最大载荷。它不是链路设置,也不是由 MTU 协商出来的 —— 每一端通告自己能够接收的 MSS,并根据自己知道的 MTU 减去相应首部来得出这个值。
MSS = MTU − IP 首部 − TCP 首部 IPv4:1500 − 20 − 20 = 1460 字节 IPv6:1500 − 40 − 20 = 1440 字节
1460 这个数字太常见了,值得直接记住。如果你在一条正常工作的连接上看到远低于它的值,说明有东西在加首部 —— 而这正是本页要讲的全部内容。
每一层首部都在吃掉载荷
MTU 是在最外层测量的,因此链路帧与应用数据之间的每一个首部字节,都会减少能装下的数据量。普通路径上是 1500 字节里的 40 字节开销,约 2.7%。一旦加上隧道,这个数字会迅速攀升。
| 层 | 基础大小 | 标准 | 说明 |
|---|---|---|---|
| Ethernet II | 14 B | IEEE 802.3 | 6 + 6 + 2;4 字节 FCS 通常被网卡剥掉 |
| 802.1Q VLAN 标签 | +4 B | IEEE 802.1Q | 一层标签;QinQ 再加 4 字节 |
| IPv4 | 20 B | RFC 791 | 带选项时首部最多可到 60 字节 |
| IPv6 | 40 B | RFC 8200 | 固定长度;可选数据走扩展首部 |
| TCP | 20 B | RFC 9293 | 时间戳与 SACK 通常再加 12 字节 |
| UDP | 8 B | RFC 768 | 固定长度 |
| GRE | 4 B | RFC 2784 | 校验和、key、sequence 各加 4 字节 |
| VXLAN | 8 B | RFC 7348 | 承载在 UDP 之内;通常叠在 50 字节的开销之上 |
| GTP-U | 8 B | 3GPP TS 29.281 | E、S、PN 标志各加 4 字节 |
| PPPoE | 8 B | RFC 2516 | 6 字节 PPPoE + 2 字节 PPP 协议 |
| IPsec ESP(隧道模式) | 约 50–73 B | RFC 4303 | SPI + 序列号 + IV + 填充 + ICV |
| MPLS | 每个标签 4 B | RFC 3032 | 典型 L3VPN 用两个标签 |
最后两行正是 IPsec 与 MPLS 站点经常需要下调 MTU 的原因。一个使用 AES-CBC 加 SHA-256 完整性校验的 ESP 隧道,光它自己就要加约 73 字节,还没算外层 IP 首部。
一个完整的隧道计算
以一条承载私有站点流量、跑在公网上的 GRE 隧道为例。线上跑的数据包是公网的以太网帧,私有网络的数据包是它里面的载荷。从外往里算:
GRE over IPv4 over Ethernet,外层 MTU 1500 字节
外层以太网 14 B
外层 IPv4 20 B
GRE 首部 4 B
内层 IPv4 20 B
TCP 首部 20 B
─────────────────────────
开销合计 78 B
内层 MSS 1500 − 78 = 1422 B
所以这条路径正确的 MSS 是 1422 字节,而不是 1460。使用默认 1460 的主机构造出的数据包,会比隧道能容纳的大 38 字节。如果 PMTUD 工作正常,路由器会回送一条携带更小 MTU 的 ICMP 消息,主机随之调整。而如果 ICMP 被过滤 —— 这很常见 —— 这些包会被静默丢弃,连接在每一次大于单个报文段的传输上都会卡住。
用同样的算术看 VXLAN,就明白为什么 overlay 网络常把内层 MTU 设为 1450:以太网、IPv4、UDP、VXLAN,再加内层的以太网、IPv4 与 TCP 首部,合计 14 + 20 + 8 + 8 + 14 + 20 + 20 = 104 字节,在外层 MTU 1500 下留给内层 TCP 载荷的就是 1396 字节。
路径 MTU 发现,以及它是怎么失效的
PMTUD 本应让上述事情全部自动完成。发送方把 Don't Fragment 位置 1,并按自己认为路径能支持的尺寸发包。当某台路由器发现下一跳的 MTU 更小时,它应当丢弃该包并回送一条 ICMP 消息 —— “需要分片”,并携带更小的 MTU。发送方据此减小尺寸后重试。
失效模式在于:ICMP 经常被防火墙拦截,可能是出于安全考虑有意为之,也可能是限速带来的附带损伤。当这条消息永远到不了,发送方会继续用超大包, 而每一个都被丢弃,连接看起来就是卡死。这就是 PMTUD 黑洞,它的表现有非常明确的特征:
| 症状 | 它暗示什么 |
|---|---|
| ping 和 DNS 正常,大文件传输卡住 | 典型的 PMTUD 黑洞 —— 小包过得去,满尺寸的包过不去 |
| SSH 能连上,登录后卡死 | 横幅信息装得下,密钥交换装不下 |
| HTTPS 握手完成,页面永远加载不出来 | 握手报文段很小,数据报文段不小 |
| 局域网正常,走隧道就失败 | 隧道开销降低了路径 MTU,而没有人调整 MSS |
| 时好时坏,某些目的地更严重 | 只有部分路径的 MTU 更小,或只有部分路径过滤了 ICMP |
| 一个方向正常,反方向卡住 | 不对称的 PMTUD 失效 —— 两个方向要分别排查 |
最快的诊断方法是发一个带 Don't Fragment 的超大 ping,然后对载荷大小做二分查找,直到能通为止。在隧道上这样测出的应当就是真实的路径 MTU,可以拿它和两端所认为的值对比。
MSS 钳制,以及 PMTUD 不可靠时的选择
在不能指望 PMTUD 的地方,通常的做法是 MSS 钳制。路径上的某台路由器改写 SYN 与 SYN-ACK 报文中的 MSS 字段,这样两端会立刻使用一个装得下的值,完全不需要去发现什么。这在 PPPoE 链路上很常见:8 字节的 PPPoE 首部把有效 MTU 降到 1492,MSS 降到 1452。
MSS 钳制有几个值得了解的限制。它只影响握手经过钳制设备的那些连接,所以对于处在不在路径上的路由器后面的主机毫无作用。它也不会去改动一条已经建立的连接。而且由于它改写了两端协商一致的字段,它可能掩盖真正的配置错误,而不是修复它。
替代方案包括:在两端显式设置 MSS;把隧道的接口 MTU 调低,让算术自然成立;或者 —— 对 IPv6 而言 —— 尊重"PMTUD 是强制要求"这一点,确保 ICMPv6 类型 2 消息永远不会被过滤。
实用规则
- 按最外层 MTU 来定 MSS,而不是按 1500。把你实际使用的首部逐个加起来。MTU / MSS 计算器会逐层完成这个算术。
- 绝不要整体屏蔽 ICMP。如果必须过滤,至少要放行入方向的 fragmentation-needed(类型 3、代码 4)—— 屏蔽它会让所有终结在你身后的路径的 PMTUD 全部失效。对 IPv6 则要放行 Packet Too Big(类型 2)。
- 记住 1460 只对普通 IPv4 以太网路径成立。IPv6 是 1440,PPPoE 是 1452,任何带隧道的路径都还要更小。
- 隧道的实际代价往往高于首部算术的估算。GRE over IPsec 叠起来可以吃掉一百多字节,把 1500 字节的路径压到 MSS 接近 1330。
- 两个方向都要检查。不对称路由意味着回程路径的 MTU 可能不同,而 MSS 是按方向分别通告的。
按路径类型查 MSS
常见路径配置下主机应当使用的 MSS。除特别说明外,均基于 1500 字节的以太网 MTU。
| 路径 | 开销 | MSS(IPv4) | MSS(IPv6) |
|---|---|---|---|
| 以太网 + IPv4/IPv6 + TCP | 40 B / 60 B | 1460 | 1440 |
| 以太网 + TCP 选项(12 B) | 52 B / 72 B | 1448 | 1428 |
| PPPoE(MTU 1492) | 34 B / 54 B | 1452 | 1432 |
| 以太网 + IPv4 + GRE + IPv4/IPv6 + TCP | 58 B / 78 B | 1422 | 1402 |
| 以太网 + IPv4 + UDP + VXLAN + 内层以太网 + IPv4/IPv6 + TCP | 84 B / 104 B | 1396 | 1376 |
| 以太网 + IPv4 + UDP + VXLAN,内层 MTU 1450 | 50 B / 70 B | 1370 | 1350 |
| 以太网 + IPv4 + ESP(隧道模式)+ IPv4/IPv6 + TCP | 约 112 B / 约 132 B | 约 1338 | 约 1318 |
| 以太网 + IPv4 + GRE + ESP + IPv4/IPv6 + TCP | 约 140 B | 约 1310 | 约 1290 |
| 以太网 + GTP-U + IPv4 + TCP | 62 B | 1418 | — |
| 巨帧(MTU 9000),普通 IPv4 | 40 B | 8960 | 8940 |
这些数值按最小首部计算。你每开启一个选项 —— TCP 时间戳、IPsec 完整性校验、更多 MPLS 标签、第二层 VLAN 标签 —— MSS 都会进一步下降。
值得记住的 MTU 与 MSS 数值
| 量 | 数值 | 出处 |
|---|---|---|
| 标准以太网 MTU | 1500 字节 | IEEE 802.3 / 事实上的互联网标准 |
| 含首部与 FCS 的以太网帧 | 1522 字节(带 VLAN 为 1526) | 14 + 1500 + 4,再加 4 字节标签 |
| 默认 IPv4 TCP MSS | 1460 字节 | 1500 − 20 − 20 |
| 默认 IPv6 TCP MSS | 1440 字节 | 1500 − 40 − 20 |
| 主机必须接受的 IPv4 最小 MTU | 576 字节 | RFC 791 |
| IPv6 最小 MTU | 1280 字节 | RFC 8200 —— 路由器从不分片 |
| PPPoE MTU | 1492 字节 | 1500 − 8 字节 PPPoE 首部 |
| IPv4 首部最大值 | 60 字节 | IHL = 15 × 4 |
| TCP 首部最大值 | 60 字节 | 数据偏移 = 15 × 4 |
| 巨帧 MTU | 9000 字节 | 常见的数据中心约定,并非互联网标准 |
常见问题
MTU 和 MSS 有什么区别?
MTU 是链路能承载的最大帧或数据包,在第 2 层或第 3 层测量 —— 标准以太网是 1500 字节。MSS 是发送方可以放进单个报文段的最大 TCP 载荷,由 MTU 减去 IP 与 TCP 首部得出。在普通的 1500 字节以太网路径上跑 IPv4 与 TCP 时,MSS = 1500 − 20 − 20 = 1460 字节。MTU 是链路的属性;MSS 是两个 TCP 端点之间的约定。
隧道为什么会破坏 TCP 连接?
隧道在原数据包外面加上了自己的首部,使封装后的包比原来更大。如果隧道的外层 MTU 是 1500 字节,那么能装下的内层包就小于 1500 —— 于是认为路径 MTU 是 1500 的主机会发出装不下的包,隧道要么把它们分片,要么直接丢弃。丢包的表现是连接能建立、但在大批量传输时卡住,也就是经典的「ping 通、网页打不开」。
怎么计算隧道路径上正确的 MSS?
把外层链路帧到 TCP 载荷之间的每一个首部加起来,再用外层 MTU 减去这个总和。以以太网 + IPv4 + GRE + IPv4 + TCP 为例:14 + 20 + 4 + 20 + 20 = 78 字节,所以内层 MSS 是 1500 − 78 = 1422 字节。无论哪种隧道类型,算法都一样 —— 变的只是首部清单。MTU / MSS 计算器会按你实际使用的选项逐层完成计算。
MSS 钳制是什么?
MSS 钳制是指路径上的路由器在经过时改写 TCP 握手过程中的 MSS 值,强制两端使用能装下路径的更小报文段尺寸。它用于路径 MTU 发现无法工作的场合 —— 最常见的是 PPPoE 链路、某些隧道,以及 ICMP 被过滤的环境。它是一种权宜手段而不是修复:只对握手经过钳制路由器的连接有效,对已经进行中的流毫无作用。
路径 MTU 发现是什么?
PMTUD 的做法是发送带 Don't Fragment 位的数据包。当路径上某台路由器因为下一跳 MTU 更小而无法转发时,它会丢弃该包并回送一条携带更小 MTU 的 ICMP「需要分片」消息。发送方随即将包减小。只要没人屏蔽 ICMP,它就工作得很好 —— 而一旦被屏蔽,发送方永远不知道存在更小的 MTU,连接就会卡死。这就是为什么 PMTUD 黑洞如此常见、又如此难以诊断。
为什么 IPv6 不在路由器上分片?
IPv6 彻底取消了路由器分片:只有发送主机可以分片,路由器绝不代劳。IPv6 要求的最小 MTU 是 1280 字节,因此 PMTUD 从可选变成了强制。实际后果是:40 字节的 IPv6 首部让 MSS 相比 IPv4 少 20 字节;而某条路径降低了 MTU 却没有把 ICMPv6 Packet Too Big 消息送达时,连接会直接断开,而不是性能下降。
干脆把所有地方的 MTU 都调大行不行?
只有当路径上每一台设备都支持时才可行。9000 字节的巨帧在数据中心内很常见,但几乎不可能端到端跨广域网使用;一旦不匹配,就会制造出你原本想避开的 PMTUD 黑洞。在可控网络内提高 MTU 是值得的;在一条你无法完全掌控的路径上假定存在更大 MTU 则不可取。