本文目录导读:

tcp_mtu_probing 是 Linux 内核中 TCP/IP 协议栈的一个功能(或参数),用于解决 路径 MTU 发现(PMTUD)失败 的问题,当网络路径中由于 ICMP 不可达消息被防火墙屏蔽,导致无法通过经典的 PMTUD 方法正确发现路径 MTU 时,tcp_mtu_probing 提供了一种主动探测的机制。
它的工作原理是:主动发送不同大小的数据包来探测,而不再完全依赖对方的 ICMP 回复。
以下是其详细的探测过程和工作机制:
核心思想:二分法 或 线性试探
当 tcp_mtu_probing 被启用(设置为 1 或 2)后,TCP 连接的发送端会按照以下逻辑进行探测:
- 初始阶段:TCP 连接建立后,通常使用一个较小且安全的 MSS(最大段大小),1024 字节或更少,或者使用路由器的 MTU 信息,但默认情况下,它会尝试使用网络接口的 MTU(1500 字节)。
- 触发条件:当 TCP 发送一个大于 512 字节(或具体配置的阈值)且没有收到 ACK 确认的数据包时,内核会怀疑此包因尺寸过大被网络中间设备(路由器、防火墙、GRE 隧道等)丢弃了。
- 开始探测:内核不再发送整个大包,而是开始发送一系列 探测包,这些探测包的特点是:
- 设置了
IP_DF(Don‘t Fragment,不分片)位。 - 每次发送一个 小数据包 + 特定大小测试包 的组合。
- 探测包被成功确认(收到 ACK),说明当前测试的大小是可行的。
- 探测包未收到 ACK(超时),说明这个大小可能太大,内核会尝试更小的尺寸。
- 设置了
具体实现步骤
内核会维护一个 sysctl net.ipv4.tcp_mtu_probing 参数,以及辅助参数 tcp_base_mss 和 tcp_mtu_probe_floor。
tcp_mtu_probing = 0: 禁用(默认),使用标准 PMTUD。tcp_mtu_probing = 1: 启用,内核会主动发送探测包(禁用标准 PMTUD 依赖)。tcp_mtu_probing = 2: 启用,并 始终 发送探测包,即使标准 PMTUD 可以正常工作。
探测过程模拟:
假设本地 MTU 是 1500,但路径 MTU 可能是 1400(例如中间有 PPTP VPN 或 IPsec 隧道)。
- 初次发送:发送方尝试发送一个 1500 字节的数据包(未分片)。
- 失败:没有收到 ACK,ICMP 不可达消息可能没回来(被丢弃)。
- 主动探测:内核开始发送一系列探针(Probe):
- 探针 1:发送一个大小为 1440 字节(假设)的探针,等待 ACK。
- 如果收到 ACK -> 说明 1440 太大,需要更小,继续探针 2。
- 如果超时 -> 说明 1440 可以。
target_mss设为 1440,锁定。
- 实际采用的算法类似 指数补偿 或 二分查找:
先试 500 字节(安全),再试 1000,再试 1500,再试 1200 ... 直到找到那个能来回的最大的值。
- 探针 1:发送一个大小为 1440 字节(假设)的探针,等待 ACK。
- 锁定 MTU:一旦找到一个能成功通信的最大尺寸,内核会 缓存 这个路径 MTU 值(在路由缓存或套接字中),后续所有该连接的数据包都以此为上限。
关键参数的配合
tcp_base_mss(默认 512): 定义了探测的基准 MSS 大小,内核从这个值开始向上试探,如果设得太大(1024),探测速度会更快,但可能错过更小的 MTU(导致丢包)。tcp_mtu_probe_floor(默认 48): 定义了探测过程中允许的最小 MTU 值,低于这个值不再探测,防止探测到过于极端的小 MTU。
与标准 PMTUD 的区别
| 特性 | 标准 PMTUD (tcp_mtu_probing=0) |
tcp_mtu_probing 主动探测 (=1 或 2) |
|---|---|---|
| 依赖 | 依赖网络设备发送 ICMP Fragmentation Needed 消息 | 不依赖 ICMP 消息,通过 ACK 超时判断 |
| 触发 | 收到 ICMP 时瞬间得知 MTU 大小 | 发送的包丢失后,通过主动发送小试探包确认 |
| 缺点 | ICMP 被防火墙屏蔽时失效 | 会增加网络延迟(多一次 RTT 用于探测),且会影响初始连接速度 |
实际应用场景
- VPN 隧道(OpenVPN、WireGuard 等):隧道会降低真实 MTU,标准 PMTUD 经常失效。
- NAT 环境:ICMP 容易被 NAT 设备丢弃。
- 公共 Wi-Fi / 蜂窝网络:中间路径复杂,MTU 可能不一致。
如何开启和调整
# 临时开启(sysctl 命令) sysctl -w net.ipv4.tcp_mtu_probing=1 # 持久化(写入 /etc/sysctl.conf 或 /etc/sysctl.d/) echo "net.ipv4.tcp_mtu_probing = 1" >> /etc/sysctl.conf sysctl -p # 调整基准 MSS(可选,通常不需要改) sysctl -w net.ipv4.tcp_base_mss=1024 # 查看当前值 sysctl net.ipv4.tcp_mtu_probing
tcp_mtu_probing 相当于 TCP 协议的 "自我怀疑+主动试探" 模式,当标准路径 MTU 发现(依赖中间设备 “诚实” 发 ICMP 通知)失效时,它通过连续发送不同大小的测试包并观察 ACK 返回情况,使用类似二分法的方式,逐步缩小范围,最终找到该 TCP 连接能使用的最大无分片报文尺寸,从而避免分片或黑洞问题。