mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-31 10:41:08 +00:00
Merge bitcoin/bitcoin#34346: test: use IP_PORTRANGE_HIGH on FreeBSD for dynamic port allocation
2845f10a2be0fee13b2772d24e948052243782b8 test: extend FreeBSD ephemeral port range fix to P2P listeners (node) 34bed0ed8c449a3834927cec3447dbe6c74edf3d test: use IP_PORTRANGE_HIGH on FreeBSD for dynamic port allocation (woltx) Pull request description: Reopening #34336. I’ve now tested it on FreeBSD and confirmed it works. On FreeBSD, the default ephemeral port range (10000-65535) overlaps with the test framework's static port range (11000-26000), possibly causing intermittent "address already in use" failures when tests use dynamic port allocation (`port=0`). This PR adds a helper that sets `IP_PORTRANGE_HIGH` via `setsockopt()` before binding, requesting ports from 49152-65535 instead, which avoids the overlap, as suggested in https://github.com/bitcoin/bitcoin/issues/34331#issuecomment-3767161843 by @maflcko . From FreeBSD's [sys/netinet/in.h](https://cgit.freebsd.org/src/tree/sys/netinet/in.h): ```c #define IP_PORTRANGE 19 #define IP_PORTRANGE_HIGH 1 #define IPPORT_EPHEMERALFIRST 10000 /* default range start */ #define IPPORT_HIFIRSTAUTO 49152 /* high range start */ ``` See also: FreeBSD https://man.freebsd.org/cgi/man.cgi?query=ip&sektion=4 man page. Fixes #34331 ACKs for top commit: vasild: ACK 2845f10a2be0fee13b2772d24e948052243782b8 hebasto: ACK 2845f10a2be0fee13b2772d24e948052243782b8, I have reviewed the code and it looks OK. Tree-SHA512: ce501ce3e8a4023e07bad572df2b85d6829becf133813e4529aebba83e4eba59fa8b48e9d2197ebbb226adaf3054fad720775a787244d6b38c0078ee086102f4
This commit is contained in:
commit
f7e0c3d3d3
@ -181,3 +181,22 @@ def format_addr_port(addr, port):
|
|||||||
return f"[{addr}]:{port}"
|
return f"[{addr}]:{port}"
|
||||||
else:
|
else:
|
||||||
return f"{addr}:{port}"
|
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)
|
||||||
|
|||||||
@ -22,9 +22,11 @@ P2PTxInvStore: A p2p interface class that inherits from P2PDataStore, and keeps
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import ipaddress
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@ -76,6 +78,9 @@ from test_framework.messages import (
|
|||||||
MAGIC_BYTES,
|
MAGIC_BYTES,
|
||||||
sha256,
|
sha256,
|
||||||
)
|
)
|
||||||
|
from test_framework.netutil import (
|
||||||
|
set_ephemeral_port_range,
|
||||||
|
)
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
assert_not_equal,
|
assert_not_equal,
|
||||||
MAX_NODES,
|
MAX_NODES,
|
||||||
@ -793,8 +798,22 @@ class NetworkThread(threading.Thread):
|
|||||||
# connections, we can accomplish this by providing different
|
# connections, we can accomplish this by providing different
|
||||||
# `proto` functions
|
# `proto` functions
|
||||||
|
|
||||||
listener = await cls.network_event_loop.create_server(peer_protocol, addr, port)
|
if port == 0:
|
||||||
port = listener.sockets[0].getsockname()[1]
|
# Manually create the socket in order to set the range to be
|
||||||
|
# used for the port before the bind() call.
|
||||||
|
if ipaddress.ip_address(addr).version == 4:
|
||||||
|
address_family = socket.AF_INET
|
||||||
|
else:
|
||||||
|
address_family = socket.AF_INET6
|
||||||
|
s = socket.socket(address_family)
|
||||||
|
set_ephemeral_port_range(s)
|
||||||
|
s.bind((addr, 0))
|
||||||
|
s.listen()
|
||||||
|
listener = await cls.network_event_loop.create_server(peer_protocol, sock=s)
|
||||||
|
port = listener.sockets[0].getsockname()[1]
|
||||||
|
else:
|
||||||
|
listener = await cls.network_event_loop.create_server(peer_protocol, addr, port)
|
||||||
|
|
||||||
logger.debug("Listening server on %s:%d should be started" % (addr, port))
|
logger.debug("Listening server on %s:%d should be started" % (addr, port))
|
||||||
cls.listeners[(addr, port)] = listener
|
cls.listeners[(addr, port)] = listener
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,8 @@ import queue
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .netutil import (
|
from .netutil import (
|
||||||
format_addr_port
|
format_addr_port,
|
||||||
|
set_ephemeral_port_range,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger("TestFramework.socks5")
|
logger = logging.getLogger("TestFramework.socks5")
|
||||||
@ -202,6 +203,10 @@ class Socks5Server():
|
|||||||
self.conf = conf
|
self.conf = conf
|
||||||
self.s = socket.socket(conf.af)
|
self.s = socket.socket(conf.af)
|
||||||
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
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)
|
self.s.bind(conf.addr)
|
||||||
# When port=0, the OS assigns an available port. Update 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.
|
# to reflect the actual bound address so callers can use it.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user