Open Daniel-Nashed opened 2 years ago
There are workarounds like this https://github.com/microsoft/WSL/discussions/7395. Some developers created powershell scripts with it.
Thanks for the inspiration. But it is still tricky. Your JSON didn't work for me. I always got back that the network already exists. Even after start when it wasn't there and also when I first deleted it.
Here is what worked for me. Your link and the additional links I followed gave me inspiration. I needed to add the other parameters as well to the JSON.
My current strategy is to define the sub net and add an alias IP that either is not there yet or if already present, we can ignore the error.
See my fist version without configurable parameters.
What do you think?
Why can't WSL allow to customize the creation of the network and allow a static assignment?
Daniel
/*
Author: Daniel Nashed (Nash!Com)
(Re)creates the WSL network with a specific subnet 192.168.22.0/24 and adds an alias 192.168.222.222 for the assigned range.
In case the IP is already set, the error can be ignored.
This works on Windows 10+ but requires sudo permissions for the "ip addr " command.
In Windows 11 /etc/wsl.conf supports a command in the [boot] section executed as root to set the IP.
References and thanks for the inspiration:
https://github.com/microsoft/WSL/discussions/7395
https://github.com/skorhone/wsl2-custom-network
For testing: Powershell Get current settings :
Get-HnsNetwork | Where-Object { $_.Name -Eq "WSL" }
Useful commands:
Get WSL container IP:
wsl hostname -I
Set addtional IP
wsl sudo /sbin/ip a add 192.168.222.222/24 dev eth0
*/
#include <stdio.h>
#include <computenetwork.h>
#include <Objbase.h>
#pragma comment(lib, "computenetwork")
#pragma comment(lib, "Ole32.lib")
int main(void)
{
int ret = -1;
HRESULT hRes = 0;
HCN_NETWORK hcnNetwork = NULL;
PWSTR errRecord = NULL;
const GUID netId = {
0xB95D0C5E,
0x57D4,
0x412B,
{ 0xB5, 0x71, 0x18, 0xA8, 0x1A, 0x16, 0xE0, 0x05 } };
// Flags = EnableDnsProxy + IsolateVSwitch
// Type = Static
PCWSTR netSettings = LR"(
{
"Name" : "WSL",
"Flags": 9,
"Type": "ICS",
"IPv6": false,
"IsolateSwitch": true,
"MaxConcurrentEndpoints": 1,
"Subnets" : [
{
"ID" : "FC437E99-2063-4433-A1FA-F4D17BD55C92",
"ObjectType": 5,
"AddressPrefix" : "192.168.222.0/24",
"GatewayAddress" : "192.168.222.1",
"IpSubnets" : [
{
"ID" : "4D120505-4222-4CB2-8C53-DC0F70049696",
"Flags": 3,
"IpAddressPrefix": "192.168.222.0/24",
"ObjectType": 6
}
]
}
],
"MacPools": [
{
"EndMacAddress": "00-15-5D-52-CF-FF",
"StartMacAddress": "00-15-5D-52-C0-00"
}
]
})";
hRes = HcnOpenNetwork (netId, &hcnNetwork, &errRecord);
if (S_OK == hRes)
{
printf ("WSL Network already exists\n");
CoTaskMemFree (errRecord);
errRecord = NULL;
if (hcnNetwork)
{
hRes = HcnCloseNetwork (hcnNetwork);
hcnNetwork = NULL;
}
hRes = HcnDeleteNetwork (netId, &errRecord);
if (S_OK == hRes)
{
printf ("WSL Network deleted\n");
}
else
{
printf ("WSL Network cannot be deleted [%ls]!\n", errRecord);
}
CoTaskMemFree (errRecord);
errRecord = NULL;
}
else
{
printf ("WSL Network does not exist!\n");
}
hRes = HcnCreateNetwork (netId, netSettings, &hcnNetwork, &errRecord);
if (S_OK == hRes)
{
printf ("WSL Network created\n");
ret = 0;
}
else
{
printf ("WSL Network not created [%ls]!\n", errRecord);
}
CoTaskMemFree (errRecord);
errRecord = NULL;
/* Set the address we want in the sub-net created as an alias */
system ("wsl sudo /sbin/ip a add 192.168.222.111/24 dev eth0");
Done:
if (hcnNetwork)
{
hRes = HcnCloseNetwork (hcnNetwork);
hcnNetwork = NULL;
}
return ret;
}
Your JSON didn't work for me.
In that discussion, I just provided the code as an example or template. If you need further customizations in that JSON I can try to help.
Why can't WSL allow to customize the creation of the network and allow a static assignment?
Maybe for security? 🤷
Thanks! the JSON from the other post (referenced in my code example) helped and works.
I don't see a security issue here. We are on a local machine. The port is not exposed outside your local machine -- unless you add a proxy component. That's a different discussion.
Any other virtualization supports to specify fixed IP addresses! And it can be a configuration option.
I am OK with the workaround for now. But not everyone can compile code on their own or want to download additional EXE files from the web.. IMHO this belongs into the product. Not only to specify the network range, but also a statically assigned IP.
I will make the code available once I made it a bit customizable. Probably it would need to allow at least specifying the range and the IP set.
If you have feedback or ideas, I can write it up and publish it. This is really needed when we want to use applications that cannot use the localhost
option. It will be probably in one of my existing repositories here I need it -- with Apache 2.0 license.
Thanks again! Your code and the other references I found have been really helpful!
It was still some research and testing to get it working. And the routines needed are only available 64bit.
-- Daniel
Two things I noticed. For a specific pinned IP address, a new network endpoint has to be created using HcnCreateEndpoint and attached with WSL2 VM using HcsModifyComputeSystem. To run a command in a WSL environment, WslLaunch API can be used instead of system function. It handles the I/O of wsl.exe process internally.
Thanks! Hmmm .. The assigned IP works in my Windows 10 environment without creating a new end-point on my own.
What doesn't work for you if you don't create a new end-point?
Do you have a reference to the WslLaunch API
? I want to have a look but I think I want to keep it as simple as possible.
There might be other commands needed in future. So having it generic makes sense to me. But I would be interested to look at the API.
The routine now also allows to specify an IP. It assumes it's a class C and calculates network and gateway.
And it checks if WSL is already started printing an info that WSL needs to be restarted.
What doesn't work for you if you don't create a new end-point?
I mean like this. Only AddressPrefix
can be configured with HcnCreateNetwork. So, the WSL2 VM has a random IP address in 192.168.222.0/24
range. But you asked for "pinned IP address" which can be configured with HcnCreateEndpoint. It has IPAddress
field in JSON endpoint object.
Do you have a reference to the WslLaunch API?
Ohhh now I got it! I have the code in place but I am still looking for the JSON string.
The only reference I found so far is this Example which does not contain an IP.
Do you have a JSON example for me? This will save a lot of time spelunking around ..
Update: meanwhile I found the reference.
But an example would still really helpful!
Thanks!
Found it.. But this will require some more work to get it clean after lunch... I think I should generate the GUIDs beside the one for the network with the specific ID.
Now that I got it working. And I found the PowerShell commands to check it earlier, did you ever try to use New-HnsNetwork
could this work as well? Can the ID
of the network be specified? Would this be worth trying?
{
"SchemaVersion": {
"Major": 2,
"Minor": 0
},
"Flags" : 0,
"HostComputeNetwork" : "B95D0C5E-57D4-412B-B571-18A81A16E005",
"IpConfigurations" : [
{
"IpAddress": "192.168.123.124",
"PrefixLength": 24,
"IpSubnetId" : "4D120505-4222-4CB2-8C53-DC0F70049696"
}
]
}
Yes, that one. My test code is in primary state. I need some json parsing library written in C or C++. Can you suggest one which can fit well in this situation?
I am using RapidJSON..
My prints use the %ls
format which works in a printf as well.
Now that it works, did you ever try to use PowerShell to set it? I am not a big PowerShell fan. But this would be easier for others to consume. And PowerShell has simple to use JSON parsing.
Look at this example. In this case it is reading from a file. But it should also work form a buffer:
$DominoBackupCfg = Get-Content -Raw -Path $DominoBackupConfigFile | ConvertFrom-Json
foreach ($SearchCfg in $DominoBackupCfg)
{
if ( $RestoreTarget -eq $SearchCfg.IpAddress)
{
$RestoreVmHost = $SearchCfg.VmHost
...
}
}
Do you have an idea how the endpoint is now assigned to the WSL container?
Right now it still gets a random IP from the subnet. Sounds like the mapping would be by MAC address.
I could specify a MAC in the endpoint, but how do I know which MAC the WSL container has?
If you want to play around, I can share my current code.
@Biswa96 Looks like creating a HNSEndpoint
does not work as you expected.
I can create endpoints and assign an IP and a MACs address. But WSL always creates a new network and assigns a VirtualMachine
ID. It uses IPs and MACs from the network. But if you create one, it always does see it as an existing other entry and creates a new one.
So it looks like we can't create our own entries ant let WSL use it.
That's probably only something the WSL team can sort out.
I have code working to create the resources and also to enumerate them. Plus also converting GUIDs.
But this doesn't help and I have to stay with my trick to add an alias IP to the WSL stack.
If you have more ideas I would give it another try or share the code.
-- Daniel
In that previous discussion, I have provided the sample code to add specific IP address in WSL2. Direct link https://github.com/microsoft/WSL/discussions/7395#discussioncomment-1803186
Thanks! I haven't looked into the other management components.
I see you are removing the network and adding a new network based on the settings needed.
This needs a bit more code around it like searching the for the right VM getting the ID.
Enumerating ComputeSystems and getting the properties will probably need a bit of JSON magic around it..
Not sure if we really need to add code to compute new random GUIDs. But I have this in place in my code already.
I never worked with those type of APIs, but with your pointers how to start, I can make it work based on what I already have.
To automate the process I would need to find the VM first HcsEnumerateComputeSystems, open it HcsOpenComputeSystem and then get the VM ID via HcsGetComputeSystemProperties.
This sounds like a bit of work and as you say it requires that the VM is already created by WSL.
But this would allow other fun things to do with the VM in future :-)
quick update:
Now we finally need some JSON magic. But I am tempted to just do string operations for now. Here is what you get back from the enumeration for now before I add JSON parsing.
Not that we end up writing up our own WSL management tool .. Hmmm... I just wanted a static IP for my WSL environment ;-)
-- Daniel
[
{
"Id": "266A9C21-0EF0-4983-BD9C-CAB2F07D09CA",
"SystemType": "VirtualMachine",
"Owner": "WSL",
"RuntimeId": "266a9c21-0ef0-4983-bd9c-cab2f07d09ca",
"State": "Running"
},
{
"Id": "98456253-fb5a-41ad-ada8-787f9e01466b",
"SystemType": "VirtualMachine",
"RuntimeOsType": "Windows",
"Owner": "CmService",
"RuntimeId": "98456253-fb5a-41ad-ada8-787f9e01466b",
"State": "SavedAsTemplate"
}
]
hcsdiag.exe list
266A9C21-0EF0-4983-BD9C-CAB2F07D09CA
VM, Running, 266A9C21-0EF0-4983-BD9C-CAB2F07D09CA, WSL
98456253-fb5a-41ad-ada8-787f9e01466b
VM, SavedAsTemplate, 98456253-FB5A-41AD-ADA8-787F9E01466B, CmService
Filtering it by owner looks like the only way to distinct the VMs..
@Biswa96 it's not working for me. Have working code that creates all relevant parts. Just filling in the Virtual machine ID manually for now.
The MAC address shows up in the VM but the IP address is not picked up.
I am using Get-HNSEndpoint
in PowerShell to dump the results of the WSL created endpoint and our endpoint. The endpoint was missing the VirtualMachine
-- which I added. But still the IP address is not assigned inside the VM. I also added PrefixLength
which was also missing. There are some other fields missing in the hand crafted endpoint.
How much is this verified to work on your end? I am happy to beef up prototype code. But I can't spend hours on trying to figure out the right parameters.
How sure is it that it should work and always work? My approach to add the alias IP is much less likely to break. This approach is mocking around with the WSL created endpoint. Maybe it is time to get from feedback from the WSL team?
My current self created endpoint
Get-HNSEndpoint
ID : b95d0c5e-57d4-412b-b571-18a81a16e222
Name : Ethernet
Version : 47244640267
AdditionalParams : @{SwitchId=142A47F4-90F2-4C08-AC65-9280EE7C8F2D; SwitchPortId=B9FE0A99-0500-46D2-9AE8-5BF6D469631F}
Resources : @{AdditionalParams=; AllocationOrder=2; Allocators=System.Object[]; CompartmentOperationTime=0; Flags=0; Health=; ID=3DC35E3E-CAB2-4FD2-9E21-E4BE98DA5230;
PortOperationTime=0; State=1; SwitchOperationTime=0; VfpOperationTime=0; parentId=0FD182B8-436F-476B-AFFB-7636C43E6CCC}
State : 2
VirtualNetwork : b95d0c5e-57d4-412b-b571-18a81a16e005
VirtualNetworkName : WSL
MacAddress : 00-15-5D-52-C0-22
IPAddress : 192.168.222.111
PrefixLength : 24
GatewayAddress : 192.168.222.1
IPSubnetId : 4d120505-4222-4cb2-8c53-dc0f70049696
DNSServerList : 192.168.222.1
SharedContainers : {}
VirtualMachine : 188276C0-3D4C-45B8-94D5-2DABD11F4654```
Endpoint created by WSL
It has AdditionalParams
set to a different SwitchId
and SwitchPortId
Get-HNSEndpoint
ID : 37baae10-45dd-473c-9b82-d54c795f706a
Name : Ethernet
Version : 47244640267
AdditionalParams : @{SwitchId=573F9E5D-4BC7-49E9-994E-610AB61471BC; SwitchPortId=D913CBE5-3233-455D-9330-3CD99A981CDB}
Resources : @{AdditionalParams=; AllocationOrder=2; Allocators=System.Object[]; CompartmentOperationTime=0; Flags=0; Health=; ID=A4AD4466-82CB-4920-8340-C6AB7F4378A0;
PortOperationTime=0; State=1; SwitchOperationTime=0; VfpOperationTime=0; parentId=2FC115CC-AF91-4DE9-AB83-A07813F1A085}
State : 2
VirtualNetwork : b95d0c5e-57d4-412b-b571-18a81a16e005
VirtualNetworkName : WSL
MacAddress : 00-15-5D-52-C0-2C
IPAddress : 192.168.222.251
PrefixLength : 24
GatewayAddress : 192.168.222.1
IPSubnetId : 4d120505-4222-4cb2-8c53-dc0f70049696
DNSServerList : 192.168.222.1
SharedContainers : {}
VirtualMachine : 188276C0-3D4C-45B8-94D5-2DABD11F4654
ip addr returns thew following. The MAC is set but the IP address is not showing up.
7: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 00:15:5d:52:c0:22 brd ff:ff:ff:ff:ff:ff
Here is the current template to create the endpoint.
VirtualMachine ID is still hard coded until it works (needs to add JSON parsing ..).
PCWSTR endpointSettingsJSON = LR"(
{
"Name": "Ethernet",
"IpAddress": "%s",
"PrefixLength": 24,
"MacAddress": "00-15-5D-52-C0-22",
"VirtualNetwork" : "B95D0C5E-57D4-412B-B571-18A81A16E005",
"VirtualMachine" : "188276C0-3D4C-45B8-94D5-2DABD11F4654"
})";
How much is this verified to work on your end?
I am using this workaround in Windows 10 for WSLg stuff.
How sure is it that it should work and always work?
I am not sure if this works in Windows 11 because I have not used it yet.
Maybe it is time to get from feedback from the WSL team?
Absolutely.
I can't get it working on Windows 10. It looks all good but the machine only picks up the MAC.
This might be a detail. I did not finish the code to parse the JSON, because I wanted it to work first.
I am happy to share the code to look into it and to leverage what I built around I already.
This isn't really the final solution. IMHO the WSL project should address it.
Clearly what we are doing here is just a temporary solution to not re-create connections all the time after boot.
Still if you want to look into my code, I can bring it up somewhere. I added a couple of useful routines for testing.
But beside my first approach at the beginning of the code, all the other parts are more for testing your approach to create the EndPoint directly with a fixed MAC and IP.
The code you posted it more PoC code. I am happy to spend the time and contribute a final solution once we make sure it really works. I can't spend days on researching it right now... Even it was a lot of fun last weekend looking into it.
Again I would like the WSL team pick it up and address it in the product! It is really needed for applications requiring more than just the simple "localhost" mapping!.
-- Daniel
@craigloewen-msft why is this issue closed? I don't see any of the related issues you referenced in #7925 are a work-around nor a feature request.
For now until there is a solution I wrote a small helper tool with the help from @Biswa96 . Thanks again.
I ended up not trying to set an endpoint, which is something the WSL team should implement in the product.
The work-around creates a new network and sets an alias IP for now.
Thanks
Daniel
This issue isn't closed, it's open and our tracking place for this request!
Oh the other one is closed #7925 .. The status for this issue is on top. Sorry! Thanks for your feedback. Do you have any idea when this might be addressed. Or do you have any other work-around than the tool I wrote?
Deploying a tool is challenge for some corporate users I am working with.
Thanks Daniel
the link in Daniel's post on 1/21/2022 [i think] is dead.
i'm just a lowly physicist :-) so the code is impenetrable to me. pardon the absolutely ignorant question, but how does one use all of this? i'm currently setting up a virtual research network using wsl2 and docker desktop and a static IP is essential. not asking for anyone to spend any time explaining, if someone could just point me to a reference...? i've googled and bing-ed until my fingers are numb.
many thanks -mike
I checked the forum and found multiple requests, which are pending for a while.
WSL is great! But admins and developers need to be able to reliably access services on the WSL instances!
The
localhost
mapping does not help for all application types -- even just accessing from host to WSL.It drills down to the following requests
You can add another IP today inside the VM and assigning it via /etc/wsl.conf boot command in Win 11 should work. But if the IP range changes, this doesn't help! (e.g.
ip a add 172.21.237.111/20 dev eth0
)The hyper-v local only adapter has no configurable IP range as well. And this might be part of the problem ..
If you cannot fix it soon, maybe we can get an valid work around by changing the WSL hyper-v network adapter?
It is not clear how the adapter gets assigned to WSL.
Thanks
Daniel