什么是MTU和MSS?服务器网络传输中的参数配置与故障排查

有一类网络故障特别气人:能 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;改动前备份规则与配置,在维护窗口操作。