When you enable HyperV and WSL on the same machine, there'll usually tons of ports being used, see netsh int ipv4 show excl proto=tcp
These excluded ports are managed by winnat (see https://stackoverflow.com/questions/65272764/ports-are-not-available-listen-tcp-0-0-0-0-50070-bind-an-attempt-was-made-to).
HNS is a supporting service for HyperV, managing all HyperV vEthernet adapters and Nat related things.
In the driver, there're a PortChunkSize option, which can be set in WinNat's Parameter registry.
Setting it to 1 would reduce the number of excluded ports, but WSL would fail to connect any address. A lower value works, but when WSL makes too much connections, the network performance degrades.
Deeper dive shows that ports are allocated for ExternalAddress, which can be shown by powershell command Get-NetNatExternalAddress
Suprisingly, the Remove-NetNatExternalAddress does not work, telling us error 122, which is buffer overrun.
And later I found that the buffer length in the PowerShell module are 39, but the NatName has a length of 40, causing buffer overrun all the time. Manually allowing one byte buffer overrun made the whole process continue.
I tried to delete all ExternalAddress, but some of them cannot be deleted, telling us error 1168, and some of them although being deleted, but comes back in several minutes. So, there must be something higher controlling the ExternalAddress.
Debugging the driver shows that most port reservations are coming from service HNS.
Microsoft gives us HCN API to interact with this service: https://docs.microsoft.com/en-us/windows-server/networking/technologies/hcn/hcn-top. There're two good tools for debugging and modifying the HNS:
Each network instance in HNS results in a bunch of NetNatExternalAddress, and each NetNatExternalAddress corresponds to a chunk of ports
After a deeper dive into the flow, I found that the excluded ports are allocated for different types of HCN networks
- One is the "mirrored interface", controlled by HNS::Service::Core::IsAdapterSuitableForMirroring, which finally forms the "vEthernet (XXX)" adapters.
- Directly removing these HCN networks does work, but they will come back in several minutes
- Manually patching the HNS::Service::Core::IsAdapterSuitableForMirroring get rids of those vEthernet adapters, and their related port reservations
- Another one is the "Default Switch" and "WSL", I removed the DefaultSwitch, by
Get-HnsNetworkEx | Where-Object {$_.Name -NotMatch "WSL"} | ForEach-Object {Remove-HnsNetworkEx $_}
Now there's only WSL's switch and their excluded ports.