WSL2 无法访问 VPN 子网:读取宿主机路由
该发行版处在自己的 NAT 之后,永远看不到 Windows 隧道。从宿主机读取当前地址和前缀,再把它们加入 WSL。
在发行版内部,向内部服务器发起的 ping 会超时,而同一地址从 Windows 侧却能应答。VPN 已连接,Windows 路由也正确,因此缺口就在 WSL 边界。沿着从失败命令回到宿主机接口的路径逐段排查。修复办法不是写死配置里的 CIDR,而是读出当前地址,再把它加到发行版能用的地方。
确认路径停在哪里
在两侧执行同一测试:先从 Windows 解析并连接某台内部主机,再在发行版里重复一遍。Windows 成功而发行版超时,说明故障在两者之间,既不在 VPN 客户端,也不在内部服务器。要在改动任何路由之前做这件事,好让两次结果描述的是同一个目的地。
在发行版内部,ip -4 addr 显示一个带 WSL NAT 网段地址的虚拟适配器,ip route 显示指向 WSL 网关的默认路由。企业网段没有对应路由,因为宿主机上的隧道适配器在发行版里没有对应物。默认网关通向 WSL 的 NAT,而不是企业网络。
在可达链路上先不添加本地地址就添加企业路由,只会得到超时或网络不可达错误。让路由可用的关键就是地址,所以要先读地址,再把路由当作第二步。
选择镜像网络或脚本化路由
镜像网络把宿主机自己的接口交给发行版,隧道及其路由无需复制就会出现。该功能依赖较新的 Windows 版本以及 .wslconfig 中的 networkingMode=mirrored,开启后还会改变 localhost 转发等无关行为。要把它当作平台变更,而不是路由微调。如果版本支持且副作用可接受,这就是最短路径。
更窄的做法保留默认 NAT,并在隧道每次建立时把隧道地址和路由复制进发行版。它适用的版本更多,不改变 localhost 行为,而且 VPN 断开时会明确失败。代价是要在每次连接时运行一个小脚本。
选一种并写下来。把镜像配置和手工路由混在一起,会让发行版对同一前缀有两条路径,而且哪条都没有明确的负责人。一种有文档的做法比两套半吊子配置更容易排查。
从宿主机取出当前隧道信息
按 MAC 地址而不是名称匹配适配器,因为名称会随 VPN 客户端和连接顺序变化。先列一次适配器,记下隧道适配器的 MAC,再查询它的地址和前缀。MAC 在重连之间留在虚拟适配器上,名称和接口索引则不会:
powershell.exe 'Get-NetAdapter | Where-Object { $_.MacAddress -eq "3C-52-82-0A-1B-7C" } | Get-NetIPAddress -AddressFamily IPv4 | Select-Object IPAddress, PrefixLength'网关来自接口配置。先取接口索引,再读下一跳。索引可能在重启或适配器重置后变化,所以脚本应当读取而不是保存它:
powershell.exe '(Get-NetAdapter | Where-Object { $_.MacAddress -eq "3C-52-82-0A-1B-7C" }).InterfaceIndex'
powershell.exe '(Get-NetIPConfiguration -InterfaceIndex 27).IPv4DefaultGateway.NextHop'在赋给变量之前先用 tr -d '\r' 过滤输出。PowerShell 用回车符结束行,不去掉的话最后一个字段会带上隐藏字符,让路由命令以莫名其妙的解析错误失败。
在发行版内部应用地址和路由
把隧道地址作为带前缀的地址装到发行版接口上,然后让企业网段经宿主机网关转发:
sudo ip addr add 172.20.14.37/22 dev vpn
sudo ip route add 172.20.0.0/16 via 172.20.12.1 dev vpn连接一循环,NetworkManager 就会丢掉手工配置的地址,也没有 DHCP 客户端来要新地址。把方法设为自动,让每次重连都刷新地址,然后再把连接拉起来。手工方法在第一次重连之前看起来都很稳:
nmcli con show vpn
sudo nmcli con mod vpn ipv4.method auto
sudo nmcli con up vpn两步都要放在启动 VPN 的钩子里执行,而不是交互式 shell 里。读取地址、添加地址、添加路由并记录取值的脚本,能把反复出现的手工修复变成可重复的流程,而前缀一变,最先要看的就是那份日志。
固定接口名并验证路径
网络切换后,内核可能把发行版接口名在 eth1 和 eth2 之间改掉,按名字写死的脚本就会改错设备。按 MAC 绑定的 udev 规则能给接口一个稳定标签:
SUBSYSTEM=="net", ACTION=="add", ATTRS{address}=="3c:52:82:0a:1b:7c", NAME="vpn"然后用地址、路由和真实 TCP 连接来验证:ip -4 addr show dev vpn、ip route get 172.20.4.10,以及连到已知内部端口。路由查询应当报出 vpn 设备和添加的源地址。
如果三项在发行版里都通过而连接仍然失败,说明数据包已经离开 WSL 并在 Windows 侧被丢弃。改脚本之前先检查宿主机防火墙和 VPN 客户端自身的过滤,因为再改一次路由救不回已经到达宿主机的数据包。