Skip to content

Keep mirrored autobind ports within the host range - #41448

Open
hoobnn (hoobnn) wants to merge 3 commits into
microsoft:masterfrom
hoobnn:fix/issue-41227-ephemeral-port-range
Open

hoobnn (hoobnn) wants to merge 3 commits into
microsoft:masterfrom
hoobnn:fix/issue-41227-ephemeral-port-range

Conversation

@hoobnn

@hoobnn hoobnn (hoobnn) commented Aug 26, 2026

Copy link
Copy Markdown

Summary of the Pull Request

Mirrored networking reserves a host ephemeral port range in HCN and initially
configures Linux to use the same range. If a distribution later expands
net.ipv4.ip_local_port_range, Linux autobind can select a source port outside
the HCN allocation. Localhost TCP connections from those ports then remain in
SYN-RECV.

This change reserves the complement of the HCN allocation in
net.ipv4.ip_local_reserved_ports. Linux can still change
ip_local_port_range, but automatic TCP and UDP assignments remain inside the
host range. Explicit binds are unaffected. Existing reserved-port entries are
preserved when the complement is installed.

It also adds experimental.ephemeralPortRangeSize, with a default of 4096 and
validation for even values from 2 through 8192, so the allocated HCN range and
Linux reservation can be covered by regression tests.

PR Checklist

Detailed Description

GuestNetworkService::AllocateEphemeralPortRange() now accepts the configured
port count and returns the inclusive range allocated by HCN. Mirrored
networking passes that exact range to init.

Init continues to set ip_local_port_range to the HCN range. It reads the
current ip_local_reserved_ports value, preserves it, and appends the ranges
below and above the HCN allocation. A later change to ip_local_port_range
therefore cannot let root-namespace autobind escape the host reservation.

Configuration parsing rejects odd values, values outside 2-8192, empty or
negative values, and trailing characters. Invalid values use the existing
invalid-integer warning and fall back to the 4096-port default without
disabling mirrored networking.

The reservation is scoped to the initial root network namespace. A later
explicit write to ip_local_reserved_ports, or a newly created network
namespace, has its own lifecycle; this PR targets the issue's root-distro case
where ip_local_port_range alone is changed after boot.

Regression Coverage

NetworkTests::ConfigurableEphemeralPortRangeSize now verifies:

  • an 8192-port HCN allocation and the installed reserved-port complement;
  • widening Linux ip_local_port_range to 1024 65535 after boot;
  • an explicit TCP bind-to-zero remains within the HCN range;
  • a real guest loopback TCP connect() completes and its implicit source port
    remains within the HCN range;
  • UDP connect() autobind also selects a source port within the HCN range.

The default mirrored-mode test asserts the existing 4096-port behavior.
Configuration tests cover the legal minimum, default, empty, negative, odd,
out-of-range, and trailing-character values.

Validation

The original implementation was built as WSL 2.9.9.3 and manually validated
against the #41227 reproduction: expanding the range changed 0/20 successful
connections before the fix to 20/20 after the fix.

For the current rebased head:

  • changed-line clang-format produces no diff;
  • whitespace/diff validation passes;
  • the change applies cleanly to current master.

The new head still requires the repository's full Windows build and the added
TAEF paths to run in a Windows test environment.

Copilot AI lite review requested due to automatic review settings August 26, 2026 06:21
@hoobnn
hoobnn (hoobnn) requested a review from a team as a code owner August 26, 2026 06:21
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a mirrored-networking reliability issue where Linux autobind could select ephemeral source ports outside the host (HCN) reserved range after a distro widens net.ipv4.ip_local_port_range, causing localhost TCP connections to hang (e.g., SYN-RECV). It aligns Linux autobind behavior with the host-reserved range by reserving the complement of the HCN allocation in ip_local_reserved_ports, and adds a configurable ephemeral port-range size to support regression testing.

Changes:

  • Make the HCN ephemeral port reservation size configurable (experimental.ephemeralPortRangeSize) and plumb the requested count through mirrored networking to the HCN allocation call.
  • Update Linux init to reserve ports outside the HCN-allocated ephemeral range via net.ipv4.ip_local_reserved_ports, preventing autobind from escaping the host-reserved range.
  • Add/extend Windows tests to validate config parsing and mirrored-mode behavior for configurable ephemeral port range sizing and reserved-port complements.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/windows/UnitTests.cpp Adds config-warning coverage for invalid experimental.ephemeralPortRangeSize values.
