克隆完成了认证,对象计数出现,随后几分钟毫无动静。Windows 报告 VPN 已连接,主机名能够解析,同一台机器上的浏览器也能毫无困难地打开同一个仓库页面。这种组合指向包大小而非凭据,而大小问题必须在两处提问,因为 WSL2 有自己的 Linux 接口和自己的 MTU。一个数字属于路径的 Windows 一侧,另一个属于发行版,只看 VPN 适配器等于漏掉了一半路径。

三个信号把大小问题与其他原因区分开

第一个信号是形状:连接建立和少量交换成功,随后大流量传输不再前进。第二个信号是对称性:同一个仓库、同一种传输失败,而同一路径上的另一台主机正常,或者故障随载荷大小而非时段变化。第三个信号是沉默:没有认证错误,没有代理拒绝,没有名称解析失败,只有一次永远完不成的传输。

这三点合起来足以支持包大小假设。DNS 失败、令牌过期、SSH 密钥被拒、代理拒绝 CONNECT,各自都会产生自己的错误,没有一种看起来像成功之后的停滞。测试时保持同一仓库、同一传输和同一时间窗,比较才诚实。

先比较两侧接口,再下结论

在同一次会话里收集 Windows 侧和 Linux 侧的视图,并且以仓库地址为准,而不是猜哪块接口承载它。下面的命令先解析目标地址,这样路由查询描述的是克隆实际连接的服务器,它可能与浏览器显示的域名不同。两半要紧接着运行,因为 VPN 可能在两次尝试之间改变接口度量。

netsh interface ipv4 show subinterfaces
wsl -d Ubuntu -- bash -lc 'dest=$(getent ahostsv4 github.com | awk \'NR==1{print $1}\'); ip -br link show; ip route get "$dest"'

Linux 侧要在发行版内部运行,而不是把输出粘到 Windows shell 里,因为接口名和路由选择只在那里才有意义。从 Windows shell 粘贴出来的结果会非常自信地报告错误的数字。改动任何东西之前,先记录两侧的设备、选中的路由和 MTU。

判断这个数字是否应该更小

接口 MTU 大于路径实际能承载的值并不自动是故障,因为路径 MTU 发现机制正是为此存在。故障模式出现在发现被压制时:ICMP 消息在途中某处被过滤,发送方始终学不到上限,于是大包消失、小包通过。这是路径的属性,不是 Git 的属性。这也解释了为什么小文件能在连接上成功,而大文件永远传不完。

如果证据支持,临时修改 Linux 接口是最便宜的测试。选一个低于可用路径大小的值,记下原值,并保持其他一切不变,比较才有意义。如果传输在小值下完成,你得到的是诊断,而不是巧合。

ip -br link show dev eth0
sudo ip link set dev eth0 mtu 1380
ip -br link show dev eth0

决定这个设置应该放在哪里

测试之后有三种选择,通常只有一种合适。如果较小的值修好了传输,而且这个值对本网络确实必要,设置就需要一个生命周期归属:发行版的启动钩子、由平台管理的网络配置,或者成文的流程。如果这个值什么都修不好,就回退,并回到传输、代理和认证的问题上。

是否存在启动钩子,取决于所用的 Windows 与 WSL 版本以及配置的网络模式。镜像 Windows 网络 会改变 VPN 与发行版的交互方式,其影响可能比调整 MTU 更大,因此在为它设计变通方案之前,先确认模式。适合某个运营商或某个 VPN 配置文件的值,不是下一个的默认值。

要避免的是那种诱人的放置:把特权网络命令放进每个交互式 shell 的启动文件,或者为了让脚本执行一行而给整个 ip 二进制配置免密 sudo。两者都把一次诊断变成长期暴露,并让原始问题以后更难被发现。

干净重启之后再验证,然后回退

只在发行版重启之前有效的改动不算修复。重启发行版,重新连接 VPN,并重复原来的 Git 操作,而不是用 ping 或一个小 HTTP 请求代替。然后确认新会话中接口确实带着预期的值。

ip -br link show dev eth0
ip route get "$(getent ahostsv4 github.com | awk 'NR==1{print $1}')"

把那记下的原值放在改动旁边,以便干净地撤销实验;并记录测试的是哪个地址族,因为 IPv4 的结果说明不了同一主机上的 IPv6 连接。WSL 网络文档 描述了决定修复应为何种形态的几种模式;MTU 数字本身是本地测量,不是推荐值。

相关文章