ping6.net

IPv6 MTU 和路径 MTU 发现:避免分片问题

了解路径 MTU 发现在 IPv6 中的工作原理,为什么路由器不能对数据包进行分片,以及如何排查与 MTU 相关的连接问题。

ping6.net2024年12月14日6 min read
IPv6MTUPMTUDfragmentationnetworkingtroubleshooting

数据包分片是那些你不注意它就不会出问题,一出问题就会导致一切崩溃的问题之一。

TL;DR - 快速摘要

要点:

  • IPv6 路由器不能分片:源主机必须通过路径 MTU 发现处理所有分片
  • 永远不要阻止 ICMPv6 类型 2:Packet Too Big 消息对于 PMTUD 工作至关重要
  • 最小 MTU 为 1280 字节:所有 IPv6 链路必须支持此值,用作后备
  • 隧道减少 MTU:考虑封装开销(6in4:20 字节,VPN:40-80 字节)
  • 使用 MSS clamping:当 PMTUD 失败时强制 TCP 使用较小的段

跳转至: PMTUD 黑洞 | 诊断 MTU 问题 | 防火墙配置


为什么 MTU 在 IPv6 中更重要#

IPv6 完全移除了基于路由器的分片。在 IPv4 中,如果路由器收到一个对于下一跳来说太大的数据包,它可以对其进行分片。IPv6 路由器不能这样做——它们会丢弃数据包并向源发送 ICMPv6"数据包过大"消息。

源主机负责所有分片。这个设计决定提高了路由器性能并简化了协议,但它将负担转移到了端点上,使得路径 MTU 发现(PMTUD)变得至关重要。

如果 PMTUD 失败,你的连接会神秘地挂起。TCP 握手完成,但数据传输停滞。HTTP 请求超时。SSH 会话在身份验证后冻结。这些都是典型的 MTU 黑洞症状。

IPv6 最小 MTU#

所有 IPv6 链路必须支持至少 1280 字节。这是最小 MTU,不可协商。如果你的链路无法处理 1280 字节的数据包,它就不符合 IPv6 标准。

大多数网络使用 1500 字节(标准以太网 MTU),这为 40 字节的 IPv6 头部加上 1460 字节的有效载荷提供了空间。这相当于 IPv4 的典型 TCP MSS 1460。

隧道和封装协议会减少可用的 MTU。如果你运行 IPv6-in-IPv4(6in4)、6to4 或 ISATAP,你会因为外部头部而损失字节。VPN、IPsec、GRE 和 VXLAN 都会消耗 MTU 空间。

你会遇到的常见 MTU 值:

  • 1500:标准以太网
  • 1480:PPPoE(PPP 开销损失 8 字节)
  • 1460:6in4 隧道(IPv4 头部损失 20 字节)
  • 1420:PPPoE 上的 6in4
  • 1280:最小 IPv6 MTU,回退值
  • 9000:巨型帧(数据中心网络)

路径 MTU 发现的工作原理#

PMTUD 确定可以在源和目标之间遍历路径而不进行分片的最大数据包大小。

过程很简单:

  1. 主机使用当前 MTU 发送数据包
  2. 如果路径上的路由器具有较小的 MTU,它会丢弃数据包
  3. 路由器向源发送 ICMPv6 类型 2"数据包过大"消息
  4. 消息包含下一跳的 MTU
  5. 主机减小其路径 MTU 并重新发送
  6. 过程重复,直到数据包遍历整个路径

"数据包过大"消息(ICMPv6 类型 2,代码 0)在消息有效载荷中包含 MTU 值。这告诉源数据包可以有多大,而不仅仅是它们太大了。

ICMPv6 消息如下所示:

ICMPv6 Packet Too Big:
  Type: 2
  Code: 0
  MTU: 1280
  [Original packet headers up to minimum MTU]

主机维护一个路径 MTU 缓存,其条目通常在 10 分钟后过期(RFC 8201 建议)。如果网络路径发生变化,过时的缓存条目可能会导致问题,直到它们过期。

PMTUD 黑洞#

当 ICMPv6 类型 2 消息被阻止时,系统会崩溃。这会创建"PMTUD 黑洞",数据包会悄无声息地消失。

常见原因:

过度热心的防火墙:管理员有时会阻止所有 ICMP 流量,将其视为安全风险。这是错误的。ICMPv6 是 IPv6 操作的组成部分,不是像 ping 这样的可选诊断协议。

