WSL2 two separate Centos distributions have same eth0 inet addres
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.gzEXCEPT 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.
52 Answers
@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
- User namespace
- (I believe) Cgroup namespace
- (And probably) IPC and UTS namespaces
- WSLg System Distribution (Windows 11 only)
However, they all share the same:
- (the only one we really care about here) Network namespace
- Device tree (other than
/dev/pts) - CPU/Kernel/Memory/Swap (obviously)
/initbinary (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_usernameNot 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 numberOr 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.serverYou 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 startOr 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/hostsgeneration and define your own. Or set it up via DNS, of course.
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.
1