diff --git a/WSL/media/network-mirrored-mode1.png b/WSL/media/network-mirrored-mode1.png new file mode 100644 index 00000000..0c77739b Binary files /dev/null and b/WSL/media/network-mirrored-mode1.png differ diff --git a/WSL/media/network-mirrored-mode2.png b/WSL/media/network-mirrored-mode2.png new file mode 100644 index 00000000..9dc4d600 Binary files /dev/null and b/WSL/media/network-mirrored-mode2.png differ diff --git a/WSL/media/network-nat-mode.png b/WSL/media/network-nat-mode.png new file mode 100644 index 00000000..b0ff4ade Binary files /dev/null and b/WSL/media/network-nat-mode.png differ diff --git a/WSL/networking.md b/WSL/networking.md index 37ac8c4a..f039f200 100644 --- a/WSL/networking.md +++ b/WSL/networking.md @@ -6,133 +6,249 @@ ms.date: 07/16/2024 ms.topic: article --- -# Accessing network applications with WSL +# WSL networking behavior and configuration -There are a few considerations to be aware of when working with networking apps and WSL. By default WSL uses a [NAT based architecture](#default-networking-mode-nat), and we recommend trying the new [Mirrored networking mode](#mirrored-mode-networking) to get the latest features and improvements. +## The big picture -### Identify IP address +A running WSL instance is like a virtual machine(VM) running inside a Windows machine. Let's consider this Windows machine(abbreviated as WinHost in this article) a real-world physical machine who resides in a physical network(or say, physical LAN). So the PCs/Servers running on the physical network appear to WSL instances as "outside world". -There are two scenarios to consider when identifying the IP address used for a Linux distribution running via WSL: +There are substantially three roles in this WSL world: WinHost, WSL VMs, and outside world machines. Then what's the networking software behavior among these three roles? That is not a question which can be answered in one simple sentence. We need some elaboration here. -**Scenario One:** From the perspective of the Windows host, you want to query a Linux distribution's IP address running via WSL2, so that a program on Windows host can connect to a server program running inside the distribution (instance). +A WSL instance can operate in three flavors, and there are two WSL networking mode involved. -The Windows host can use command: +| WSL flavor | WSL1 | WSL2 in NAT mode | WSL2 in mirrored mode | +| -------- | ---- | ------------------- | -------------------------| +| Networking behavior of a WSL instance | Shares WinHost's IP address(es).

Looks like it has network cable directly attached to the physical LAN. | Attached to an internal virtual LAN inside WinHost.

