服务器如何排查MTU不匹配问题?大数据包丢失与路径MTU检测

"能打开网页首页,但一提交表单就卡死""SSH 能连上,但传大文件就断""ping 小包正常,ping 大包直接不通""VPN 建起来了,但访问内部网站一直转圈"——这些症状有一个共同的、极容易被误判的根因:MTU 不匹配。它不像普通丢包那样持续丢失,而是"小包全通、大包必丢",特征鲜明但常被当成网络不稳定。

这篇文章讲清 MTU 问题的来龙去脉:先解释 MTU、分片、路径 MTU 发现(PMTUD)的机制,再给出检测"当前路径的 MTU 到底是多少"的具体命令,然后讲清最常见的触发场景(VPN、隧道、PPPoE、云网络),最后给出修复方法和风险提示。命令覆盖 Linux 服务器端,兼顾 ping、tracepath、ss、iptables 等工具。

核心概念要先立住:链路层每帧能承载的最大数据量就是 MTU(最大传输单元),以太网默认 1500 字节。数据包如果超过路径上最小的一跳的 MTU,要么被分片,要么被丢弃。现代网络依赖"路径 MTU 发现"机制来自动协商,但当 ICMP 被防火墙拦截时,这个机制就会失效,问题随之出现。

一、MTU、分片与路径 MTU 发现

MTU(Maximum Transmission Unit) 是链路层能承载的最大有效载荷。几种常见链路的 MTU:

链路类型 典型 MTU 备注
标准以太网 1500 最常见的默认值
PPPoE(部分宽带) 1492 比以太网小 8 字节
VPN(IPsec/GRE) 1400-1460 看封装开销
VXLAN 隧道 1450 50 字节封装开销
IPv6 最小要求 1280 不能低于此值
环回 lo 65536 不走物理链路
巨型帧以太网 9000 需端到端都支持

分片:当一个 IP 包大于出接口的 MTU 时,发送方或中间设备会把它切成多个分片。IPv4 允许中间路由器分片,IPv6 则把分片限制在源端(中间设备遇到过大包直接丢弃并回 ICMPv6 "Packet Too Big")。

路径 MTU 发现(PMTUD):源端先按本地 MTU 发送,如果路径上某跳 MTU 更小,那个设备会丢弃该包并返回一个 ICMP "Fragmentation Needed"(IPv4 是 Type 3 Code 4,IPv6 是 ICMPv6 Type 2)报文,告知实际可用的 MTU。源端据此调小发送尺寸。同时,发送方会设置 DF(Don't Fragment)位,禁止中间分片,以触发这个反馈机制。

问题就出在这里:很多防火墙、安全设备会拦截 ICMP 报文(包括 "Fragmentation Needed"),导致源端收不到反馈,傻傻地继续发大包,大包被一路丢弃,表现为"小包通、大包不通"。这就是经典的"PMTUD 黑洞"。

二、先确认症状是不是 MTU 问题

MTU 问题的特征很明确,遇到以下现象优先怀疑:

  • 小数据量操作正常(ping、打开小页面),大数据量操作失败(大文件上传、大 POST 请求、数据库大查询)。
  • TLS 握手成功但传输大数据时卡住(因为证书链或大响应超过了 MTU)。
  • SSH 能登录,但执行输出多的命令卡死,或 scp/sftp 传大文件中途断。
  • VPN/隧道建立后,访问某些站点正常、某些失败。

验证方法:用小包 ping 通、用大包 ping 不通来快速判定。

# 小包 ping,应该通
ping -c 3 -s 100 8.8.8.8

# 大包 ping,设置 DF 位禁止分片,逐步逼近 MTU
# -M do 表示禁止分片(Linux ping 的参数),-s 指定负载大小
ping -c 3 -M do -s 1472 8.8.8.8
# 1472 + 28(IP 20 + ICMP 8)= 1500,正好是标准以太网 MTU

# 如果 1472 不通,逐步减小 -s 找临界值
ping -c 3 -M do -s 1400 8.8.8.8
ping -c 3 -M do -s 1300 8.8.8.8
ping -c 3 -M do -s 1200 8.8.8.8

如果 -s 1472 不通但小包通,并能找到一个可通的临界值(比如 1400),那么路径 MTU 大致就是临界值 + 28。这个测试既确认了症状,又估出了 MTU。

注意:有些网络会拦截 ICMP,导致 -M do 大包测试也不准。这时要结合 tracepath 和实际应用层现象综合判断。

三、用 tracepath 精确定位路径 MTU

ping 只能测到目标,tracepath 能逐跳发现路径上的 MTU 瓶颈,是排查 MTU 问题的首选工具。

# tracepath 会自动做 PMTUD 探测,报告每一跳的 MTU
tracepath 8.8.8.8
tracepath -n 8.8.8.8        # -n 不解析主机名,更快

# IPv6 版本
tracepath6 2400:3200::1
# 或者用 tracepath -6

# 安装:RHEL/CentOS/Rocky
yum install -y iputils      # 或 dnf install iputils
# Debian/Ubuntu
apt install -y iputils-tracepath

tracepath 的输出会显示每一跳的 pmtu(路径 MTU)值,随着跳数增加逐渐变小,直到稳定在最终路径 MTU。比如:

 1?: [LOCALHOST]                      pmtu 1500
 1:  192.168.1.1                       0.5ms
 2:  10.0.0.1                          2.1ms pmtu 1400
 3:  203.0.113.1                       8.3ms
 4:  8.8.8.8                          12.5ms reached

这个例子里第 2 跳把 pmtu 降到了 1400,说明有一条 1400 的隧道或链路在中间。

补充手段:用 mtr 也能看出一些端倪(大包丢、小包不丢的跳),但它不直接报 MTU,tracepath 才是专门干这个的。

# mtr 大包测试对比(小包 vs 大包丢包差异)
mtr -rw -c 20 -s 1400 8.8.8.8
mtr -rw -c 20 -s 100 8.8.8.8

四、常见触发场景与原因

MTU 问题不会无缘无故出现,它总和一个"改变了封装结构"的因素相关:

场景 原因 典型可用 MTU(经验值)
PPPoE 宽带拨号 多了 8 字节 PPPoE 头 1492
IPsec VPN 加密封装开销 1400-1460
GRE 隧道 GRE 头开销 1476 或更低
VXLAN/Overlay 网络 50 字节封装 1450
双栈 IPv6 IPv6 头更长,且中间不分片 通常需降到 1280-1480
云网络跨区/EIP 云内封装 看厂商,常 1450 左右
中间设备封 ICMP PMTUD 黑洞 各种值都可能

还有一个特别隐蔽的场景:只在某些目标不可用。因为路径 MTU 是"到不同目标走不同路径、经过不同设备"决定的,所以可能访问 A 站正常、访问 B 站失败,取决于路径上有没有小 MTU 的瓶颈、以及那些设备是否放行 ICMP 反馈。

反过来,云服务器遇到的现象很典型:同可用区内部通信正常,跨可用区或走公网 EIP 时大包出问题。因为云内的虚拟网络(Overlay)会给包加上封装头,实际可用 MTU 比 1500 小。主流云厂商会在文档里给出各场景的推荐 MTU 值,配置 VPN、隧道的容器网络时要特别注意。

五、检测本机与接口的 MTU

先从自己这头看起:

# 查看所有网卡的 MTU
ip link show
ip -br link show           # 简洁输出
# 关注 mtu 字段,例如 mtu 1500 / mtu 1450

# 查看某张网卡
ip link show dev eth0

# 查看路由的 MTU 设置(路由也可以下发 MTU)
ip route show
ip route get 8.8.8.8
# 输出里若有 "mtu 1400" 表示这条路由限定了 MTU

# 查看 TCP 的 MSS 相关信息
ss -tin | grep -i mss      # 看已建立连接的 MSS
ip route show | grep -i mtu

# 查看接口统计里的分片情况
netstat -i
ip -s link show eth0

如果本机接口 MTU 是 1500,而路径实际 MTU 更小,那问题在路径或 PMTUD 失效;如果本机接口被设成了非标准值(比如 9000 去连不支持巨型帧的网络),那就是配置问题。

六、修复方法

修复 MTU 问题有两条路线:修正路径 MTU 发现(治本但依赖中间设备配合)和主动降低 MTU/MSS(治标但立竿见影)。

方法一:直接调小接口 MTU。 适合隧道、VPN 场景,把出接口 MTU 设成路径安全值。

# 临时调小 MTU(重启失效)
ip link set dev eth0 mtu 1400

# 调回
ip link set dev eth0 mtu 1500

# 持久化(Ubuntu netplan)
# ethernets:
#   eth0:
#     mtu: 1400
netplan apply

# 持久化(RHEL/CentOS/Rocky ifcfg)
# MTU=1400 写入 /etc/sysconfig/network-scripts/ifcfg-eth0
# 或用 nmcli
nmcli con mod eth0 802-3-ethernet.mtu 1400
nmcli con up eth0

方法二:调整 TCP MSS(推荐,影响面更小)。 MTU 作用在所有流量上,MSS 只影响 TCP,通过钳制 MSS 让 TCP 主动协商出更小的段,常用来搞定"PMTUD 黑洞"而不必动整机 MTU。

# 在出口方向把 TCP SYN 里的 MSS 钳到指定值(clamp-mss-to-pmtu 表示按 PMTU 自动钳制)
# 这是最常用的抗 PMTUD 黑洞手段
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# 或固定钳到某个值(MSS = MTU - 40,比如 MTU 1400 则 MSS 1360)
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

# 本机出向流量钳制(作用于本机发起的连接)
iptables -t mangle -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# 保存规则
# Debian/Ubuntu
netfilter-persistent save
# RHEL/CentOS/Rocky(用 firewalld 时通过 firewalld 配置,或 iptables-save)
iptables-save > /etc/sysconfig/iptables

方法三:修 PMTUD(放行必要的 ICMP)。 如果确认是中间设备拦截了 ICMP 反馈,理论治本方案是放行 ICMP 的 "Fragmentation Needed"(IPv4 Type 3 Code 4)和 ICMPv6 Type 2。但中间设备往往不归你控制,所以实践中方法二更常用。

# 如果自己的防火墙误拦了 ICMP,需要放行(示例)
# 放行 ICMP type 3 code 4(fragmentation needed)
iptables -A INPUT -p icmp --icmp-type fragmentation-needed -j ACCEPT
# 放行 ICMPv6 "packet too big"(type 2)
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT
# 更简单:放行所有 ICMP echo 和必要的错误报文,不要一刀切封 ICMP
iptables -A INPUT -p icmp -j ACCEPT

风险提示与回滚:

  • ip link set dev eth0 mtu 在线生效,改错可能导致大流量异常甚至短暂断连,远程操作要谨慎;回滚就是设回原值(通常是 1500)。
  • iptables 的 MSS 钳制规则如果是全量 FORWARD,会影响转发流量性能(略增 CPU),且规则写错可能影响所有 TCP 连接。改前先 iptables-save > /root/iptables.bak,用 -I(插入到顶部)时特别注意顺序,回滚用 -D 删除同一条规则或恢复备份。
  • 调整 MTU 会改变分片行为,某些依赖固定包大小的老旧设备可能受影响,生产环境先在测试链路验证。
  • 不要把"放行所有 ICMP"当成万能,虽然放行必要的 ICMP 错误报文是对的,但也要评估安全影响,至少保留速率限制。

七、验证修复效果

改完之后要验证,方法还是那套大包测试:

# 大包 ping 测试(应能通过)
ping -c 3 -M do -s 1472 8.8.8.8      # 若 MTU 已调整到 1500 且 PMTUD 正常

# 按新的 MTU 测试(例如 MTU 1400,则 -s = 1400-28 = 1372)
ping -c 3 -M do -s 1372 8.8.8.8

# 用 tracepath 确认路径 MTU
tracepath -n 8.8.8.8

# 实际业务验证:大文件传输、大 POST、数据库大查询
curl -o /dev/null -m 60 https://www.example.com/bigfile
# 或用 scp/rsync 传一个较大文件,观察是否中断

只有实际业务(大文件、大请求)恢复正常,才算真正修好。ping 通了但应用还断,可能是应用层还有别的限制。

常见问题

Q:ping 大包不通但小包通,一定是 MTU 问题吗?
大概率是。但要排除"目标禁止 ICMP 大包"和"中间设备对 ICMP 限速"的干扰。建议结合 tracepath 和实际业务现象判断,业务层面"小数据正常、大数据失败"才是更可靠的证据。

Q:为什么改了 MTU 有的网站能开、有的还是不行?
因为路径 MTU 是"到具体目标"的,不同目标走不同路径、经过不同设备。你调小了本机 MTU 只解决了自己这侧,如果对端或中间链路仍有问题,某些站点依然会失败。这类情况更适合用 MSS 钳制配合排查。

Q:tracepath 报的 pmtu 一直是 1500,但业务确实大包失败,怎么解释?
可能是 PMTUD 探测用的 ICMP 被放行了,但实际数据流经过了另一条策略路由、或某个设备只对特定协议做了限制。也可能是 TCP MSS 协商问题。用 ss -tin 看实际连接的 MSS,并做双向抓包确认。

Q:云服务器需要改 MTU 吗?
取决于云厂商的虚拟网络封装。如果厂商文档说明内网可用 MTU 小于 1500(常见 1450 左右),那么在部署 VPN、隧道或容器 Overlay 时要相应降低 MTU,否则大包会有问题。按厂商推荐值配置即可。

Q:为什么 VPN 一连上就访问不了内网网站?
这是经典的 VPN MTU 问题。VPN 封装减小了可用 MTU,而客户端仍按 1500 发大包,PMTUD 又被拦截,于是大包被丢。解决办法是把 VPN 接口的 MTU 调小(IPsec 常用 1400,OpenVPN 常试 1400-1450),或在服务端做 MSS 钳制。

总结

MTU 问题的典型特征是"小包通、大包丢",常伴随"能登录但传大文件断""页面能开但提交表单卡死",排查时要优先怀疑它而不是误判成网络不稳定。

检测分三步:用 ping -M do -s <大小> 逐步逼近可通尺寸快速判定症状;用 tracepath 逐跳发现路径 MTU 瓶颈;用 ip link/ip route 看本机接口和路由的 MTU 设置。

根因通常是封装开销(PPPoE、VPN、GRE、VXLAN 使可用 MTU 变小)加 PMTUD 反馈被防火墙拦截(PMTUD 黑洞),两者叠加就形成"大包必丢"。

修复优先用 TCP MSS 钳制(iptables -t mangle ... TCPMSS --clamp-mss-to-pmtu),影响面小于整机改 MTU;隧道/VPN 场景可把接口 MTU 调小到路径安全值(常 1400-1450);治本要放行必要的 ICMP "Fragmentation Needed" 和 ICMPv6 "Packet Too Big"。

所有 MTU 和防火墙变更前先备份(iptables-save、记录原 MTU),改完必须用大包 ping 和实际大文件传输双重验证,确认业务真正恢复。