From: Ondřej Surý Date: Thu, 24 Jul 2025 09:24:24 +0000 (+0200) Subject: Improve the source port selection on Linux X-Git-Tag: v9.21.19~16^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2c48fcaeedbe29205c0e0283a0d13aa9a2161280;p=thirdparty%2Fbind9.git Improve the source port selection on Linux Since 2015, Linux has introduced a new socket option to overcome TCP limitations: When an application needs to force a source IP on an active TCP socket it has to use bind(IP, port=x). As most applications do not want to deal with already used ports, x is often set to 0, meaning the kernel is in charge to find an available port. But kernel does not know yet if this socket is going to be a listener or be connected. This IP_BIND_ADDRESS_NO_PORT socket option ask the kernel to ignore the 0 port provided by application in bind(IP, port=0) and only remember the given IP address. The port will be automatically chosen at connect() time, in a way that allows sharing a source port as long as the 4-tuples are unique. Enable IP_BIND_ADDRESS_NO_PORT on the outgoing TCP sockets to overcome this TCP limitation. --- diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 04e2a398c17..9ea93e089f6 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -1386,6 +1386,12 @@ isc__nm_socket_min_mtu(uv_os_sock_t fd, sa_family_t sa_family); * Use minimum MTU on IPv6 sockets */ +isc_result_t +isc__nm_tcp_bind_no_port(uv_tcp_t *handle); +/*%< + * Set IP_BIND_ADDRESS_NO_PORT on the socket (Linux only). + */ + void isc__nm_set_network_buffers(uv_handle_t *handle); /*%> diff --git a/lib/isc/netmgr/socket.c b/lib/isc/netmgr/socket.c index 4b71e9dbfd1..d75beb97b6a 100644 --- a/lib/isc/netmgr/socket.c +++ b/lib/isc/netmgr/socket.c @@ -369,3 +369,20 @@ isc__nm_socket_min_mtu(uv_os_sock_t fd, sa_family_t sa_family) { return ISC_R_SUCCESS; } + +isc_result_t +isc__nm_tcp_bind_no_port(uv_tcp_t *handle ISC_ATTR_UNUSED) { +#ifdef IP_BIND_ADDRESS_NO_PORT + uv_os_sock_t fd = -1; + + int r = uv_fileno((const uv_handle_t *)handle, (uv_os_fd_t *)&fd); + if (r < 0) { + return ISC_R_FAILURE; + } + + if (setsockopt_on(fd, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT) == -1) { + return ISC_R_FAILURE; + } +#endif + return ISC_R_SUCCESS; +} diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 84e57693bbb..7dd7184cfea 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -141,6 +141,8 @@ tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { } isc__nm_incstats(sock, STATID_OPEN); + isc__nm_tcp_bind_no_port(&sock->uv_handle.tcp); + if (req->local.length != 0) { r = uv_tcp_bind(&sock->uv_handle.tcp, &req->local.type.sa, 0); if (r != 0) {