Has a dynamically allocated internal IP, which is different than WinHost's own IP. | Behaves the same as WSL1. | + +So we call the two modes "mirrored mode" and "NAT mode". Illustration helps highlight there difference. + +### WSL instances running in mirrored mode + +Figure 1: A WinHost running two WSL instances in mirrored mode, with one NIC(network interface card). + +![Networking mirrored mode one NIC](media/network-mirrored-mode1.png) + +Figure 2: A WinHost running two WSL instances in mirrored mode, with two NICs: + +![Networking mirrored mode two NICs](media/network-mirrored-mode2.png) + +Key characteristics: In mirrored mode, a WSL instance, although as a self-contained VM, does not have its own dedicated IP address. Windows WSL engine magically shares physical NIC's IP address(es) among all mirrored-mode-enabled WSL VMs. +- If WinHost has one physical NIC configured with IP 10.2.3.1, then the WSL instance accordingly sees itself equipped with an NIC having IP 10.2.3.1 . +- If WinHost has two physical NICs with IP 10.2.3.1 and 192.168.3.1 respectively, then the WSL instance also sees two NICs with IP 10.2.3.1 and 192.168.3.1 respectively. + + +### WSL instances running in NAT mode + + +Figure 3: A WinHost running two WSL instances both in NAT mode + +![Networking NAT mode](media/network-nat-mode.png) + + +We see that: + +- WinHost is equipped with one physical NIC (blue) connecting to all machines on the physical LAN. +- WinHost is also equipped with one virtual NIC(pink) connecting to all running WSL instances. + + +Each WSL instance(actually a Hyper-V virtual machine) has its own virtual NIC and its own IP address in the virtual LAN. This IP address(172.28.240.53 for example) is automatically allocated by WSL engine. + +Key characteristics: The physical LAN(10.2.X.X) and the virtual LAN(172.28.X.X) are two different LAN segments, so IP packets are not directly reachable from each other. The Windows operating system provides some facilities to make them finally reachable. +- **From WSL instance to outside world:** WSL engine's NAT code is always ready, so that the WSL instance(172.28.240.53 for example) can connect to a outside world TCP/UDP port(10.2.3.51's TCP port 80 for example) without extra work from the application code. A little drawback is that, the server side machine(10.2.3.51) only see the TCP/UDP connection is from WinHost's IP, which is due to NAT(Network Address Translation) protocol's working nature. +- **From outside world to WSL instance:** The whole virtual LAN is actually hidden from the outside world, so, if the client PC 10.2.3.52 wants to connect to a network server program inside WSL2 VM, WinHost has to relay that connection. This relay action is not the default behavior of the Windows OS, so user has to configure it manually. This is described in later section **"NAT mode: WSL as server, physical LAN machines as client"**. + + + +## How to determine current networking mode for a WSL instance? + +First, you should determine whether a WSL instance is running as WSL1 or WSL2. + +If it is WSL1, that instance is using mirrored mode. + +Second, for those WSL2 instances, they either all run in NAT mode, or all run in mirrored mode, no mixing allowed. + +On WinHost, the command `wsl -l -v` shows WSL operating version(WSL1 or WSL2), on the right-most `VERSION` column, for each instance. Output is something like this: ``` -wsl -d hostname -I + NAME STATE VERSION + Ubuntu-22.04 Stopped 1 +* WSL2-Ubuntu-24.04 Stopped 2 + WSL1-Ubuntu-20.04 Stopped 1 + openSUSE-Leap-15.6 Stopped 2 + docker-desktop Stopped 2 + docker-desktop-data Stopped 2 ``` -If querying the default distribution, this part of the command designating the distribution can be omitted: `-d `. Be sure to use a capital `-I` flag and not a lower-case `-i`. +For a WSL2 instance, you run query command `wslinfo --networking-mode` inside that Linux instance. There can be three results: +- It prints `nat`, then it is in NAT mode. +- It prints `mirrored`, then it is in mirrored mode. +- It blames `wslinfo` command not found, or some other wacky output info, that means your WinHost's WSL software is now new enough to support mirrored mode. + +### How to enable WSL2 mirrored mode? Under the hood, host command `wsl.exe` launches the target instance and executes Linux command `hostname -I` (short notation for `--all-ip-addresses`). This command then prints the IP address of the WSL instance to `STDOUT`. The `STDOUT` text content is then relayed back to wsl.exe. Finally, wsl.exe displays that output to the command line. -A typical output might be: +WSL2 mirrored mode is a relatively new feature which requires at least Windows 11 22H2, and, `wsl --update` should be run to update WSL engine to the latest version. -```powershell -172.30.98.229 +Besides, you should enable WSL mode explicitly, by editing `%UserProfile%\.wslconfig`. Add an extra line to `[wsl2]` section, like this: + +``` +[wsl2] +networkingMode=mirrored ``` -**Scenario Two:** A program running inside a Linux distribution via WSL2 (instance) wants to know the Windows host's IP address, so that a Linux program can connect to a Windows host server program. +More about [`.wslconfig` file](./wsl-config.md#configuration-settings-for-wslconfig). -The WSL2 Linux user can use command: -```bash -ip route show | grep -i default | awk '{ print $3}' -``` +## WSL Networking behavior + +This section is all about two questions: +1. If I run a network server program on WinHost, how can it be reachable by a client program from a WSL instance? +2. If I run a network server program inside a WSL instance, how can it be reachable by a client program from WinHost, or even from outside world(physical LAN client)? + +### Mirrored mode + +Mirrored mode is straightforward with least hassle, so we talk about it first. + +From TCP/IP networking perspective, a server or client program running in a WSL instance is just like running with WinHost's own IP address(es), so every machine on the LAN is directly reachable by the WSL instance, and vice versa. -A typical output might be: +For example with Figure 2 above, a server program in L1 WSL instance has two IPs(10.2.3.1 and 192.168.3.1), so +- R2(10.2.3.52) can connect to L1 via destination IP 10.2.3.1 , and +- R4(192.168.3.52) can connect to L1 via destination IP 192.168.3.1 . + +—no IP routing or NAT procedure involved, very straightforward. + +Of course, L1's client program can connect to L2's server program via destination IP 127.0.0.1 . + + +### NAT mode: WinHost as server, WSL as client + +The key point here is: WSL client program needs to know WinHost's IP address on virtual LAN segment. + +Take Figure 3 above for example, WinHost runs a server program and L3 (172.28.240.53) wants to connect, then L3 has to take 172.28.240.1 as server's IP address. + +Then how does L3 know that exact server IP address? That IP changes each time after WinHost reboots, so it cannot be a constant pre-configured for L3. The canonical way provided by Microsoft is, executing this command inside WSL: + +ip route show | grep -i default | awk '{ print $3}' + +In our example, the output will be a IP string `172.28.240.1` . + +The rationale is: A NAT mode WSL instance will always have WinHost's virtual LAN IP(172.28.240.1 in this example) as default gateway. And, `ip route show` will output something like: ``` -172.30.96.1 +default via 172.28.240.1 dev eth0 proto kernel +172.28.240.0/20 dev eth0 proto kernel scope link src 172.28.240.53 ``` -So the `172.30.96.1` is the host IP address for Windows, in this example. +The "default" line, column 3, shows the default gateway IP, so it is the right server IP we need. > [!NOTE] -> These above IP address querying action is typically required when WSL2 is running with the default [NAT network mode](#default-networking-mode-nat). -> When the WSL2 is running with the new [mirrored mode](#mirrored-mode-networking), the Windows host and WSL2 VM can connect to each other using `localhost` (127.0.0.1) as the destination address, so the trick of using a query peer's IP address is not required. +> The WinHost server program should listen on IP address `0.0.0.0` (instead of `127.0.0.1` or `localhost`) in order to accept connection from WSL, because in the eye of WinHost, WSL instance L1 and LAN client R1 are both considered remote clients. -## Default networking mode: NAT -By default, WSL uses a NAT (Network Address Translation) based architecture for networking. Keep the following considerations in mind when working with a NAT-based networking architecture: +### NAT mode: WSL as server, WinHost as client -### Accessing Linux networking apps from Windows (localhost) - -If you are building a networking app (for example an app running on a NodeJS or SQL server) in your Linux distribution, you can access it from a Windows app (like your Edge or Chrome internet browser) using `localhost` (just like you normally would). +From Figure 3 above, we see that WSL instance L3 has IP 172.28.240.53 . -### Accessing Windows networking apps from Linux (host IP) +A WinHost client program can reach a WSL server program(L3 for example), by assigning 172.28.240.53 as destination IP, which is no surprise. -If you want to access a networking app running on Windows (for example an app running on a NodeJS or SQL server) from your Linux distribution (ie Ubuntu), then you need to use the IP address of your host machine. While this is not a common scenario, you can follow these steps to make it work. +But, WSL engine provides a facility feature for this scenario. If a WSL server program listens on TCP port N, then, WinHost client program can connect to `localhost` port N to reach it. +For example, L3 has a TCP server program listening on `0.0.0.0:4000` or even on `localhost:4000`, then, WinHost can connect to `localhost:4000` to reach the server. This "proxy listening port" feature makes it as if the WSL server program is listening on WinHost itself. +======= 1. Obtain the IP address of your host machine by running this command from your Linux distribution: ```bash ip route show | grep -i default | awk '{ print $3}'` ``` 2. Connect to any Windows server using the copied IP address. -The picture below shows an example of this by connecting to a Node.js server running in Windows via curl. +> [!NOTE] +> The "proxy listening port" feature works only for TCP, not for UDP. (Microsoft staff please confirm this.) + + +### NAT mode: WSL as server, physical LAN machines as client -![Connect to NodeJS server in Windows via Curl](media/wsl2-network-l2w.png) +The challenge here is, a WSL instance's IP address is hidden from physical LAN, so WinHost has to do port-forwarding so that the WSL's IP address can be indirectly reachable from the outside world. -### Connecting via remote IP addresses +Here's an example of using the [Netsh interface portproxy](/windows-server/networking/technologies/netsh/netsh-interface-portproxy) Windows command to add a port proxy that listens on your host port and forward the connection request to the IP address and port for the WSL2 VM. -When using remote IP addresses to connect to your applications, they will be treated as connections from the Local Area Network (LAN). This means that you will need to make sure your application can accept LAN connections. +``` +netsh interface portproxy add v4tov4 listenport=14000 listenaddress=0.0.0.0 connectport=4000 connectaddress=172.28.240.53 +``` -For example, you may need to bind your application to `0.0.0.0` instead of `127.0.0.1`. In the example of a Python app using Flask, this can be done with the command: `app.run(host='0.0.0.0')`. Keep security in mind when making these changes as this will allow connections from your LAN. +In the above example, we tell WinHost to listen on port 14000 and accept connections from any remote machine. Once connected, the connection is redirected to 172.28.240.53:4000 . So, a server program running inside L3 listening on port 4000 can now actually accept connection from any remote machine. -### Accessing a WSL 2 distribution from your local area network (LAN) +In normal case, we assign the same value for `listenport` and `connectport`, but to demonstrate the concept clearly, we use different values in this example. -When using a WSL 1 distribution, if your computer was set up to be accessed by your LAN, then applications run in WSL could be accessed on your LAN as well. -This isn't the default case in WSL 2. WSL 2 has a virtualized ethernet adapter with its own unique IP address. Currently, to enable this workflow you will need to go through the same steps as you would for a regular virtual machine. (We are looking into ways to improve this experience.) +**Here we have an important point to mention.** A WSL instance gets its IP address differently across each WinHost reboot(WSL engine allocates the IP dynamically). If the system administrator of WinHost determines to write a startup script (e.g. `wsl-portforward.bat`) to apply the above `netsh interface portproxy ...` command, then how does he determine the `connectaddress`'s value? -Here's an example of using the [Netsh interface portproxy](/windows-server/networking/technologies/netsh/netsh-interface-portproxy) Windows command to add a port proxy that listens on your host port and connects that port proxy to the IP address for the WSL 2 VM. +OK. There is a way. We can use a WinHost command to query L1's IP address as long as we know L1's "WSL name"(we have called it "distribution name" or "instance name"). + +To list all WSL names on WinHost, we execute `wsl -l` . The output is like this: -```powershell -netsh interface portproxy add v4tov4 listenport= listenaddress=0.0.0.0 connectport= connectaddress=(wsl hostname -I) +``` +Windows Subsystem for Linux Distributions: +Ubuntu-22.04 (Default) +Ubuntu-24.04 ``` -In this example, you will need to update `` to a port number, for example `listenport=4000`. `listenaddress=0.0.0.0` means that incoming requests will be accepted from ANY IP address. The Listen Address specifies the IPv4 address for which to listen and can be changed to values that include: IP address, computer NetBIOS name, or computer DNS name. If an address isn't specified, the default is the local computer. You need to update the `` value to a port number where you want WSL to connect, for example `connectport=4000`. Lastly, the `connectaddress` value needs to be the IP address of your Linux distribution installed via WSL 2 (the WSL 2 VM address), which can be found by entering the command: `wsl.exe hostname -I`. +Different WSL names identify different WSL instances registered on this WinHost. A WSL name is determined upon its registration on the system and it will not change across WinHost's reboot. -So this command may look something like: +Assume that WinHost's script needs to query Ubuntu-22.04's current IP address, it should launch the following command and grab its STDOUT output: -```powershell -netsh interface portproxy add v4tov4 listenport=4000 listenaddress=0.0.0.0 connectport=4000 connectaddress=192.168.101.100 +```cmd +wsl.exe -d Ubuntu-22.04 hostname -I ``` -To obtain the IP address, use: +That output string is the IP address of the very WSL instance. -- `wsl hostname -I` for the IP address of your Linux distribution installed via WSL 2 (the WSL 2 VM address) -- `cat /etc/resolv.conf` for the IP address of the Windows machine as seen from WSL 2 (the WSL 2 VM) +Under the hood, that command launches WSL instance named `Ubuntu-22.04` right away, and tells the instance to execute command `hostname -I` (which prints WSL instance's own IP to STDOUT) then the STDOUT text content is relayed back to wsl.exe, and wsl.exe finally prints that out to command-line terminal. -Using `listenaddress=0.0.0.0` will listen on all [IPv4 ports](https://stackoverflow.com/questions/9987409/want-to-know-what-is-ipv4-and-ipv6#:~:text=The%20basic%20difference%20is%20the,whereas%20IPv6%20has%20128%20bits.). +Now, our WinHost script can uses this IP string as `connectaddress`'s value. Problem solved. -> [!NOTE] -> Using a lowercase "i" with the hostname command will generate a different result than using an uppercase "I". `wsl hostname -i` is your local machine (127.0.1.1 is a placeholder diagnostic address), whereas `wsl hostname -I` will return your local machine's IP address as seen by other machines and should be used to identify the `connectaddress` of your Linux distribution running via WSL 2. +A concrete example of wsl-portforward.bat : -## IPv6 access +```cmd +@echo off -- `wsl hostname -i` for the IP address of your Linux distribution installed via WSL 2 (the WSL 2 VM address) -- `ip route show | grep -i default | awk '{ print $3}'` for the IP address of the Windows machine as seen from WSL 2 (the WSL 2 VM) +set vmip=vimp_null +FOR /F %%i IN ('wsl.exe -d Ubuntu-22.04 hostname -I') DO set vmip=%%i -Using `listenaddress=0.0.0.0` will listen on all [IPv4 ports](https://stackoverflow.com/questions/9987409/want-to-know-what-is-ipv4-and-ipv6#:~:text=The%20basic%20difference%20is%20the,whereas%20IPv6%20has%20128%20bits.). +REM === TODO: some text error checking for vmip. === -## Mirrored mode networking +netsh interface portproxy add v4tov4 listenport=14000 listenaddress=0.0.0.0 connectport=4000 connectaddress=%vmip% + +REM === This netsh does not set ERRORLEVEL on error, if given wrong parameter. Beg your own fortune. === +``` -On machines running Windows 11 22H2 and higher you can [set `networkingMode=mirrored` under `[wsl2]` in the `.wslconfig` file](./wsl-config.md#configuration-settings-for-wslconfig) to enable mirrored mode networking. Enabling this changes WSL to an entirely new networking architecture which has the goal of 'mirroring' the network interfaces that you have on Windows into Linux, to add new networking features and improve compatibility. -Here are the current benefits to enabling this mode: +## More about WSL2 mirrored mode networking + +Compared to WSL2 NAT mode, here are the current benefits to enabling this mode: - IPv6 support -- Connect to Windows servers from within Linux using the localhost address `127.0.0.1`. IPv6 localhost address `::1` is not supported +- Connect to WinHost server programs from within WSL instance using localhost address `127.0.0.1`. But, IPv6 localhost address `::1` is not supported. - Improved networking compatibility for VPNs - Multicast support - Connect to WSL directly from your local area network (LAN) -> [!NOTE] -> Run the following command in PowerShell window with admin privileges to [Configure Hyper-V firewall](/windows/security/operating-system-security/network-security/windows-firewall/hyper-v-firewall) settings to allow inbound connections: `Set-NetFirewallHyperVVMSetting -Name '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -DefaultInboundAction Allow` or `New-NetFirewallHyperVRule -Name "MyWebServer" -DisplayName "My Web Server" -Direction Inbound -VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -Protocol TCP -LocalPorts 80`. -This new mode addresses networking issues seen with using a NAT (Network Address Translation) based architecture. Find known issues or file feedback on any bugs identified in the [WSL product repo on GitHub](https://github.com/microsoft/wsl). +Be aware, WSL2 mirrored mode does not exempt WSL2 server programs from WinHost's firewall rule. That means, for a WSL program to serve on TCP port 80, WinHost administrator has to [configure Hyper-V firewall](/windows/security/operating-system-security/network-security/windows-firewall/hyper-v-firewall) to allow this inbound connection, using command: + +```powershell +Set-NetFirewallHyperVVMSetting -Name '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -DefaultInboundAction Allow +``` + +or + +```powershell +New-NetFirewallHyperVRule -Name "MyWebServer" -DisplayName "My Web Server" -Direction Inbound -VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -Protocol TCP -LocalPorts 80 +``` + +This new mirrored mode addresses networking issues seen with earlier NAT mode. Find known issues or file feedback on any bugs identified in the [WSL product repo on GitHub](https://github.com/microsoft/wsl). + + ## DNS Tunneling