Outgoing connections bound with IP_BIND_ADDRESS_NO_PORT

The socket of a connection to the destination is bound to the external
address before connect(), with port 0 unless a range is given. bind()
then takes an ephemeral port for the socket alone: not one in use
towards another destination, nor one of a connection still closing in
TIME_WAIT, which connect() would reuse. With many connections the range
runs out and bind() fails, result 12, or searches long for a port: 7 ms
a call was seen. The more connections are open at once, the sooner:
with event loops, serving more of them at once, the client of every
tenth connection or so of a saturated relay got its end at once.

Where the system has IP_BIND_ADDRESS_NO_PORT (Linux 4.2), the port is
left to connect() for a socket that is going to connect: chosen for
the pair with the destination, together with IP_LOCAL_PORT_RANGE when
a range is given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-25 16:33:39 +03:00
parent b56e5aff2b
commit aaa36f1dd1

View File

@ -780,6 +780,22 @@ int doconnect(struct clientparam * param){
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
#endif
if(extbindtodevice(param, param->remsock)) return 12;
#ifdef IP_BIND_ADDRESS_NO_PORT
/* A port the kernel chooses is chosen by connect(), for the pair with
the destination: one in use towards another destination, or closing
from an earlier connection, does as well. bind() would take one of
its own, and with many connections find none left, or search long
for it. */
if((param->operation >= 256 || (param->operation & CONNECT) || param->redirected)
# ifdef WITH_UN
&& *SAFAMILY(&param->sinsl) != AF_UNIX
# endif
){
int one = 1;
param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, (char *)&one, sizeof(one));
}
#endif
#ifdef WITH_UN
if(*SAFAMILY(&param->sinsl) != AF_UNIX)
#endif