Skip to content

Latest commit

 

History

History
164 lines (124 loc) · 9.81 KB

ROUTING.md

File metadata and controls

164 lines (124 loc) · 9.81 KB

Fixing Route Table for WSL2

This guide applies to Cisco AnyConnect in environments where it has been configured to block split-tunneling.

Step #1 - WSL2 Internet Access

First you need to ensure your WSL2 distributions can access the internet. Before connecting to the VPN your routes for WSL2 will look something like (using the Get-NetRoute command in powershell):

ifIndex DestinationPrefix                              NextHop                                  RouteMetric ifMetric PolicyStore
------- -----------------                              -------                                  ----------- -------- -----------
23      172.24.31.255/32                               0.0.0.0                                          256 5000     ActiveStore
23      172.24.16.1/32                                 0.0.0.0                                          256 5000     ActiveStore
23      172.24.16.0/20                                 0.0.0.0                                          256 5000     ActiveStore

But when you connect to the VPN, AnyConnect adds a non-functional route with a lower metric:

23      172.24.31.255/32                               0.0.0.0                                          256 5000     ActiveStore
23      172.24.16.1/32                                 0.0.0.0                                          256 5000     ActiveStore
4       172.24.16.0/20                                 10.74.112.1                                        1 1        ActiveStore
23      172.24.16.0/20                                 0.0.0.0                                          256 5000     ActiveStore

Unfortunately we cannot remove or modify this route because it will be automatically replaced by AnyConnect. However, Windows determines the best route by the lowest sum of interface metric + route metric. What we can do is increase the AnyConnect interface metric:

Get-NetAdapter | Where-Object {$_.InterfaceDescription -Match "Cisco AnyConnect"} | Set-NetIPInterface -InterfaceMetric 6000

Now the route table will allow WSL2's NAT connection to the Internet, because 5256 is a lower metric than 6001:

23      172.24.31.255/32                               0.0.0.0                                          256 5000     ActiveStore
23      172.24.16.1/32                                 0.0.0.0                                          256 5000     ActiveStore
4       172.24.16.0/20                                 10.74.112.1                                        1 6000     ActiveStore
23      172.24.16.0/20                                 0.0.0.0                                          256 5000     ActiveStore

Step #2 - Automation

The AnyConnect metric will unfortunately be reset every time the VPN is started, so we need to automate this fix with task scheduler. Save the above powershell command as setCiscoVpnMetric.ps1

Open task scheduler and click "Create task":

  • Name: "Update AnyConnect Adapter Interface Metric for WSL2"
  • Security options: Check "Run with highest privileges"
  • Triggers:
    • On an Event, Log: Cisco Secure Client - AnyConnect VPN, Source: csc_vpnagent, Event ID: 2039
    • On an Event, Log: Cisco Secure Client - AnyConnect VPN, Source: csc_vpnagent, Event ID: 2041
  • Actions: Start a program, Program/script: powershell.exe, Add arguments: -WindowStyle Hidden -NonInteractive -ExecutionPolicy Bypass -File %HOMEPATH%\Documents\setCiscoVpnMetric.ps1
  • Conditions: Uncheck "Start the task only if the computer is on AC power"

Step #3 - Working Windows DNS

The above fix then leads to a problem for the Windows host - the interface metric is also used by Windows to determine the ordering of DNS servers. As a result the VPN adapter now has the lowest priority DNS:

PS C:\Users\jdhalsey> Get-NetIPInterface | Where-Object {$_.ConnectionState -eq 'Connected'} | Sort-Object -Property InterfaceMetric

ifIndex InterfaceAlias                  AddressFamily NlMtu(Bytes) InterfaceMetric Dhcp     ConnectionState PolicyStore
------- --------------                  ------------- ------------ --------------- ----     --------------- -----------
23      vEthernet (WSL)                 IPv4                  1500              15 Disabled Connected       ActiveStore
23      vEthernet (WSL)                 IPv6                  1500              15 Enabled  Connected       ActiveStore
13      Ethernet                        IPv4                  1500              25 Enabled  Connected       ActiveStore
13      Ethernet                        IPv6                  1492              25 Enabled  Connected       ActiveStore
17      Wi-Fi                           IPv6                  1492              50 Enabled  Connected       ActiveStore
17      Wi-Fi                           IPv4                  1500              50 Enabled  Connected       ActiveStore
1       Loopback Pseudo-Interface 1     IPv4            4294967295              75 Disabled Connected       ActiveStore
1       Loopback Pseudo-Interface 1     IPv6            4294967295              75 Disabled Connected       ActiveStore
4       Ethernet 2                      IPv6                  1300            6000 Enabled  Connected       ActiveStore
4       Ethernet 2                      IPv4                  1300            6000 Disabled Connected       ActiveStore

