Skip to content

Instantly share code, notes, and snippets.

@NyaMisty
Created December 28, 2021 22:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NyaMisty/aac9a5d4f9c79893333251b2d8e13a1d to your computer and use it in GitHub Desktop.
Save NyaMisty/aac9a5d4f9c79893333251b2d8e13a1d to your computer and use it in GitHub Desktop.
Make HNS not consuming too much ports

Windows 10 / Windows 11 Hyper-V Port Excluded issue

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

HNS WinNat

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.

The WinNat Driver

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

Interact with NetNat powershell module

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.

Interact with 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.

def patch(filepath='C:\\Windows\\System32\\HostNetSvc.dll'):
f = open(filepath, 'r+b')
content = bytearray(f.read())
# find telemetry string of that function
t = content.find(b'HNS::Service::Core::IsAdapterSuitableForMirroring')
found = -1
for i in range(len(content)):
# find lea instruction referencing the telemetry str
if int.from_bytes(content[i:i+4], 'little') + i + 4 == t:
found = i
break
if found == -1:
print("Failed to find IsAdapterSuitableForMirroring!")
return False
# find the function prologue
funcStart = content.rfind(b'\x4C\x8B\xDC\x57\x41\x54\x41\x55\x41\x56', 0, found)
f.seek(funcStart)
# patch the prolog into mov eax, 0; retn
f.write(b'\xB8\x00\x00\x00\x00\xC3')
#content[funcStart:funcStart+6] = b'\xB8\x00\x00\x00\x00\xC3'
f.close()
return True
patch()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment