test: use IP_PORTRANGE_HIGH on FreeBSD for dynamic port allocation
What changed, and why it matters
This commit fixes a flaky test problem on FreeBSD. When Bitcoin Core's test software asked the operating system for any available network port, FreeBSD could hand back a port number that the test framework already planned to use elsewhere, causing 'address already in use' failures. The change tells FreeBSD to pick ports only from the high-numbered range (49152-65535), which does not overlap with the test framework's reserved ports. It is a test-only reliability improvement, not a security fix.
No security action needed. Treat as a normal test-framework reliability patch. FreeBSD CI/test runners may benefit from this change to reduce intermittent port-collision failures.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adds set_ephemeral_port_range() in test/functional/test_framework/netutil.py, which on FreeBSD sets IP_PORTRANGE/IPV6_PORTRANGE to IP_PORTRANGE_HIGH before binding to port 0. socks5.py now calls this helper when dynamic port allocation is used. This avoids collisions between FreeBSD’s default ephemeral range (10000-65535) and the test runner’s static port range starting at TEST_RUNNER_PORT_MIN (default 11000). The change is confined to the functional test framework and does not alter node networking code.
Changed components
test/functional/test_framework/netutil.pytest/functional/test_framework/socks5.pyInspect captured patch +25 / −1
diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py
index 75577b0c..5504029a 100644
--- a/test/functional/test_framework/netutil.py
+++ b/test/functional/test_framework/netutil.py
@@ -181,3 +181,22 @@ def format_addr_port(addr, port):
return f"[{addr}]:{port}"
else:
return f"{addr}:{port}"
+
+
+def set_ephemeral_port_range(sock):
+ '''On FreeBSD, set socket to use the high ephemeral port range (49152-65535).
+
+ FreeBSD's default ephemeral port range (10000-65535) overlaps with the test
+ framework's static port range starting at TEST_RUNNER_PORT_MIN (default=11000).
+ Using IP_PORTRANGE_HIGH avoids this overlap when binding to port 0 for dynamic
+ port allocation.
+ '''
+ if sys.platform.startswith('freebsd'):
+ # Constants from FreeBSD's netinet/in.h and netinet6/in6.h
+ IP_PORTRANGE = 19
+ IPV6_PORTRANGE = 14
+ IP_PORTRANGE_HIGH = 1 # Same value for both IPv4 and IPv6
+ if sock.family == socket.AF_INET6:
+ sock.setsockopt(socket.IPPROTO_IPV6, IPV6_PORTRANGE, IP_PORTRANGE_HIGH)
+ else:
+ sock.setsockopt(socket.IPPROTO_IP, IP_PORTRANGE, IP_PORTRANGE_HIGH)
diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py
index 711734cf..085c5a2e 100644
--- a/test/functional/test_framework/socks5.py
+++ b/test/functional/test_framework/socks5.py
@@ -11,7 +11,8 @@ import queue
import logging
from .netutil import (
- format_addr_port
+ format_addr_port,
+ set_ephemeral_port_range,
)
logger = logging.getLogger("TestFramework.socks5")
@@ -202,6 +203,10 @@ class Socks5Server():
self.conf = conf
self.s = socket.socket(conf.af)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ # When using dynamic port allocation (port=0), ensure we don't get a
+ # port that conflicts with the test framework's static port range.
+ if conf.addr[1] == 0:
+ set_ephemeral_port_range(self.s)
self.s.bind(conf.addr)
# When port=0, the OS assigns an available port. Update conf.addr
# to reflect the actual bound address so callers can use it.
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.