Windows will try the IPv6 and IPv4 DNS servers on both your Ethernet and Wi-Fi first, even though they are unreachable due to the VPN. Depending on how many servers are configured this can add long delays to DNS resolution, for example:

You can verify this by running nslookup.exe google.com, you should see that it attempts to use the first DNS server of whichever interface has the lowest metric, in this case your Ethernet adapter, and therefore will fail when connected to the AnyConnect VPN.

You therefore need to update the interface metrics on your Ethernet and Wi-Fi adapters to be greater than the VPN, for both IPv4 and IPv6. You can use:

Set-NetIPInterface -InterfaceAlias "Ethernet" -InterfaceMetric 6025
Set-NetIPInterface -InterfaceAlias "Wi-Fi" -InterfaceMetric 6050

(Or alternatively: Control Panel -> Network and Sharing Center -> Change adapter settings -> Ethernet Properties -> Internet Protocol Version 4/6 -> Advanced -> Interface Metric)

The interface ordering should now look like:

PS C:\Users\jdhalsey> Get-NetIPInterface | Where-Object {$_.ConnectionState -eq 'Connected'} | Sort-Object -Property InterfaceMetric

ifIndex InterfaceAlias                  AddressFamily NlMtu(Bytes) InterfaceMetric Dhcp     ConnectionState PolicyStore
------- --------------                  ------------- ------------ --------------- ----     --------------- -----------
23      vEthernet (WSL)                 IPv6                  1500              15 Enabled  Connected       ActiveStore
23      vEthernet (WSL)                 IPv4                  1500              15 Disabled Connected       ActiveStore
1       Loopback Pseudo-Interface 1     IPv4            4294967295              75 Disabled Connected       ActiveStore
1       Loopback Pseudo-Interface 1     IPv6            4294967295              75 Disabled Connected       ActiveStore
4       Ethernet 2                      IPv4                  1300            6000 Disabled Connected       ActiveStore
4       Ethernet 2                      IPv6                  1300            6000 Enabled  Connected       ActiveStore
13      Ethernet                        IPv6                  1492            6025 Enabled  Connected       ActiveStore
13      Ethernet                        IPv4                  1500            6025 Enabled  Connected       ActiveStore
17      Wi-Fi                           IPv6                  1492            6050 Enabled  Connected       ActiveStore
17      Wi-Fi                           IPv4                  1500            6050 Enabled  Connected       ActiveStore

You can verify this is working by running nslookup.exe google.com, you should see a successful request to the address of the VPN DNS server.

WSL2 <-> Host Communication

In step #1 we modified the route metrics such that WSL should in theory now be reachable:

PS C:\Users\jdhalsey> Find-NetRoute -RemoteIPAddress "172.24.18.66" | select InterfaceAlias

InterfaceAlias
--------------
vEthernet (WSL)
vEthernet (WSL)

Yet if we launch an HTTP server in WSl2:

python3 -m http.server 8000

And attempt to connect to it whilst AnyConnect is running, under certain AnyConnect setups this may fail:

PS C:\Users\jdhalsey> curl.exe http://172.24.18.66:8000
curl: (7) Failed to connect to 172.24.18.66 port 8000 after 0 ms: Bad access

The reason for this is that AnyConnect is blocking communication at the firewall level, unfortunately this is implemented using a Windows Filtering Platform kernel level driver, so it cannot easily be disabled.

Fortunately if you are on WSL 18970 or newer you can use the localhost relay to work around this:

PS C:\Users\jdhalsey> curl.exe http://localhost:8000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

Your VPN administrator should however be able to fully fix this using the AnyConnect's BypassVirtualSubnetsOnlyV4 options, see Connectivity Issues with VM-based Subsystems.