状态检查问题:一些防火墙不能正确地将 ICMPv6 错误消息与现有连接关联,将它们作为未经请求的流量丢弃。

速率限制:安全设备可能会限制 ICMPv6 的速率,在繁忙期间丢弃合法的 PMTUD 消息。

NAT64 网关:如果网关不能正确转换 ICMP 消息,IPv6 和 IPv4 之间的转换可能会丢失 PMTUD 信息。

PMTUD 黑洞的症状:

  • TCP 连接建立(SYN、SYN-ACK、ACK 完成)
  • 小数据包工作(DNS 查询、SSH 登录横幅)
  • 大数据包失败(文件传输、带正文的 HTTP POST、身份验证后的 SSH 交互会话)
  • 没有错误消息,只有超时
  • 问题看起来是间歇性的(取决于数据包大小)

诊断 MTU 问题#

从 ping 开始。发送特定大小的数据包以测试路径 MTU:

# 使用 1280 字节测试(最小 MTU)
ping6 -M do -s 1232 2001:db8::1
 
# 使用 1500 字节测试(标准以太网)
ping6 -M do -s 1452 2001:db8::1
 
# 使用 1280 字节有效载荷测试
ping6 -c 5 -s 1280 2001:db8::1

-M do 标志禁止分片(不分片)。-s 参数指定有效载荷大小——为 ICMPv6 头部添加 8 字节,为 IPv6 头部添加 40 字节以获得总数据包大小。

如果 1452 字节失败但 1232 成功,你的路径 MTU 在 1280 和 1500 之间。二分查找以找到确切值:

# 尝试 1400
ping6 -M do -s 1352 2001:db8::1
 
# 尝试 1450
ping6 -M do -s 1402 2001:db8::1

使用 traceroute 识别 MTU 更改的位置:

traceroute6 -n 2001:db8::1

然后单独测试每个跳的 MTU。大数据包开始失败的跳是你的问题点。

对于 TCP 连接,观察实际行为:

# 捕获数据包
tcpdump -i eth0 -n 'ip6 and (icmp6 or tcp)'

寻找:

  • 初始握手后的 TCP 重传
  • ICMPv6 类型 2 消息(如果 PMTUD 正常工作应该出现)
  • 缺少 ICMPv6 类型 2(表示阻塞或黑洞)

现代工具可以自动化这一点。MTR 将 traceroute 与数据包大小测试相结合:

# 使用 1400 字节数据包测试
mtr -6 -s 1400 2001:db8::1

TCP MSS 限制#

MSS(最大段大小)是 TCP 选项,它告诉另一端每个段可以发送多少数据。MSS = MTU - IP 头部 - TCP 头部。

对于 IPv6:MSS = MTU - 40(IPv6)- 20(TCP)= MTU - 60

使用标准 1500 字节 MTU:MSS = 1440

MSS 限制强制 TCP 连接使用较低的 MSS 值,在不依赖 PMTUD 的情况下解决 MTU 问题。这在终止隧道或 PPPoE 连接的路由器上很常见。

在 Linux 上配置 MSS 限制:

# 将 IPv6 的 MSS 限制为 1280
ip6tables -t mangle -A FORWARD \
  -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --set-mss 1220

值 1220 考虑了 1280 MTU 减去 60 字节的头部。

对于 1492 MTU 的 PPPoE:

ip6tables -t mangle -A FORWARD \
  -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --set-mss 1432

MSS 限制仅影响 TCP。UDP、SCTP 和其他协议仍然依赖 PMTUD 或应用层分片。

防火墙配置#

你的防火墙必须允许 ICMPv6 类型 2 消息才能使 PMTUD 正常工作。这对于生产 IPv6 网络来说是不可协商的。

IPv6 操作所需的最小 ICMPv6 类型:

  • 类型 1:目标不可达
  • 类型 2:数据包过大(PMTUD)
  • 类型 3:时间超过
  • 类型 4:参数问题
  • 类型 133-137:邻居发现协议

PMTUD 的 iptables 规则:

# 允许 ICMPv6 数据包过大
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT
ip6tables -A FORWARD -p ipv6-icmp --icmpv6-type 2 -j ACCEPT

更好的方法——允许所有必要的 ICMPv6:

# 允许已建立的 ICMPv6
ip6tables -A INPUT -p ipv6-icmp -m state --state ESTABLISHED,RELATED -j ACCEPT
 
