一次 Clash Hysteria2 连不上引出的 MTU 问题排查
最近遇到一个很怪的问题:
同一组 Hysteria2 节点,在云服务器上测试完全正常,但在家里网络下就是连不上。更奇怪的是,服务端监听、防火墙放行、客户端参数看起来都没问题,甚至从外部机器测试也能正常出流量。
一开始怀疑是服务端没有监听 IPv4,后来确认服务端绑定在双栈地址上,系统也允许 IPv4 映射访问。再用外部 VPS 跑 sing-box 客户端测试,两个节点都能成功连接,出口 IP 也符合预期。这基本排除了节点配置、密码、SNI、证书跳过验证等常见问题。
问题集中到本地网络后,我在家里的 Linux 主机上跑同样的 sing-box 配置,结果两个不同节点都失败。日志里出现过类似:
read udp ...: message too long timeout: no recent network activity
这时开始怀疑是 UDP/QUIC 和 MTU 的问题。
继续看路由信息,察觉本地出口并不是普通 IPv4 WAN,而是 IPv4-over-IPv6 (ipip6) 隧道。
隧道接口 MTU 只有 1280。本地到外部节点的路由缓存也显示 MTU 为 1280。
抓包后问题就比较清楚了:
Hysteria2/QUIC 初始 UDP 包长度接近 1280,经过 IPv4 封装后发生了分片。对于 TCP,路由器上的 MSS 钳制 可以缓解 MTU 问题。
但 Hysteria2 基于 UDP/QUIC,TCP MSS 修正帮不上忙。结果就是初始握手包或返回包在隧道路上被分片、丢弃或处理异常。
为了验证这个判断,我换用支持 QUIC 参数的新版本 sing-box 测试,并在 Hysteria2 outbound 中加入:
"initial_packet_size": 1200, "disable_path_mtu_discovery": true
再次测试后,两个原本失败的节点都恢复正常。抓包也显示初始 UDP payload 从 1280 降到了 1200,IPv4 包长度落在隧道 MTU 范围内,不再发生分片。
这次排查的关键结论是:节点本身没有坏,服务端也没有问题,真正的问题是本地网络出口的 IPv4-over-IPv6 隧道 MTU 太低,而 Hysteria2/QUIC 默认初始包过大,导致 UDP 分片在路径中出问题。
最终可行的解决方向有几个:
使用支持 initial_packet_size 的 sing-box 版本,并把值调到 1200 左右
尽量走原生 IPv6,绕开 IPv4-over-IPv6 隧道
换用 TCP 类协议,避免 UDP/QUIC 对路径 MTU 的敏感性
在路由器侧调整隧道 MTU
这类问题最迷惑的地方在于:同一个节点在别的网络下完全正常,因此很容易误判成客户端配置问题或服务端防火墙问题。
实际排查时,最好同时看三件事:外部网络能否连通、本地路由 MTU、以及本地抓包里 UDP 是否发生分片。