tcp: disallow bind() to reuse addr/port
authorEric Dumazet <eric.dumazet@gmail.com>
Tue, 11 Jan 2011 01:14:22 +0000 (01:14 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 11 Jan 2011 22:03:07 +0000 (14:03 -0800)
inet_csk_bind_conflict() logic currently disallows a bind() if
it finds a friend socket (a socket bound on same address/port)
satisfying a set of conditions :

1) Current (to be bound) socket doesnt have sk_reuse set
OR
2) other socket doesnt have sk_reuse set
OR
3) other socket is in LISTEN state

We should add the CLOSE state in the 3) condition, in order to avoid two
REUSEADDR sockets in CLOSE state with same local address/port, since
this can deny further operations.

Note : a prior patch tried to address the problem in a different (and
buggy) way. (commit fda48a0d7a8412ced tcp: bind() fix when many ports
are bound).

Reported-by: Gaspar Chilingarov <gasparch@gmail.com>
Reported-by: Daniel Baluta <daniel.baluta@gmail.com>
Tested-by: Daniel Baluta <daniel.baluta@gmail.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/inet_connection_sock.c
net/ipv6/inet6_connection_sock.c

index 25e318153f143366894ec111dc5e6cb89d980c9b..97e5fb76526500d6355b921be6359680bf7a8720 100644 (file)
@@ -73,7 +73,7 @@ int inet_csk_bind_conflict(const struct sock *sk,
                     !sk2->sk_bound_dev_if ||
                     sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) {
                        if (!reuse || !sk2->sk_reuse ||
-                           sk2->sk_state == TCP_LISTEN) {
+                           ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) {
                                const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);
                                if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) ||
                                    sk2_rcv_saddr == sk_rcv_saddr(sk))
@@ -122,7 +122,8 @@ again:
                                            (tb->num_owners < smallest_size || smallest_size == -1)) {
                                                smallest_size = tb->num_owners;
                                                smallest_rover = rover;
-                                               if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) {
+                                               if (atomic_read(&hashinfo->bsockets) > (high - low) + 1 &&
+                                                   !inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) {
                                                        spin_unlock(&head->lock);
                                                        snum = smallest_rover;
                                                        goto have_snum;
index e46305d1815ae38a3cb8282a24a165a52e2e2433..d144e629d2b43d1091800df484ca98ff590cc71d 100644 (file)
@@ -44,7 +44,7 @@ int inet6_csk_bind_conflict(const struct sock *sk,
                     !sk2->sk_bound_dev_if ||
                     sk->sk_bound_dev_if == sk2->sk_bound_dev_if) &&
                    (!sk->sk_reuse || !sk2->sk_reuse ||
-                    sk2->sk_state == TCP_LISTEN) &&
+                    ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) &&
                     ipv6_rcv_saddr_equal(sk, sk2))
                        break;
        }