In this blog post, I’m going to show you how to access (ping, ssh, etc.) to Multipass VMs running on Windows from WSL2.

On my Windows 10/11 computer, I have Multipass and WSL2 installed. They both rely on the same hypervisor (Hyper-V) but they DON’T use the same Virtual Network adapter (switch). I mean, when you start a WSL2-based instance, it’s connected to the vEthernet (WSL) adapter, while the Multipass VM is connected by default to the vEthernet (Default Switch) adapter. So they are in different networks, and those two NAT’d networks cannot talk to each other by default. This makes the communication impossible between the instances.

You can list the interfaces (switch) present on your computer via PowerShell:

PS C:\Users\kenneth> Get-NetIPInterface | where AddressFamily -eq "IPv4" | select ifIndex,InterfaceAlias,AddressFamily,ConnectionState,Forwarding | Sort-Object -Property IfIndex | Format-Table

ifIndex InterfaceAlias                AddressFamily ConnectionState Forwarding
------- --------------                ------------- --------------- ----------
      1 Loopback Pseudo-Interface 1            IPv4       Connected   Disabled
      7 Ethernet                               IPv4    Disconnected   Disabled
     10 Connexion au réseau local* 10          IPv4    Disconnected   Disabled
     16 Connexion au réseau local* 1           IPv4    Disconnected   Disabled
     19 Wi-Fi                                  IPv4       Connected   Disabled
     20 Connexion réseau Bluetooth             IPv4    Disconnected   Disabled
     25 vEthernet (Default Switch)             IPv4       Connected   Disabled
     47 vEthernet (WSL)                        IPv4       Connected   Disabled

TLDR - How to actually fix it?

The workaround is to enable forwarding on each interface (adapter).

To do so, open PowerShell console as administrator and run the commands below:

Set-NetIPInterface -ifAlias "vEthernet (WSL)" -Forwarding Enabled
Set-NetIPInterface -ifAlias "vEthernet (Default Switch)" -Forwarding Enabled

We can check if the change is successfully applied:

PS C:\Users\kenneth> Get-NetIPInterface | where AddressFamily -eq "IPv4" | select ifIndex,InterfaceAlias,AddressFamily,ConnectionState,Forwarding | Sort-Object -Property IfIndex | Format-Table

ifIndex InterfaceAlias                AddressFamily ConnectionState Forwarding
------- --------------                ------------- --------------- ----------
      1 Loopback Pseudo-Interface 1            IPv4       Connected   Disabled
      7 Ethernet                               IPv4    Disconnected   Disabled
     10 Connexion au réseau local* 10          IPv4    Disconnected   Disabled
     16 Connexion au réseau local* 1           IPv4    Disconnected   Disabled
     19 Wi-Fi                                  IPv4       Connected   Disabled
     20 Connexion réseau Bluetooth             IPv4    Disconnected   Disabled
     25 vEthernet (Default Switch)             IPv4       Connected    Enabled
     47 vEthernet (WSL)                        IPv4       Connected    Enabled

Now, the instances are able to communicate with each other. Unfortunately, those changes revert to default after a reboot.

Let’s make it persistent across reboot

We need to create a scheduled task via the Windows Task Scheduler, which executes our PowerShell script on WSL2 startup.

Write the script from the previous section into a file and choose a location on your disk to save it. For example, C:\EnableForwardingWSLHyperV\EnableForwarding.ps1.

  1. Open Task Scheduler by pressing the Windows key + R and typing taskschd.msc in the Run dialog box, then press Enter.
  2. In the Task Scheduler window, click on Create Task in the right-hand pane.
  3. In the General tab, give the task a name and select Run with highest privileges and Configure for to match your Windows version. Change the User or Group to System
  4. In the Triggers tab, click New to create a new trigger.
  5. In the New Trigger window, select On an event from the drop-down menu next to Begin the task. Select System as Log, Hyper-V-VmSwitch as Source, and type 9 for the Event ID. Click on Enabled and OK to save.
  6. In the Actions tab, click New to create a new action.
  7. In the New Action window, select Start a program from the drop-down menu next to Action. Type powershell.exe in Program/script field. Type -ExecutionPolicy Bypass C:\EnableForwardingWSLHyperV\EnableForwarding.ps1 in the Add arguments field where C:\EnableForwardingWSLHyperV\EnableForwarding.ps1 is the full path to your script file.
  8. Click OK to save the action and return to the Create Task window.
  9. Make the conditions as below. Then click OK to save the task and exit Task Scheduler.

