13

Using Windows Subsystem for Linux 2, I want to run two separate instances of Centos 7, but when I do this both instances have the same eth0 inet address. Here's what I did...

I created a base Centos 7 tarball for my base, then created two separate distros and it all looks and runs as I expect it to look;

    > wsl --import centos7-1 centos7-1 centos7.tar.gz --version 2
    > wsl --import centos7-2 centos7-2 centos7.tar.gz --version 2
    > wsl -l -v
      NAME                   STATE           VERSION
    * centos7-1              Stopped         2
      centos7-2              Stopped         2
    > ls
        Directory: C:\Users\me\centos
    Mode                 LastWriteTime         Length Name
    ----                 -------------         ------ ----
    d-----          4/8/2022  11:28 AM                centos7-1
    d-----          4/8/2022  11:29 AM                centos7-2
    -a----          4/8/2022  11:24 AM      174323670 centos7.tar.gz

EXCEPT when I open and run each distro in separate PS windows, and run ifconfig eth0 in each, they both show the same eth0 inet address.

So, what am I missing? How do I get them to have independent IP addresses?

I've noticed, since I first posted, that even instances of different linux distros, eg Ubuntu, run at the same time as Centos, all get the same IP address.

NotTheDr01ds
  • 28,025
CAB
  • 485

2 Answers2

13

@DanielB's answer is an excellent explanation of why all WSL2 distributions (I like to call them "instances") share the same address.

Each WSL2 instance has a separate:

  • PID namespace
  • Mount namespace
  • IPC namespace
  • UTS namespace
  • WSLg System Distribution

However, they all share the following with the parent WSL2 VM (and thus each other):

  • User namespace
  • (the only one we really care about here) Network namespace
  • Cgroup namespace
  • Device tree (other than /dev/pts)
  • CPU/Kernel/Memory/Swap (obviously)
  • /init binary (but not process)

The interesting thing is that we can use the same technology, namespaces, to "pretend" to create a separate IP address for each instance.

For the "n-tier testing" scenario you mention in the comments, this will hopefully be sufficient. We can script the creation of the namespace for each instance and (optionally) automatically enter it via the wsl command-line.

While I had a hunch that this would work, I had never played around with network namespaces before trying this out. Don't worry - It was a worthwhile learning exercise for me. Huge credit goes to this blog post for getting me 90% there.

With that said, here's a first (or second, or third ...) pass at a script to configure a network namespace that will allow each namespace in each instance to have a different IP.

Create this as something like /usr/local/sbin/wsl-netns.sh:

#!/usr/bin/env bash
instance_num=$1
#if [ -e /run/netns/]

Create the bridge that will be common to all instances.

Only a wsl --shutdown will terminate the bridge, unless

otherwise manually removed.

if [ ! -e /sys/devices/virtual/net/br1 ] then ip link add name br1 type bridge ip addr add 10.0.0.253/24 brd + dev br1 ip link set br1 up fi

Add namespace for this instance

if [ ! -e /run/netns/vnet${instance_num} ] then ip netns add vnet${instance_num} fi

Adds a veth pair. The vethX

side will reside # inside the namespace

and be the primary NIC inside that namespace.

The br-vethX end will reside in the primary

namespace.

ip link add veth${instance_num} type veth peer name br-veth${instance_num} ip link set veth${instance_num} netns vnet${instance_num}

Give it a unique IP based on the instance number

ip netns exec vnet${instance_num}
ip addr add 10.0.0.${instance_num}/24 dev veth${instance_num} ip link set br-veth${instance_num} up

Add the bridged end of the veth pair

to br1

ip link set br-veth${instance_num} master br1 ip netns exec vnet${instance_num}
ip link set veth${instance_num} up

Set the default route in the namespace

ip netns exec vnet${instance_num}
ip route add default via 10.0.0.253

Enable loopback fort he namespace

ip netns exec vnet${instance_num}
ip link set up dev lo

Set up NAT for return traffic

iptables
-t nat
-A POSTROUTING
-s 10.0.0.0/24
-j MASQUERADE

Enable forwarding

sysctl -w net.ipv4.ip_forward=1

Optional - Start a namespace for the

default WSL user (UID 1000).

You can exit this namespace normally

via the exit comamnd or Ctrl+D.

default_username=$(getent passwd 1000 | cut -d: -f1) nsenter -n/var/run/netns/vnet${instance_num} su - $default_username

Not much error handling in there at the moment, but it's fairly straightforward. Set it executable and run it from an instance with:

/usr/local/sbin/wsl-netns.sh 1 # replace with the instance number

Or start up WSL directly with it via:

wsl ~ -d centos7-1 -u root -e sh -c "/usr/local/sbin/wsl-netns.sh 1"

Just make sure to use a unique instance number for each. The address for each instance/namespace will be 10.0.0.<instance_number>, and you can access it from any other WSL2 instance.

To test, start a simple service inside one of the namespaces. For example, if you are in the namespace that the script dropped you into, you could just run:

python3 -m http.server

You can then access that server at 10.0.0.1:8000 from another instance. You will not be able to access the service via any other IP.

Alternatively, you don't have to use nsenter to drop into the namespace. You can directly start a service inside it via:

sudo ip netns exec vnet<num> \
    service xyz start

Or whatever your bring-up command is.

Added bonus:

  • IPv6 works since you aren't leaving internal WSL2 network.

Limitations:

  • Tested in Ubuntu - May need small tweaks for CentOS.
  • You cannot access those IP addresses from Windows, just other WSL2 instances. You would need to add additional forwarding rules to handle that.
  • Currently, all communication is via address. If you want to assign hostnames, you'll need to override WSL's automatic /etc/hosts generation and define your own. Or set it up via DNS, of course.
NotTheDr01ds
  • 28,025
8

WSL 2 distributions all reside in the same network namespace.

You may or may not be familiar with Docker. Internally, WSL 2 works in a very similar way. WSL 2 runs in a virtual machine, on a real Linux kernel. All WSL distributions share the same virtual machine. This works by using different containers for each. They are isolated in various regards: each distribution has a different view on process IDs and the filesystem. They do share the same view on network interfaces.

You cannot make them have different IP addresses. It also is (mostly) irrelevant, because you can only tell the difference on your local machine. Outside, they are NATed to your PC's IP address.

NotTheDr01ds
  • 28,025
user219095
  • 65,551