The connect() routine returns -EADDRNOTAVAIL without doing a 4 tuple check when the hash buckets were previously allocated by bind() and all local ports are used.
The bind() routine creates the local port hash buckets in inet_csk_get_port(). Depending on the socket options it sets tb->fastreuse and tb->fastreuseport to 0 or 1 in the bucket. However the __inet_hash_connect() routine initializes the hash buckets differently and sets these to -1. The end result is that connect() calling into __inet_hash_connect() will subsequently ignore the check_established() routine if, here __inet_hash_connect() . . if (tb->fastreuse >= 0 ||↩ tb->fastreuseport >= 0)↩ goto next_port; and cycle through all local ports until it returns -EADDRNOTAVAIL. The 4 tuple check is in check_established() so connect() can fail unnecessarily. Prerequisites for this to happen: 1) The local tcp port range must be exhausted. 2) A process must have called bind() followed by connect() for all local ports. 3) A different process calls connect() only which returns -EADDRNOTAVAIL. 4) The system more than 1 interface configured. If a system has 2 IP Addresses and all local tcp ports are in use for connection from IP Address (1). Connecting to the same ports via IP Address (2) should work based on the 4 tuple rule. But it fails under this condition. To fix this make __inet_hash_connect() honour inet_csk_get_port()'s tb->fastreuse* variables. Signed-off-by: Jon Maxwell <jmaxwel...@gmail.com> --- net/ipv4/inet_hashtables.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 9111a4e..b39e89e 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -513,8 +513,8 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, inet_bind_bucket_for_each(tb, &head->chain) { if (net_eq(ib_net(tb), net) && tb->port == port) { - if (tb->fastreuse >= 0 || - tb->fastreuseport >= 0) + if (tb->fastreuse > 0 || + tb->fastreuseport > 0) goto next_port; WARN_ON(hlist_empty(&tb->owners)); if (!check_established(death_row, sk, @@ -530,8 +530,6 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, spin_unlock(&head->lock); break; } - tb->fastreuse = -1; - tb->fastreuseport = -1; goto ok; next_port: -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/