一次跨机房 IPSec VPN 隧道大包不通导致业务间歇性卡死的排查记录

跨机房 IPSec VPN 下 ping 通、SSH 正常,但文件传输和数据库同步一到大数据量就卡死,最终定位到 PMTUD 黑洞并用 MSS 钳制根治。

问题背景

我们两个机房(A 机房应用集群、B 机房数据库与文件存储)之间通过一条站点到站点的 IPSec VPN 隧道互联,跑了大半年一直平稳。某次业务扩容后,运维群里陆续冒出几条零散反馈:A 机房的备份任务往 B 机房的存储推文件时经常"卡住不动",跨机房的 MySQL 主从同步偶尔中断重连,个别同事反映从 B 机房拉大的日志包会在中途"僵住"。

但奇怪的是,这些抱怨都不"稳定"——同一个人换个小文件、换个时间点又一切正常。网络监控上带宽、丢包、延迟三条曲线都健康得很,隧道状态 up,ping 对端也是稳稳的 0 丢包。于是问题被当成"偶发抖动"拖了两天,直到一次跨机房的数据库全量同步在传到几百 MB 时彻底断掉、重试三次都失败,才被正式立项排查。

故障现象

把现象归拢后,特征异常清晰又诡异:

  • 小流量全通,大流量必卡ping(默认 64 字节)0 丢包;SSH 登录、执行短命令、curl 拉小页面都秒回。但只要是持续的大数据传输——scp 大文件、rsync 全量、mysqldump 跨机房导入——传输曲线都会走到某个点后骤降为 0,连接不报错、就是永久挂起,直到超时。
  • 方向不对称。从 A→B 推大文件几乎必卡;B→A 拉反而好一些,但也偶发。
  • 协议无关。HTTP、MySQL、NFS 全中招,说明不是应用层的问题。
  • 同机房内部完全正常。同样的传输在各自机房内部跑,速度拉满、毫无问题。问题只出现在"跨隧道 + 大数据量"这个交集上。

“ping 通但大文件传不动"这组症状,几乎是 MTU/分片类问题的教科书式指纹。方向立刻收敛到链路 MTU 上。

排查过程

第一步:用带 DF 标志的大包探测路径 MTU。 普通 ping 用小包,掩盖了问题。我改用大包 + 禁止分片(DF)来探路。Linux 下:

1
2
3
4
# -M do 设置 DF 位(不允许分片),-s 指定 ICMP 数据载荷大小
ping -M do -s 1472 10.20.0.10   # 1472 + 28(IP+ICMP头) = 1500
ping -M do -s 1400 10.20.0.10
ping -M do -s 1300 10.20.0.10

结果非常关键:

  • -s 1472(整包 1500):100% 丢失,且没有收到任何 “Frag needed” 的 ICMP 回包,就是静默超时。
  • -s 1400(整包 1428):依旧不通。
  • -s 1300 附近开始逐步能通,二分下去,实测隧道能稳定通过的整包上限在 1422 字节左右

这解释了一切:隧道的实际可用 MTU 远小于以太网标准的 1500。IPSec 封装(ESP 头 + 认证 + 可能的 NAT-T UDP 封装)会给每个包额外增加几十字节开销,把有效载荷 MTU 压到了 1400 出头。

第二步:确认这是"PMTUD 黑洞"而非普通 MTU 不匹配。 正常情况下,当一个 1500 的 DF 包进不了小 MTU 的链路时,中间设备应该回一个 ICMP Type 3 Code 4(Fragmentation Needed and DF set)告诉发送方"请把包改小到 1422”,这就是路径 MTU 发现(PMTUD)。发送方收到后会自动缩小该目的地的 MSS,传输就能恢复。

但我们的探测里,大包是静默丢弃、没有任何 ICMP 反馈。抓包印证了这一点:

1
2
3
# 在 A 机房发送端抓,只见发出的大包,收不到任何 ICMP Type 3
tcpdump -ni any 'icmp and icmp[icmptype]==3'
# 长时间无输出

发送方永远等不到"包太大"的通知,就会一直用 1500 的大包硬发、永远被丢,同时因为 TCP 已经建连(三次握手是小包,能通),应用层只看到"传着传着就不动了"。这正是 PMTUD 黑洞(PMTUD Black Hole):ICMP 差错报文在路径某处被吞掉,导致路径 MTU 发现机制失效。

第三步:定位 ICMP 被谁吃掉。 沿着隧道两端逐跳排查防火墙策略,发现 B 机房出口防火墙上有一条几个月前为"防 ICMP 洪水"加的粗暴规则——直接 deny 了所有入向 ICMP。这条规则把正常的 echo 挡了(但因为大家平时 ping 的是内网网关、没走到这条规则,一直没暴露),更致命的是把 PMTUD 赖以工作的 Type 3 Code 4 差错报文也一并丢弃了。

第四步:解释方向不对称。 A→B 更容易卡,是因为 A 机房应用发出的大包在进入隧道后触达小 MTU,而回程的 ICMP 差错又被 B 侧防火墙拦截;B→A 方向的差错报文路径不同、部分能漏回来,于是表现"好一点"。至此,因果链完全闭合。

解决方案

处理分两层:先恢复 PMTUD,再用 MSS 钳制兜底,双保险。

1. 放行 PMTUD 必需的 ICMP。 在 B 机房出口防火墙上,把"一刀切 deny ICMP"改为精确放行差错类报文(尤其是 Type 3 Code 4),仅对 echo-request 做限速而非全禁:

1
2
3
4
# 放行 destination-unreachable(含 fragmentation-needed)
allow icmp type 3
# echo 改为限速,兼顾防洪与可运维
limit icmp echo-request rate 50/s

2. 在隧道网关上做 TCP MSS 钳制(MSS clamping)。 这是应对 MTU 受限隧道最稳妥的通用手段——不依赖 ICMP 是否可达,直接在网关改写经过的 TCP SYN 包里的 MSS 选项,强制两端协商出一个不会超限的分段大小。按隧道实测 MTU 1422 计算,MSS = 1422 − 40(IP+TCP 头)= 1382,保守取整用 1360:

1
2
3
4
5
6
# Linux/iptables 在 FORWARD 链对 SYN 包钳制 MSS
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -o tun0 -j TCPMSS --set-mss 1360
# 或让内核按出接口 MTU 自动算
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -o tun0 -j TCPMSS --clamp-mss-to-pmtu

网络设备(如思科/华为)上对应的是 ip tcp adjust-mss 1360 配到隧道接口。

改完立刻复测:scp 大文件、rsync 全量、跨机房 MySQL 同步全部一次性跑通、速度稳定。之前必卡的几百 MB 全量导入顺畅完成。

根因分析

根因是两个独立因素在"大包 + 跨隧道"场景下叠加:

  1. IPSec 封装开销压低了隧道有效 MTU,1500 的标准以太网包进隧道后超限;
  2. B 侧防火墙一刀切禁用 ICMP,掐断了 PMTUD 的反馈通道,让本可自愈的 MTU 问题恶化成静默黑洞。

单独任一因素都不致命:只有封装开销,PMTUD 能自动降 MSS;只有禁 ICMP,若无小 MTU 链路也无害。两者相遇,才造出"ping 通、小包通、大包静默死"这种最难缠的间歇性故障。而 TCP 建连用小包能成功、真正的数据传输才触发大包,正是它伪装成"应用偶发卡顿"的原因。

预防措施

  • 隧道/Overlay 网关默认配置 MSS 钳制:凡是 IPSec、GRE、VXLAN、PPPoE 等有封装开销的链路,一律在网关上启用 clamp-mss-to-pmtu 或按实测 MTU 固定 MSS,不要指望 PMTUD 一定可用。
  • ICMP 不要一刀切禁用:Type 3(尤其 Code 4 fragmentation-needed)、Type 11(time-exceeded)必须放行,防洪应对 echo 用限速而非全禁。把这条写进防火墙基线评审清单。
  • 上线跨链路业务前做大包连通性验证:用 ping -M do -s <size> 二分探测真实路径 MTU,纳入变更验收,而不是只 ping 一下就签字。
  • 监控补一条大包探针:在跨机房链路上定时发 DF 大包做拨测,MTU 收缩能第一时间告警,而不是等业务传大文件才发现。
  • 变更留痕:那条"防 ICMP 洪水"的规则加的时候没评估副作用。任何安全策略变更都应评估对 PMTUD/路径探测的影响。

总结

这次故障的迷惑性全在于"ping 通"给人的虚假安全感——小包能过不代表链路健康,大包才是真正的试金石。排查的关键转折是改用带 DF 的大包探测,一眼看穿隧道 MTU 被压缩,再顺着"为何没有 ICMP 反馈"揪出被防火墙吞掉的 PMTUD 通道。对所有带封装的隧道链路,记住两条铁律:默认做 MSS 钳制永远不要一刀切禁 ICMP 差错报文。把这两点固化进基线,这类"小包通、大包死"的幽灵故障就能从源头杜绝。

使用 Hugo 构建
主题 StackJimmy 设计