diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py index 75577b0ca84..5504029a766 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 711734cf98e..085c5a2e324 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.