1. 网络基础与“包裹”解构

核心目标: 理解每一层协议对数据包大小的限制,以及为什么“开销”不可避免。

1.1 为什么 MTU 属于第二层?

  • MTU (Maximum Transmission Unit):指数据链路层(L2)能通过的最大数据帧长度。
  • 物理制约:就像隧道的限高。以太网标准(IEEE 802.3)规定默认 MTU 为 1500 字节
  • 常见介质默认值:
    • 以太网 (Ethernet): 1500
    • Wi-Fi: 2304(由于无线环境不稳定,系统常会协商降至 1500)
    • PPPoE (拨号上网): 1492(因为多了 8 字节的 PPPoE 封装头)

1.2 数据包头部的“代价” (Overhead)

当你发送数据时,每一层都会增加报头:

  • IPv4 报头: 固定 20 字节。
  • IPv6 报头: 固定 40 字节。
  • TCP 报头: 固定 20 字节(不含选项)。
  • UDP 报头: 8 字节。
  • 计算公式:
    $$MTU = Payload + Headers$$

1.3 MTU vs. MSS

  • MSS (Maximum Segment Size):TCP 层允许的最大负载大小。
  • 核心逻辑:在 TCP 三次握手阶段,双方会告知自己的 MSS,通常取两端最小值。
  • 计算公式:
    $$MSS = MTU - IP_Header - TCP_Header$$
    • 标准以太网:$1500 - 20 - 20 = 1460$ 字节。

2. 路径发现与“撞墙”机制

核心目标: 理解数据包在传输路径中如何“沟通”失败,以及为何“全禁 ICMP”是运维的噩梦。

2.1 分片(Fragmentation)与重组

当 1500 字节的包遇到 MTU 1400 的路由器:

  1. 路由器拆包:将数据拆成两个 IP 包。
  2. 性能损耗:路由器 CPU 压力增大;且一旦丢掉任何一个分片,整个原始大包都要重传。

2.2 DF (Don’t Fragment) 位

现代系统(如 SSH、HTTPS)为了性能,通常在 IP 报头设置 DF=1。这意味着:“宁可丢弃,也别分片。”

2.3 PMTUD (Path MTU Discovery)

  • 原理:发送方发大包(DF=1),若路由器过不去,需返回 ICMP Type 3 Code 4 (Fragmentation Needed)。发送方收到后会调小包大小重发。
  • ICMP 黑洞:若防火墙禁掉 ICMP,发送方永远收不到这个“Packet Too Big”信号。

    现象: Ping 能通(小包),SSH 握手能成功(小包),但一旦传输密钥或执行 ls(大包),连接就会卡死。

3. 隧道封装与“空间剥削”

核心目标: 理解 VPN 和虚拟化为什么是 MTU 问题的重灾区。

3.1 封装开销(Encapsulation Overhead)

VPN 将原始数据包“套娃”在另一个协议里,导致可用空间被压缩。

协议 典型损耗 (Bytes) 建议 MTU (基于 1500)
WireGuard (Netbird) 60 (IPv4) / 80 (IPv6) 1420
VXLAN (PVE 集群) 50 1450
Tailscale (DERP) 60+ 1280 (保守兼容值)

3.2 嵌套隧道 (Nested Tunnels)

如果在 MTU 1400 的公司 VPN 里跑 Netbird(-60),有效空间将减至 1340。如果不手动调整,网络必出异常。

3.3 TCP MSS Clamping(上帝模式)

在网关上强行修改 TCP SYN 包里的 MSS 值。

  • 用途:无需修改客户端,只需在出口网关一劳永逸。
  • iptables 命令
    iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

4. 实战诊断工具箱

核心目标: 掌握定位路径“窄点”的方法。

4.1 二分法探测法

手动指定包大小并设置 DF 位,测试临界点:

  • macOS: ping -D -s 1472 1.1.1.1
  • Linux: ping -M do -s 1472 1.1.1.1
  • Windows: ping -f -l 1472 1.1.1.1
    (注:1472 数据 + 28 报头 = 1500。如果 1472 不通但 1400 通,说明路径 MTU < 1500)

4.2 路由追踪分析

使用 tracepath 自动探测路径上每一跳的 PMTU:

tracepath 1.1.1.1
# 输出示例: 1: [localhost] pmtu 1500
#          2: [router] pmtu 1420

4.3 常见故障场景复盘

  1. Docker 镜像拉取一半报错:通常是宿主机隧道 MTU (1420) 与 Docker 网桥 (1500) 不匹配。
  2. 网页文字显示正常,图片加载不出来:文字是小包,图片数据触碰了 MSS 上限。
  3. SSH 登录后执行 cat 大文件直接断开:典型的 MTU 限制且 ICMP 被屏蔽。

5. 为什么网页访问不会遇到,但是 ssh 就会报错

无论是访问网页还是启动 SSH,最开始的步骤都是 TCP 三次握手

  • SYN / SYN-ACK / ACK:这些控制报文非常小(通常只有 40-60 字节)。
  • 结果:即使路径上某个地方的 MTU 只有 1280,这些小包也能轻松穿过。所以你会看到 SSH 提示你输入密码,或者网页的 Header 已经返回了。
    SSH 和普通网页访问的一个重大区别在于它对 数据包大小 的敏感性:
  • 密钥交换 (Key Exchange):在输入密码前后,SSH 会交换加密密钥。为了安全,这些包含证书和密钥的数据包通常很大,往往会触及 1500 字节 的上限。
  • DF 位 (Don’t Fragment):SSH 为了保证安全性及防止分片攻击,发出的 IP 包几乎全部带有 DF=1 标志。
  • 结果:大包撞上了 MTU 较小的路由器(比如你的 Netbird 隧道或公司防火墙),路由器想分片但被 DF 标志禁止,只能将包丢弃。

6. 核心元凶:ICMP 黑洞

最让 SSH 痛苦的是 ICMP 被防火墙拦截

  1. 路由器丢弃了大包。
  2. 路由器尝试回发一个 ICMP 包说“Packet Too Big”。
  3. 但是,你路径上的某个防火墙(或者你自己的服务器配置)认为 ICMP 是危险的,把它 丢弃 了。
  4. 结果:你的 SSH 客户端一直在等那个大包的确认,路由器一直在等客户端减小包大小。双方陷入了死锁,直到你看到 Connection timed out