WSL 2 loses all networking under Check Point VPN hub mode

· Valentin Mihai · about a 6 minute read

  • wsl-2
  • check-point-vpn
  • routing
  • windows
  • powershell
TL;DR

In hub mode the Check Point client pulls your locally attached networks into the tunnel — and it counts the private Windows-to-WSL link as one of them. It installs two routes, each covering half the WSL subnet, pointing at the VPN adapter. Windows prefers the more specific route, so a /21 beats WSL's /20, and every reply meant for the WSL virtual machine disappears into the tunnel.

The fix is to delete exactly those routes, which is what wsl-checkpoint-vpn-fix does.

the symptom

Connect the VPN and Windows carries on as if nothing happened. The browser loads pages, PowerShell reaches the internet, corporate resources resolve. Then you drop into WSL and everything is dead:

$ curl -sS https://www.google.com
curl: (28) Failed to connect to www.google.com port 443 after 9964 ms: Connection timed out

$ ping -c 2 8.8.8.8
2 packets transmitted, 0 received, 100% packet loss

apt hangs. git clone hangs. pip, npm, anything that opens a socket sits there until it gives up. Disconnect the VPN and it all comes back instantly. Reconnect and it breaks again. The correlation is perfect, which is the one genuinely helpful thing about this bug.

why it looks like DNS, and why it isn't

The instinct is DNS, and a great deal of the advice online will agree with you. It is the wrong trail, and there is a clean way to prove it in one command:

$ getent hosts example.com
93.184.216.34   example.com

Names resolve fine. WSL's DNS tunneling answers lookups through Windows, and Windows still has working DNS. So resolution is healthy and it is the connections that fail — which rules out the entire family of resolv.conf workarounds in one go.

The MTU theory dies almost as quickly. MTU problems are selective: small packets get through, large ones stall. Here nothing gets through at all, not even a bare ICMP echo. And in NAT mode WSL had already negotiated an MTU below the VPN adapter's 1350.

Both of those are worth ruling out explicitly, because both are the kind of fix that appears to work for unrelated reasons and then wastes an afternoon.

what is actually happening

WSL 2 runs inside a lightweight virtual machine. In NAT mode — the default — Windows talks to that VM over a private virtual network on the vEthernet (WSL) adapter, using a subnet picked at random on each restart, something like 172.23.160.0/20. Windows forwards the VM's traffic outward and routes the replies back over that subnet.

Hub mode, meanwhile, means all traffic goes through the tunnel. And unless the gateway explicitly enables exclude_local_networks_in_hub_mode — which is off by default — "all" includes the networks your machine is directly attached to. The client has no reason to think the WSL link is special. It is a local network, so in it goes.

Concretely, on connect the client:

  • adds two routes, each half the size of the WSL subnet — two /21s covering one /20 — pointing at the VPN adapter;
  • adds host routes on the VPN adapter for the Windows end of the link (172.23.160.1/32) and for the subnet's broadcast address, with a better metric than the originals.

And here is the whole bug in one sentence: Windows always prefers the most specific matching route, and a /21 is more specific than a /20.

Note what that does to the traffic. WSL's outbound packets still leave the machine — the VM's own routing is untouched. It is the return path that breaks, because replies addressed back into the WSL subnet now match a /21 aimed at the tunnel. They go in and never come out. From inside WSL this is indistinguishable from the internet having quietly stopped existing, which is exactly why the symptom is so misleading.

Splitting a subnet into two halves rather than matching it exactly is a neat trick, incidentally: it guarantees a more specific match without needing to know the original prefix length.

seeing it for yourself

The client writes its route operations to trac.log, in hex, as <destination, mask, next hop, interface index, metric>. Decoding one line is enough to confirm the diagnosis — ac17a000 is 172.23.160.0 and fffff800 is a /21:

[vna_rtm]  <ac17a000, fffff800, 0a000019, 13, 1>   # 172.23.160.0/21 -> VPN adapter
[vna_rtm] vnartm_perform_route_op: ADDED

The two settings behind the behaviour are in the same log: neo_route_all_traffic_through_gateway = true is hub mode, and exclude_local_networks_in_hub_mode = false is the part that drags your local networks along with it.

mirrored mode does not save you

The obvious escape hatch is networkingMode=mirrored, where WSL gets its own copy of the host's adapters — including the Check Point one, with the correct Office Mode address and routes. Its routing table is right.

It still doesn't work, and this one can't be fixed from the outside. Packets sent into that adapter go unanswered; in testing, hundreds went out and only ARP replies came back. The client's virtual adapter works by trapping traffic from Windows' own network stack, and mirrored-mode WSL hands it frames through the Hyper-V switch instead, where they are dropped. This is tracked upstream as microsoft/WSL#13426.

So NAT mode plus a route repair is not a workaround chosen over a cleaner option. It is the only layer left where you have any leverage.

the fix

Stay on NAT mode with DNS tunneling in %UserProfile%\.wslconfig:

[wsl2]
networkingMode=nat
dnsTunneling=true

Then, after the VPN connects, delete the offending routes. The script in the repo does this properly rather than with hardcoded addresses — it discovers the current WSL subnet (remember, it changes on every restart), removes only Check Point routes that fall entirely inside it, restores the host route for the Windows end of the link if it went missing, and re-checks 30 seconds later in case the client puts them back.

powershell -ExecutionPolicy Bypass -File .\src\Fix-WslCheckPointRoutes.ps1 -WhatIf

-WhatIf needs no admin rights and changes nothing, so it doubles as the diagnostic: if it lists two /21s inside your WSL subnet, this note is about your problem. If it reports no overlap, it isn't.

The routes come back on every connect, so there is also an installer that registers a scheduled task firing on Windows' "new network connected" event — which covers both connecting and reconnecting after sleep. Full instructions, limitations and security notes are in the repository README.

Two things worth being clear about, since this is VPN-adjacent: your WSL traffic still goes through the tunnel — the only thing being repaired is a private link that exists entirely inside your own PC — and the changes live in the active route table only, so they evaporate when the VPN disconnects or Windows restarts.

the fix you should prefer, if you can get it

Everything above is client-side work to undo a server-side decision. If you have any influence over the gateway, the real fix is to set exclude_local_networks_in_hub_mode to true or client_decide in trac_client_1.ttm and install policy. With client_decide, users get a checkbox and can make the call themselves.

Two honest caveats. Check Point's documentation doesn't say whether virtual adapters like vEthernet (WSL) count as local networks — the client log shows it treating the WSL subnet exactly like a physical LAN, so it very probably applies, but that is inference, not a documented guarantee. And if the network behind the VPN overlaps your home LAN's address range, excluding local networks will make those corporate addresses unreachable instead.

Which is the usual shape of this kind of problem: the clean fix needs someone else's cooperation, and the one you can actually deploy today is four routes and a scheduled task.

references

Tested on Windows 11 25H2 (build 26200), WSL 2.6.3, Ubuntu 22.04, Check Point Endpoint Security VPN build 98.61, gateway in hub mode with Office Mode. Unofficial and unaffiliated with Check Point or Microsoft.