test/windows/NetworkTests.cpp Adds mirrored-mode test coverage for configurable ephemeral port range sizing and reserved-port complement behavior.
test/windows/Common.h Extends test config defaults with ephemeralPortRangeSize.
test/windows/Common.cpp Emits experimental.ephemeralPortRangeSize in generated test configs.
src/windows/service/exe/WslCoreGuestNetworkService.h Updates API to allocate ephemeral port range with a requested port count.
src/windows/service/exe/WslCoreGuestNetworkService.cpp Implements size-parameterized HCN range allocation + validation and enhanced logging.
src/windows/service/exe/MirroredNetworking.cpp Passes configured ephemeral port count into the guest network service allocation call.
src/windows/common/WslCoreConfig.h Adds experimental.ephemeralPortRangeSize to config settings and default config state.
src/windows/common/WslCoreConfig.cpp Parses/validates experimental.ephemeralPortRangeSize from config.
src/linux/init/main.cpp Writes ip_local_reserved_ports complement of the host-reserved range to constrain Linux autobind.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/windows/common/WslCoreConfig.cpp
Copilot AI review requested due to automatic review settings August 26, 2026 06:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/linux/init/main.cpp:3675

  • Writing the complement directly to /proc/sys/net/ipv4/ip_local_reserved_ports overwrites any existing reserved-port configuration in the distro (e.g., values set by sysctl.conf). That can change application behavior by un-reserving ports the user/distro intended to keep out of autobind. Consider merging the existing ip_local_reserved_ports value with the complement (deduping if needed) rather than replacing it.
    // Mirrored networking reserves this exact range on the Windows host. Keep ports outside the range out of Linux
    // autobind selection even if a distro later widens ip_local_port_range. Explicit bind() calls are unaffected.
    std::string ReservedPorts;
    if (Start > 1)
    {
        ReservedPorts = std::format("1-{}", Start - 1);
    }

    if (End < USHRT_MAX)
    {
        if (!ReservedPorts.empty())
        {
            ReservedPorts += ',';
        }
        ReservedPorts += std::format("{}-{}", End + 1, USHRT_MAX);
    }

    return WriteToFile("/proc/sys/net/ipv4/ip_local_reserved_ports", ReservedPorts.c_str());

test/windows/NetworkTests.cpp:4469

  • This test binds port 0 but does not validate the actual port chosen, nor does it widen net.ipv4.ip_local_port_range after boot (the scenario that caused autobind to escape the host allocation). As written, it will pass even if autobind still escapes once ip_local_port_range is widened. Consider widening ip_local_port_range and using BindGuestPortZero() to assert the assigned port stays within [startPort, endPort].
        auto tcpPort = NetworkTests::BindGuestPort(L"TCP4-LISTEN:0", true);
        auto udpPort = NetworkTests::BindGuestPort(L"UDP4-LISTEN:0", true);

Mirrored networking reserves a host ephemeral port range in HCN, but changing ip_local_port_range allows Linux autobind to select ports outside that range. Localhost TCP connections from those ports then hang in SYN-RECV.

Reserve the complement of the host range with ip_local_reserved_ports so later range changes cannot escape the HCN allocation. Add an experimental range-size setting and regression coverage for TCP and UDP autobind.
Parse the setting with strtol and require full-string consumption, matching
ConfigKey::ParseImpl for integers. Values like 4096abc are now rejected with
the invalid-integer warning, and base prefixes such as 0x1000 are accepted
consistently with other integer settings.

Addresses review feedback on the std::stoi inconsistency. Adds a warning
test for a trailing-character value.
Merge the existing ip_local_reserved_ports value before adding the HCN complement so current reservations are not discarded.

Exercise the original widened-range failure with a real guest loopback TCP connect, verify UDP autobind source ports, and cover the default and parser boundary values.
Copilot AI review requested due to automatic review settings August 31, 2026 01:30
@hoobnn
hoobnn (hoobnn) force-pushed the fix/issue-41227-ephemeral-port-range branch from b89caac to 773d747 Compare August 31, 2026 01:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Comment thread src/linux/init/main.cpp
Comment on lines +3658 to +3685
// Mirrored networking reserves this exact range on the Windows host. Keep ports outside the range out of Linux
// autobind selection even if a distro later widens ip_local_port_range. Explicit bind() calls are unaffected.
constexpr auto ReservedPortsPath = "/proc/sys/net/ipv4/ip_local_reserved_ports";
std::string ReservedPorts;
{
std::ifstream ExistingReservedPorts{ReservedPortsPath};
std::getline(ExistingReservedPorts, ReservedPorts);
}

if (Start > 1)
{
if (!ReservedPorts.empty())
{
ReservedPorts += ',';
}
ReservedPorts += std::format("1-{}", Start - 1);
}

if (End < USHRT_MAX)
{
if (!ReservedPorts.empty())
{
ReservedPorts += ',';
}
ReservedPorts += std::format("{}-{}", End + 1, USHRT_MAX);
}

return WriteToFile(ReservedPortsPath, ReservedPorts.c_str());
Comment on lines +2203 to +2208
auto [output, warnings] = LxsstuLaunchWslAndCaptureOutput(command.c_str(), 0);
VERIFY_IS_TRUE(warnings.empty());

const auto assignedPort = static_cast<uint16_t>(std::stoul(output));
VERIFY_IS_TRUE(assignedPort > 0);
return assignedPort;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants