Repository navigation
tcp: release the port of a wildcard-bound endpoint when it is closed - #16180
copybara-service[bot] merged 1 commit into
Conversation
A wildcard-bound endpoint reserves a port using the wildcard address, but connecting replaces its local address with the connected address A dual-stack endpoint also changes its effective network protocols when connecting to an IPv4-mapped peer, leaving an IPv6 reservation that cleanup did not release Track the address and network protocols of the reservation independently from the connected endpoint and retain the remaining IPv6 ownership after releasing the IPv4 reservation Verify IPv4 and IPv4-mapped wildcard binds with a Linux and gVisor syscall test and add a bind/close benchmark Fixes google#16102
ed3f77e to
48e209b
Compare
|
Could a maintainer approve the fork workflows for 48e209b? Go, Lint, Gazelle, License header, Labeler and Actions Workflow Security Scan are marked Local validation commands and results are in the PR description, while |
|
ありがとう |
|
can you post the raw benchstat output for BenchmarkWildcardBindClose btw? |
|
Thanks, I will post the raw benchstat output tonight (Tokyo time) |
|
@milantracy Here is the raw Measured on Linux ARM64 in a Colima-backed Ubuntu container with 6 vCPUs, using 32 samples per version and alternating the before/after execution order
Each sample runs with Command: This measures a 1.76% increase in bind/close time, with unchanged allocation bytes and counts |
Fixes #16102 ### Compatibility `COMPATIBILITY:NEW-APP-CONFIG` — fixes source-port exhaustion in the repeated wildcard-bound connections used by the BusyBox `nc` healthchecks reported in #16102 ### Problem and fix A wildcard bind reserves its port using the wildcard address, but connecting replaces the endpoint local address with the connected address A dual-stack wildcard bind also reserves both IPv6 and IPv4, while connecting to an IPv4-mapped peer changes `effectiveNetProtos` to IPv4 and releases the IPv4 reservation Cleanup must therefore use the reservation address and the network protocols still owned by the endpoint, rather than the connected address and effective protocols - Record `boundAddr` and `boundNetProtos` for explicit binds, implicit connect reservations and accepted tuples - Release the IPv4 reservation with its complete metadata and retain the IPv6 reservation for close - Use the recorded reservation in both close and cleanup paths, with `checklocks` annotations on the new fields Rebased onto `0bbcbf9de7626fcecba97f37723dce80b435d73d` ### Regression coverage Adapted the syscall test suggested by @tamird into `SocketInetLoopback.WildcardBoundPortReleasedOnClose`, covering IPv4 wildcard and dual-stack wildcard connections to an IPv4 peer The test uses an abortive close to avoid TIME_WAIT and verifies that both IPv4 and IPv6-only wildcard binds can reuse the port The review reproducer passes on Linux and fails on the previous PR implementation with `EADDRINUSE` for the IPv6-only bind The updated test passes on both Linux and gVisor, replacing the duplicate Go behavior test ### Local validation Linux ARM64 in a Colima-backed Ubuntu container, Bazel 8.8.1 - Focused regression on native Linux and gVisor systrap — passed - Full `socket_inet_loopback` suite on native Linux and gVisor systrap — passed, 8 shards each - TCP and ports package tests — all 28 targets passed, including nogo and checklocks analysis for ARM64 and AMD64 - Ports, TCP and dual-stack tests with race detection and lockdep enabled together — all 3 targets passed - `make lint` — passed - `make lint-cc` — passed with the compilation database scoped to the affected syscall target and its 16 source files - `git diff --check` — passed ```sh bazel test $common --test_output=errors --test_sharding_strategy=disabled \ --test_filter=SocketInetLoopback.WildcardBoundPortReleasedOnClose \ //test/syscalls:socket_inet_loopback_test_native make syscall-tests DOCKER_BUILD=false BAZEL_OPTIONS="$common" \ TARGETS=//test/syscalls:socket_inet_loopback_test_runsc_systrap_shared \ OPTIONS="--test_sharding_strategy=disabled --test_filter=SocketInetLoopback.WildcardBoundPortReleasedOnClose" bazel test $common --test_output=errors \ //pkg/tcpip/ports/... //pkg/tcpip/transport/tcp/... make syscall-tests DOCKER_BUILD=false BAZEL_OPTIONS="$common" \ TARGETS=//test/syscalls:socket_inet_loopback_test_native make syscall-tests DOCKER_BUILD=false BAZEL_OPTIONS="$common" \ TARGETS=//test/syscalls:socket_inet_loopback_test_runsc_systrap_shared bazel test $common --config=race --config=lockdep --test_output=errors \ //pkg/tcpip/ports:ports_test //pkg/tcpip/transport/tcp:tcp_test \ //pkg/tcpip/transport/tcp/test/e2e:dual_stack_test make lint DOCKER_BUILD=false BAZEL_OPTIONS="$common" python3 tools/gen_compile_commands.py //test/syscalls/linux:socket_inet_loopback_test make lint-cc DOCKER_BUILD=false git diff --check ``` ### Performance Added `BenchmarkWildcardBindClose` and compared the master base against this change with the same benchmark and build flags Alternating before/after execution order, 32 samples per version, Linux ARM64 with 6 vCPUs | Metric | Before | After | Comparison | | --- | --- | --- | --- | | Time | 1253 ns/op | 1275 ns/op | +1.76%, p=0.016 | | Allocation bytes | 4960 B/op | 4960 B/op | unchanged | | Allocations | 26 allocs/op | 26 allocs/op | unchanged | The benchmark measures a small bind/close overhead, with no additional allocations Each sample runs the built test binary with `-test.run='^$' -test.bench='^BenchmarkWildcardBindClose$' -test.benchmem -test.count=1`, followed by `benchstat` comparison These are local results, not a claim that GitHub CI has passed Auto-fixed-by: [🤖] @EtiennePerot FUTURE_COPYBARA_INTEGRATE_REVIEW=#16180 from akasakariko:fix-wildcard-bind-port-release 48e209b PiperOrigin-RevId: 1013927052
Fixes #16102 ### Compatibility `COMPATIBILITY:NEW-APP-CONFIG` — fixes source-port exhaustion in the repeated wildcard-bound connections used by the BusyBox `nc` healthchecks reported in #16102 ### Problem and fix A wildcard bind reserves its port using the wildcard address, but connecting replaces the endpoint local address with the connected address A dual-stack wildcard bind also reserves both IPv6 and IPv4, while connecting to an IPv4-mapped peer changes `effectiveNetProtos` to IPv4 and releases the IPv4 reservation Cleanup must therefore use the reservation address and the network protocols still owned by the endpoint, rather than the connected address and effective protocols - Record `boundAddr` and `boundNetProtos` for explicit binds, implicit connect reservations and accepted tuples - Release the IPv4 reservation with its complete metadata and retain the IPv6 reservation for close - Use the recorded reservation in both close and cleanup paths, with `checklocks` annotations on the new fields Rebased onto `0bbcbf9de7626fcecba97f37723dce80b435d73d` ### Regression coverage Adapted the syscall test suggested by @tamird into `SocketInetLoopback.WildcardBoundPortReleasedOnClose`, covering IPv4 wildcard and dual-stack wildcard connections to an IPv4 peer The test uses an abortive close to avoid TIME_WAIT and verifies that both IPv4 and IPv6-only wildcard binds can reuse the port The review reproducer passes on Linux and fails on the previous PR implementation with `EADDRINUSE` for the IPv6-only bind The updated test passes on both Linux and gVisor, replacing the duplicate Go behavior test ### Local validation Linux ARM64 in a Colima-backed Ubuntu container, Bazel 8.8.1 - Focused regression on native Linux and gVisor systrap — passed - Full `socket_inet_loopback` suite on native Linux and gVisor systrap — passed, 8 shards each - TCP and ports package tests — all 28 targets passed, including nogo and checklocks analysis for ARM64 and AMD64 - Ports, TCP and dual-stack tests with race detection and lockdep enabled together — all 3 targets passed - `make lint` — passed - `make lint-cc` — passed with the compilation database scoped to the affected syscall target and its 16 source files - `git diff --check` — passed ```sh bazel test $common --test_output=errors --test_sharding_strategy=disabled \ --test_filter=SocketInetLoopback.WildcardBoundPortReleasedOnClose \ //test/syscalls:socket_inet_loopback_test_native make syscall-tests DOCKER_BUILD=false BAZEL_OPTIONS="$common" \ TARGETS=//test/syscalls:socket_inet_loopback_test_runsc_systrap_shared \ OPTIONS="--test_sharding_strategy=disabled --test_filter=SocketInetLoopback.WildcardBoundPortReleasedOnClose" bazel test $common --test_output=errors \ //pkg/tcpip/ports/... //pkg/tcpip/transport/tcp/... make syscall-tests DOCKER_BUILD=false BAZEL_OPTIONS="$common" \ TARGETS=//test/syscalls:socket_inet_loopback_test_native make syscall-tests DOCKER_BUILD=false BAZEL_OPTIONS="$common" \ TARGETS=//test/syscalls:socket_inet_loopback_test_runsc_systrap_shared bazel test $common --config=race --config=lockdep --test_output=errors \ //pkg/tcpip/ports:ports_test //pkg/tcpip/transport/tcp:tcp_test \ //pkg/tcpip/transport/tcp/test/e2e:dual_stack_test make lint DOCKER_BUILD=false BAZEL_OPTIONS="$common" python3 tools/gen_compile_commands.py //test/syscalls/linux:socket_inet_loopback_test make lint-cc DOCKER_BUILD=false git diff --check ``` ### Performance Added `BenchmarkWildcardBindClose` and compared the master base against this change with the same benchmark and build flags Alternating before/after execution order, 32 samples per version, Linux ARM64 with 6 vCPUs | Metric | Before | After | Comparison | | --- | --- | --- | --- | | Time | 1253 ns/op | 1275 ns/op | +1.76%, p=0.016 | | Allocation bytes | 4960 B/op | 4960 B/op | unchanged | | Allocations | 26 allocs/op | 26 allocs/op | unchanged | The benchmark measures a small bind/close overhead, with no additional allocations Each sample runs the built test binary with `-test.run='^$' -test.bench='^BenchmarkWildcardBindClose$' -test.benchmem -test.count=1`, followed by `benchstat` comparison These are local results, not a claim that GitHub CI has passed Auto-fixed-by: [🤖] @EtiennePerot FUTURE_COPYBARA_INTEGRATE_REVIEW=#16180 from akasakariko:fix-wildcard-bind-port-release 48e209b PiperOrigin-RevId: 1013927052
Fixes #16102
Compatibility
COMPATIBILITY:NEW-APP-CONFIG— fixes source-port exhaustion in the repeated wildcard-bound connections used by the BusyBoxnchealthchecks reported in #16102Problem and fix
A wildcard bind reserves its port using the wildcard address, but connecting replaces the endpoint local address with the connected address
A dual-stack wildcard bind also reserves both IPv6 and IPv4, while connecting to an IPv4-mapped peer changes
effectiveNetProtosto IPv4 and releases the IPv4 reservationCleanup must therefore use the reservation address and the network protocols still owned by the endpoint, rather than the connected address and effective protocols
boundAddrandboundNetProtosfor explicit binds, implicit connect reservations and accepted tupleschecklocksannotations on the new fieldsRebased onto
0bbcbf9de7626fcecba97f37723dce80b435d73dRegression coverage
Adapted the syscall test suggested by @tamird into
SocketInetLoopback.WildcardBoundPortReleasedOnClose, covering IPv4 wildcard and dual-stack wildcard connections to an IPv4 peerThe test uses an abortive close to avoid TIME_WAIT and verifies that both IPv4 and IPv6-only wildcard binds can reuse the port
The review reproducer passes on Linux and fails on the previous PR implementation with
EADDRINUSEfor the IPv6-only bindThe updated test passes on both Linux and gVisor, replacing the duplicate Go behavior test
Local validation
Linux ARM64 in a Colima-backed Ubuntu container, Bazel 8.8.1
socket_inet_loopbacksuite on native Linux and gVisor systrap — passed, 8 shards eachmake lint— passedmake lint-cc— passed with the compilation database scoped to the affected syscall target and its 16 source filesgit diff --check— passedPerformance
Added
BenchmarkWildcardBindCloseand compared the master base against this change with the same benchmark and build flagsAlternating before/after execution order, 32 samples per version, Linux ARM64 with 6 vCPUs
The benchmark measures a small bind/close overhead, with no additional allocations
Each sample runs the built test binary with
-test.run='^$' -test.bench='^BenchmarkWildcardBindClose$' -test.benchmem -test.count=1, followed bybenchstatcomparisonThese are local results, not a claim that GitHub CI has passed