有一类网络故障特别气人:能 ping 通,SSH 也能连上,可一旦传大文件、访问某个接口、上传超过某个大小的图片,连接就卡死或超时。小包正常、大包不通——这几乎可以锁定是 MTU/MSS 的问题。
这种问题在 VPN、隧道、PPPoE 拨号、跨云互联的环境里尤其常见。这篇文章讲清 MTU 和 MSS 是什么、它们的关系、路径 MTU 发现为什么会被"黑洞"打断、怎么用 ping -M do 测出真实 MTU,以及怎么调整 MTU 或做 MSS clamping。读完你能自己定位并解决"大包不通"这类隐蔽故障。
一、MTU 是什么
MTU(最大传输单元)指的是一条链路上单个数据帧能承载的最大数据量,单位是字节。它工作在二层/三层边界上:链路层的帧能装多少载荷,决定了 IP 包最大能做多大。
以太网的标准 MTU 是 1500 字节。超过这个大小的 IP 包,必须分片才能发出去。而"分片"这件事在很多场景里是麻烦的来源。
几个关键点:
- MTU 指的是 IP 包的大小(含 IP 头),不是整个以太网帧(帧还要加 14 字节头等)。
- 标准以太网 MTU = 1500。
- 隧道、VPN、PPPoE 会额外加封装头,导致实际可用的 MTU 变小。
如果链路两端 MTU 不一致,或者路径上某段 MTU 更小却没被正确处理,就会出现大包被丢弃却又无人告知的"MTU 黑洞"。
二、MSS 是什么,和 MTU 什么关系
MSS(最大报文段长度)是 TCP 报文段里数据部分的最大长度,也就是 TCP 一次最多能塞多少应用层数据。它和 MTU 的关系非常直接:
MSS = MTU − IP头长度 − TCP头长度
IPv4 的 IP 头最小 20 字节,TCP 头最小 20 字节,所以:
标准以太网:MSS = 1500 − 20 − 20 = 1460 字节
也就是说,在 MTU 1500 的链路上,TCP 每个报文段最多携带 1460 字节的应用数据。这就是为什么你经常看到 1460 这个数字。
| 场景 | MTU | 计算出的 MSS |
|---|---|---|
| 标准以太网(IPv4) | 1500 | 1460 |
| 标准以太网(IPv6,头 40 字节) | 1500 | 1440 |
| PPPoE 拨号 | 1492 | 1452 |
| IPsec VPN(典型) | 1400 左右 | 约 1360 |
| 常见隧道(如 GRE) | 1476 或更低 | 相应减少 |
MSS 是在 TCP 三次握手时通过 TCP 选项协商的:双方各自在 SYN 里声明"MSS = X",取较小值。所以如果双方都声明 1460,但路径中间某段只支持 1400,那问题就来了——协商出来的 MSS 大了,包发出去就被中间设备丢弃。
三、路径 MTU 发现与 DF 位
路由器按理不该随意分片(分片会增加负担、降低性能),现代 IPv4 引入了 DF 位(Don't Fragment):如果发送方把 IP 包标记为 DF=1,任何一段链路发现包太大放不下时,不能分片,而应该回一个 ICMP "需要分片但 DF 置位"的错误,告诉发送方"把包改小点"。
发送方收到这个 ICMP 后,减小包大小重发。这个动态探测过程就叫路径 MTU 发现(PMTUD)。
问题就出在这里:很多防火墙、安全设备、云网关默认屏蔽 ICMP。于是发送方发的大包被丢,却收不到"请改小"的通知,只能一直重传、一直失败,表现为连接卡死或超时。这就是臭名昭著的 MTU 黑洞(PMTUD blackhole)。
IPv6 更彻底:IPv6 路由器不允许分片,PMTUD 是强制要求,一旦 ICMPv6 "Packet Too Big" 被屏蔽,IPv6 的表现会更糟糕。
举个典型现场:你在云服务器上通过 IPsec 隧道往对端传输一个几 MB 的备份文件,前几 KB 一切正常,传输到中途突然卡住不动,ssh 会话本身却没事。原因就是 SSH 交互包都很小,永远触发不了 MTU 上限,而大文件传输必然会产生接近 MTU 的大包,一旦被路径上更小的 MTU 拦住又收不到 ICMP 反馈,就表现为"传着传着没动静了"。另一个高发场景是网站接口:小请求返回几十字节正常,返回一个几百 KB 的 JSON 或图片时响应卡死,抓包能看到服务端不断重传同一个大包。凡是"大小相关"的网络故障,把 MTU 列进嫌疑名单,排查效率会高很多。
四、什么会让 MTU 变小
任何在原始 IP 包外面再套一层封装的技术,都会吃掉一部分 MTU:
- PPPoE:宽带拨号常见,额外 8 字节,MTU 变 1492,MSS 1452。这是很多家庭宽带"外网大文件下载卡住"的元凶。
- VPN / 隧道(IPsec、GRE、WireGuard 等):封装头少则几十字节,MTU 常在 1400 上下。
- VXLAN / 云网络叠加:数据中心里常见,MTU 需要预留,典型值是 1450。
- 跨云专线 / 多云互联:两端 MTU 不一致,中间链路段更小。
规律很简单:能 ping 通小包、ping 不通大包,就往 MTU 上想。
五、诊断:用 ping 测真实 MTU
最经典的排查方法是 ping 配合 DF 位,逐步试探。以 Linux 为例:
# Linux:-M do 表示不分片,-s 指定数据部分大小(不含 IP/TCP 头)
# 注意:-s 的大小 = MTU − 28(IPv4 的 20 + 8)
# 探测 MTU 1500:数据部分应为 1500 − 28 = 1472
ping -M do -s 1472 -c 3 目标IP
# 若提示 "Message too long" 或丢包,逐步减小
ping -M do -s 1400 -c 3 目标IP
ping -M do -s 1300 -c 3 目标IP
找到能通的最大 -s 值,加上 28 就是该路径的 MTU。比如 -s 1400 能通、-s 1472 不通,说明路径 MTU 大约在 1428 附近,应按 1400 左右配置。
# macOS:-D 表示不分片,-s 指定数据部分
ping -D -s 1472 目标IP
# Windows:使用 -f(不分片)和 -l(数据部分大小)
ping -f -l 1472 目标IP
还要注意一个坑:ping 通小包、不通大包时,先确认中间设备是否回 ICMP。用抓包看有没有收到 ICMP "fragmentation needed":
# 抓 ICMP 消息
sudo tcpdump -nn -i eth0 'icmp'
# 抓 TCP 大包重传
sudo tcpdump -nn -i eth0 'tcp and greater 1400'
如果看到大量重传、却没有对应的 ICMP,就是 PMTUD 黑洞,需要改 MTU 或做 MSS clamping。
六、怎么修复
方法一:调整接口 MTU
如果是本机/本链路的 MTU 偏大,把它调小:
# 查看当前接口 MTU
ip link show eth0
# 或
ip addr show eth0 | grep mtu
# 临时改为 1400(重启失效)
sudo ip link set dev eth0 mtu 1400
# 永久生效(不同发行版方式不同)
# RHEL/CentOS/Rocky 使用 NetworkManager 的 nmcli
sudo nmcli connection modify "有线连接 1" 802-3-ethernet.mtu 1400
sudo nmcli connection up "有线连接 1"
# Debian/Ubuntu 传统 ifupdown:编辑 /etc/network/interfaces
# 在对应接口下加一行: mtu 1400
sudo ip link set dev eth0 mtu 1400 # 或重启网络服务
# systemd-networkd:在 /etc/systemd/network/*.network 的 [Link] 段加 MTUBytes=1400
sudo systemctl restart systemd-networkd
风险提示:改接口 MTU 会短暂中断该接口流量。生产环境务必确认该网段其他设备也支持目标 MTU,避免两头不一致导致更严重的问题。建议先在维护窗口操作,保留一个可远程恢复的通道(如带外管理或控制台)。回滚:把 MTU 改回 1500 或删除配置项重启网络。
方法二:MSS clamping(更推荐)
相比全局改 MTU,MSS clamping 更精准:在网关上把经过的 TCP SYN 包里的 MSS 选项改写成一个更小的值,让两端协商出更小的 MSS,从而让 TCP 包天然不超过路径 MTU。这样不用改接口 MTU,也不影响其他流量。
在 Linux 网关上用 iptables/nftables:
# iptables:对转发流量做 MSS clamp 到 1360
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
# 或指定固定值
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360
# 查看规则
sudo iptables -t mangle -L -n -v
--clamp-mss-to-pmtu 会根据路由表里该路径的 MTU 自动计算合适的 MSS,比写死数值更灵活。若要持久化,RHEL 系用 iptables-services 或 firewalld 的 direct 规则,Debian/Ubuntu 用 iptables-persistent 或 nftables 配置文件。
风险提示:修改 mangle 表规则会直接作用于数据面,规则写错可能导致连接异常。修改前先
iptables-save > /root/iptables.bak备份,出问题时iptables-restore < /root/iptables.bak回滚。
方法三:修正隧道相关配置
如果是 VPN/隧道引起的,通常在隧道工具侧指定 MTU,或把外层接口 MTU 调小。例如 IPsec 场景可设置 mtu 1400;PPPoE 拨号在 pppd 配置里设置 mtu 1492 mru 1492,避免默认 1500 导致的大包丢弃。
七、常见误区
误区一:MTU 调到越大越好。不是。超过路径实际支持的 MTU,包会被丢,反而更慢。要按最小路径 MTU 来配。
误区二:只在自己服务器上调 MTU 就行。MTU 是端到端的问题,要关注整条路径上最小的那一段以及是否返回 ICMP。
误区三:能 ping 通就没问题。ping 默认发的是小包(Windows 默认 32 字节,Linux 默认 56 字节),根本触发不了 MTU 问题。必须用 -M do -s 发大包测。
误区四:MSS 越大吞吐越高。在 MTU 受限的链路上,MSS 太大会导致分片或丢包,吞吐反而下降。
常见问题
Q:为什么 VPN 一开,网页就有些图片加载不出来?
A:典型的 MTU 问题。VPN 封装让可用 MTU 变小,而 PMTUD 被屏蔽。解决:在 VPN 配置里调小 MTU,或在出口做 MSS clamping。
Q:ping -M do -s 1472 不通,是服务器坏了吗?
A:不是。大概率是路径 MTU 小于 1500(比如经过 PPPoE 或隧道),把 -s 调到能通的最大值,加上 28 即为实际 MTU。
Q:MSS 是在什么时候确定的?
A:TCP 三次握手时通过 SYN 包的 MSS 选项协商,双方取较小值。中途网关也可以改写这个选项,即 MSS clamping。
Q:为什么 IPv6 的 MSS 比 IPv4 小 20?
A:IPv6 头固定 40 字节,比 IPv4 的 20 字节多 20,所以同样 MTU 1500 下,IPv6 的 MSS = 1500 − 40 − 20 = 1440。
Q:抓包看到很多重传但没有 ICMP,怎么办?
A:这是 PMTUD 黑洞特征。要么放行必要的 ICMP(尤其是 "fragmentation needed"/IPv6 的 "packet too big"),要么在网关做 MSS clamping,或直接调小 MTU。
总结
- MTU 是链路单帧可承载的最大 IP 包大小,标准以太网为 1500;MSS 是 TCP 数据部分最大值,MSS = MTU − IP 头 − TCP 头,IPv4 标准场景为 1460。
- PPPoE、VPN、隧道会减小可用 MTU,是"小包能通、大包不通"的头号原因。
- PMTUD 依赖 ICMP 反馈,一旦 ICMP 被屏蔽就成了 MTU 黑洞,表现为大包静默丢弃、连接卡死。
- 诊断用
ping -M do -s从大往小试,找到能通的最大值;抓包观察重传与 ICMP 反馈。 - 修复优先用网关上的 MSS clamping(
--clamp-mss-to-pmtu),其次调整接口 MTU;改动前备份规则与配置,在维护窗口操作。
企业QQ咨询