# 允许必要的 ICMPv6 类型
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 1 -j ACCEPT    # Destination Unreachable
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT    # Packet Too Big
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 3 -j ACCEPT    # Time Exceeded
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 4 -j ACCEPT    # Parameter Problem
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -j ACCEPT  # Echo Request
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 129 -j ACCEPT  # Echo Reply
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 133 -j ACCEPT  # Router Solicitation
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 134 -j ACCEPT  # Router Advertisement
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -j ACCEPT  # Neighbor Solicitation
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 136 -j ACCEPT  # Neighbor Advertisement

速率限制是可以接受的,但要设置宽松的限制:

# 允许带速率限制的 ICMPv6
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 2 \
  -m limit --limit 10/sec --limit-burst 20 -j ACCEPT

永远不要完全阻止 ICMPv6。这不是安全风险——这是协议要求。

隧道 MTU 注意事项#

隧道通过封装头部的开销减少有效 MTU。

常见隧道类型和开销:

隧道类型开销有效 MTU(来自 1500)
6in420 字节1480
6to420 字节1480
6rd20 字节1480
ISATAP20 字节1480
GRE24 字节1476
IPsec50-70 字节1430-1450
WireGuard60 字节1420
OpenVPN40-80 字节1420-1460

明确配置隧道 MTU 以避免问题:

# 6in4 隧道
ip tunnel add tun6in4 mode sit remote 203.0.113.1 local 198.51.100.1
ip link set tun6in4 mtu 1480
ip link set tun6in4 up

对于 WireGuard:

[Interface]
MTU = 1420

一些隧道协议支持 MTU 发现,但显式配置更可靠。

巨型帧和数据中心#

数据中心网络通常使用 9000 字节 MTU(巨型帧)来提高批量传输的吞吐量。

在接口上启用巨型帧:

ip link set eth0 mtu 9000

确保路径中的所有交换机和路由器都支持巨型帧。一个具有 1500 字节 MTU 的设备将成为瓶颈。

PMTUD 仍然适用。如果数据包离开你的巨型帧网络前往互联网,它必须减少到 1500 或更少。

测试和验证#

验证主机的 MTU 配置:

ip -6 link show

查找每个接口上的 MTU 值。

检查当前路径 MTU 缓存:

ip -6 route get 2001:db8::1

输出显示该目标的缓存 MTU。

使用不同的数据包大小测试端到端连接:

for size in 1232 1352 1402 1452; do
  echo "Testing $size bytes:"
  ping6 -M do -s $size -c 3 2001:db8::1
done

使用 netcat 测试 TCP MSS 协商:

# 服务器
nc -6 -l 8080
 
# 客户端
nc -6 2001:db8::1 8080

使用 tcpdump 捕获数据包并验证 SYN 数据包中的 MSS 值。

常见场景和修复#

问题:网页加载缓慢,图像失败 原因:由于防火墙阻止 ICMPv6 类型 2 导致的 PMTUD 黑洞 修复:配置防火墙以允许 ICMPv6 类型 2

问题:SSH 连接但登录后冻结 原因:MTU 太大,PMTUD 不工作 修复:减小客户端接口上的 MTU 或在路由器上启用 MSS 限制

问题:VPN 适用于小传输,大文件失败 原因:隧道 MTU 配置不正确 修复:减小隧道 MTU 以考虑封装开销

问题:IPv6 在本地网络上工作,通过互联网失败 原因:上游路由器具有较小的 MTU,没有正确通告 修复:联系 ISP 或将本地 MTU 减小到 1280(最小值)

问题:某些目标工作,其他失败 原因:路径特定的 MTU 问题 修复:使用 ping 测试以识别工作的 MTU,相应地配置接口

最佳实践#

  1. 始终在防火墙中允许 ICMPv6 类型 2
  2. 明确配置隧道 MTU,而不是依赖自动检测
  3. 使用最小 MTU(1280)测试以确保基线连接
  4. 在隧道端点和 PPPoE 连接上使用 MSS 限制
  5. 在故障排除期间使用数据包捕获监控 PMTUD 失败
  6. 记录每一层的网络 MTU(物理、隧道、应用)
  7. 在有疑问时设置保守的 MTU 值(1280 始终有效)

相关文章#

测试路径 MTU

使用我们的Ping 工具测试不同大小的数据包传递,使用MTR 工具识别路径上 MTU 更改的位置。

其他资源#