Read on if you use Docker

This is where things become interesting.

I have Docker installed in my Ubuntu WSL2 instance. Who says docker, also means docker networks. Docker uses by default the following address ranges for his (bridge) networks:

  • 172.16.0.0 to 172.31.255.255
  • 192.168.0.1 to 192.168.255.254

So when a docker container is created, it gets an IP address from the ranges above. You can run ip route list to view the subnets used by docker networks in your WSL instance and the interfaces mapped to each one.

kenneth@WSL2-instance:~$ ip route list
default via 172.25.16.1 dev eth0 proto kernel
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-71e0056edb0c proto kernel scope link src 172.18.0.1 linkdown
172.20.0.0/16 dev br-53060e182191 proto kernel scope link src 172.20.0.1
172.21.0.0/16 dev br-4b041d33c6fc proto kernel scope link src 172.21.0.1
172.23.0.0/16 dev br-e590ce2dd094 proto kernel scope link src 172.23.0.1
172.24.0.0/16 dev br-ccd815cbc749 proto kernel scope link src 172.24.0.1
172.25.16.0/20 dev eth0 proto kernel scope link src 172.25.26.109
172.27.0.0/16 dev br-aef2725120e1 proto kernel scope link src 172.27.0.1
172.30.0.0/16 dev br-ebb8959b6203 proto kernel scope link src 172.30.0.1 linkdown
172.31.0.0/16 dev br-694e7fb98934 proto kernel scope link src 172.31.0.1
192.168.0.0/20 dev br-302f21741602 proto kernel scope link src 192.168.0.1 linkdown
192.168.16.0/20 dev br-caac72d20eef proto kernel scope link src 192.168.16.1

Then guess what?

The default network adapter vEthernet (Default Switch) used by Multipass VMs also use the same IP address range. When I try to ping my multipass VM from WSL2, there is a conflict. The traffic is routed to the docker interface (docker0, br-X) instead of the vEthernet (Default Switch) interface.

The workaround from the previous section is no more enough.

How to fix that ?

We have to change the IP address ranges used by docker networks or either the one used by the vEthernet (Default Switch) interface.

At the date of this write-up, there is no official documented method to set the vEthernet (Default Switch) adapter’s IP address range. However, it can be achieved through some “hacky” PowerShell script which should be executed at every boot of your Windows computer.

The second option is to change the default docker network IP address ranges. This method is more elegant and is officially supported. So let’s go that way.

From your WSL2 instance terminal, edit the /etc/docker/daemon.json file with the content below. (if the file doesn’t exist, create it)

{
  "bip": "10.54.54.1/24",
  "default-address-pools": [
    {
      "base": "10.55.0.0/16",
      "size": 26
    }
  ]
}

where:

  • bip is the IP address and netmask to use for Docker’s default bridge using standard CIDR notation.
  • default-address-pools specify pools used for creating new networks. This is needed to configure new networks created by Docker Compose.
    • base specifies the CIDR range to use
    • size defines the prefix/netmask for a given subnet allocated within the base pool above

Then restart the docker service for the changes to take effect

 sudo service docker restart

Now all your future containers will automatically get IP address from that pool:

  • 10.54.54.0/24
  • 10.55.0.0/16

Note: Unfortunately, the existing docker networks subnets won’t be changed. You will have to destroy and recreate them in order to update to the new IP range.

Tips to execute multipass commands from WSL2

To execute multipass commands from a WSL2-based instance, you need to create a symbolic link:

sudo ln -s '/mnt/c/Program Files/Multipass/bin/multipass.exe' /usr/bin/multipass

Where /mnt/c/Program Files/Multipass/bin/multipass.exe is your multipass installation path on the Windows host.

References