WSL2 镜像网络模式下跨网段局域网不可达与 TUN 路由冲突排查

1. 故障现场与问题复现

1.1 运行环境与依赖版本

组件 / 环境版本 / 配置信息
宿主机操作系统Windows 11 23H2 / 24H2
子系统架构WSL2 (Linux 6.6.x 内核)
WSL 网络模式镜像模式 (networkingMode=mirrored)
宿主机代理工具Clash Verge Rev / Sing-box / Mihomo (启用 TUN 模式)
本地网络拓扑宿主机物理 IP 为 192.168.1.100/24,网关为 192.168.1.1

1.2 触发命令与错误堆栈

在 Windows PowerShell 或命令提示符中,访问同局域网另一子网下的目标主机(192.168.10.50)一切正常。但在 WSL2 终端中发起 ICMP 探测或 TCP 请求时,均无法连通:

1
ping -c 2 192.168.10.50

控制台立即返回如下 ICMP 路由不可达报错:

1
2
3
4
5
6
PING 192.168.10.50 (192.168.10.50) 56(84) bytes of data.
From 198.18.0.1 icmp_seq=1 Destination Host Unreachable
From 198.18.0.1 icmp_seq=2 Destination Host Unreachable

--- 192.168.10.50 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1129ms

2. 排查路径与根因深度推演

2.1 常见误区与无效尝试

  • 尝试 1:怀疑 Windows Defender 防火墙拦截。尝试关闭 Windows 公用与专用网络防火墙,但 WSL2 内部依然返回相同的 198.18.0.1 Destination Host Unreachable,报错并非超时(Request Timed Out),而是明确的网关拒绝。
  • 尝试 2:重启 WSL2 实例与网络堆栈。执行 wsl --shutdown 重启后,物理网络正常访问外网,但只要宿主机开启 TUN 模式,该跨网段局域网请求依然失败。

2.2 根本原因定位(Why & How)

核心根本原因简述:WSL2 镜像网络将 Windows 端代理的 TUN 虚拟网卡及大段路由同步至 Linux 内部,导致非直连物理子网的私网 IP 被 TUN 路由抢占并发送给代理核心,代理核心无法在远端解析该私网地址从而丢包。

1. 路由表规则命中分析

在 WSL2 内部执行 ip route 查看当前全局路由规则:

1
2
3
4
5
6
7
0.0.0.0/8 via 198.18.0.2 dev eth0 proto kernel metric 1
default via 192.168.1.1 dev eth2 proto kernel metric 25
...
192.0.0.0/2 via 198.18.0.2 dev eth0 proto kernel metric 1
192.168.1.0/24 dev eth2 proto kernel scope link metric 281
192.168.1.1 dev eth2 proto kernel scope link metric 25
198.18.0.0/30 dev eth0 proto kernel scope link metric 256

通过 ip route get 192.168.10.50 进行路由选路决策跟踪:

1
2
3
4
ip route get 192.168.10.50
# 输出:
192.168.10.50 via 198.18.0.2 dev eth0 src 198.18.0.1 uid 1000
cache

2. 数据流向机制推演

graph TD
    A["WSL2 发起请求<br/>目标: 192.168.10.50"] --> B{"查询内核路由表决策"}
    B -->|"优先匹配 192.0.0.0/2<br/>(Metric 1 极高优先级)"| C["出口: eth0 (TUN 网卡 198.18.0.1)"]
    B -.->|"未能命中 192.168.1.0/24<br/>(非本机直连物理网段)"| D["出口: eth2 (物理网卡 192.168.1.1)"]
    C --> E["代理核心 (Mihomo / Clash / Sing-box)"]
    E -->|"私网 IP 无法路由至远端节点"| F["直接回送 ICMP Unreachable 丢包"]
  1. 子网掩码范围差异:本机绑定的物理网卡 eth2 属于 192.168.1.0/24 子网,直连链路路由仅覆盖 192.168.1.0 ~ 192.168.1.255
  2. TUN 模式的路由劫持:代理软件(如 Mihomo / Sing-box)在 Windows 上接管全局流量时,为了覆盖私有地址外的所有公共流量,注入了 192.0.0.0/2 via 198.18.0.2(覆盖 192.0.0.0255.255.255.255),其跃点数(Metric)为 1。
  3. 选路优先级判定:目标 IP 192.168.10.50 不属于 192.168.1.0/24,因此匹配到了 Metric 极小的 192.0.0.0/2,流量被送往 eth0(TUN 虚拟网卡 198.18.0.1)。
  4. 代理核心拒绝:代理客户端的 TUN 栈接收到针对局域网私网 IP 的连接请求,无法通过上游代理节点转发,判定为不可达目标,直接回送 ICMP Unreachable 报文。

3. 终极解决方案与代码对照

3.1 核心修复方案

根据使用场景,可选择WSL 内部指定路由(快速生效)宿主机代理绕过(长期根治)

方案 A:在 WSL2 内部添加明细直连路由(即时生效)

在 WSL2 终端中,显式为目标私网网段添加一条精确路由,指定通过宿主机物理网卡网关发送:

1
sudo ip route add 192.168.10.0/24 via 192.168.1.1 dev eth2

参数说明

  • 192.168.10.0/24:需要访问的对端局域网网段。
  • 192.168.1.1:当前物理局域网网关(可通过 ip route show default 确认)。
  • eth2:WSL2 镜像网络中对应的物理网卡接口。

方案 B:在 Windows 代理软件中将内网网段加入 TUN 绕过(一劳永逸)

如果使用 Clash Verge Rev / Mihomo Party / Sing-box 等客户端,修改 TUN 配置中的绕过列表(Bypass CIDR):

1
2
3
4
5
6
7
8
9
10
11
tun:
enable: true
stack: mixed
auto-route: true
auto-redirect: true
auto-detect-interface: true
# 确保以下私网 CIDR 包含在绕过列表中
route-exclude-address:
- 192.168.0.0/16
- 10.0.0.0/8
- 172.16.0.0/12

修改后重启代理内核的 TUN 模式即可生效。

3.2 验证生效命令

在 WSL2 中重新查询选路状态并执行探测:

1
2
3
4
5
6
# 1. 验证路由决策
ip route get 192.168.10.50
# 预期输出:192.168.10.50 via 192.168.1.1 dev eth2 src 192.168.1.100

# 2. 验证连通性
ping -c 2 192.168.10.50

控制台输出正常连通响应:

1
2
3
4
5
6
7
PING 192.168.10.50 (192.168.10.50) 56(84) bytes of data.
64 bytes from 192.168.10.50: icmp_seq=1 ttl=63 time=2.15 ms
64 bytes from 192.168.10.50: icmp_seq=2 ttl=63 time=1.88 ms

--- 192.168.10.50 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.882/2.016/2.150/0.134 ms

4. 避坑防范与防御性工程总结

  1. WSL2 镜像网络特性认知:在 ~/.wslconfig 中开启 networkingMode=mirrored 后,WSL2 将完整继承 Windows 宿主机的网络命名空间与适配器。若 Windows 存在全局 VPN、TUN 驱动或虚拟网卡,Linux 内部也会同步感知其路由规则。
  2. 私网大网段规划建议:在多子网家庭或企业内网环境中,配置代理软件时应将 route-exclude-address 覆盖至整个 Class B/C 私网网段(如 192.168.0.0/16),避免跨子网时因精细掩码不匹配而落入代理大网段规则。
  3. WSL 启动自愈脚本:若需保证每次 WSL 重启后静态路由依然有效,可在 /etc/wsl.conf[boot] 段配置 command 执行路由注入脚本。

5. 参考链接与延伸阅读