]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Improve the source port selection on Linux
authorOndřej Surý <ondrej@isc.org>
Thu, 24 Jul 2025 09:24:24 +0000 (11:24 +0200)
committerOndřej Surý <ondrej@isc.org>
Fri, 20 Feb 2026 15:18:39 +0000 (16:18 +0100)
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.

(cherry picked from commit 2c48fcaeedbe29205c0e0283a0d13aa9a2161280)

lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/socket.c
lib/isc/netmgr/tcp.c

index e6c6e82830a492f9e29f96c1d525478031f74d4e..85c96a8dea7c34999da2380f79f6899e6b1f261e 100644 (file)
@@ -1373,6 +1373,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(isc_nm_t *nm, uv_handle_t *handle);
 /*%>
index 4b71e9dbfd188d18f66f07bcbb38e93fcf0b833b..d75beb97b6a2bdffa2aaa48914b21d4a5751d5ee 100644 (file)
@@ -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;
+}
index 0ddbef56e21a60d85109e633dcec898d19914c25..b36a1eb218027eb90d6a2bf8d3da9dea5ab43b4b 100644 (file)
@@ -109,6 +109,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) {