[UDP]: Do not allow specific bind when wildcard bind exists.
authorDavid S. Miller <davem@sunset.davemloft.net>
Mon, 30 Apr 2007 21:51:58 +0000 (14:51 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Mon, 30 Apr 2007 21:51:58 +0000 (14:51 -0700)
When allocating local ports, do not allow a bind to a port
with a specific local address when a bind to that port with
a wildcard local address already exists.

Noticed by Linus.

Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/udp.c

index db313d96488489de2e8c0d2100176e0ab893e171..113e0c4c8a928157d51f17abcd47804106ebdeca 100644 (file)
@@ -203,6 +203,13 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
                                result = sysctl_local_port_range[0]
                                        + ((result - sysctl_local_port_range[0]) &
                                           (UDP_HTABLE_SIZE - 1));
+                       hash = hash_port_and_addr(result, 0);
+                       if (__udp_lib_port_inuse(hash, result,
+                                                0, udptable))
+                               continue;
+                       if (!inet_sk(sk)->rcv_saddr)
+                               break;
+
                        hash = hash_port_and_addr(result,
                                        inet_sk(sk)->rcv_saddr);
                        if (! __udp_lib_port_inuse(hash, result,
@@ -214,18 +221,36 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
 gotit:
                *port_rover = snum = result;
        } else {
-               hash = hash_port_and_addr(snum, inet_sk(sk)->rcv_saddr);
+               hash = hash_port_and_addr(snum, 0);
                head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
 
                sk_for_each(sk2, node, head)
-                       if (sk2->sk_hash == hash                             &&
-                           sk2 != sk                                        &&
-                           inet_sk(sk2)->num == snum                        &&
-                           (!sk2->sk_reuse        || !sk->sk_reuse)         &&
-                           (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
-                            || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
-                           (*saddr_comp)(sk, sk2)                             )
+                       if (sk2->sk_hash == hash &&
+                           sk2 != sk &&
+                           inet_sk(sk2)->num == snum &&
+                           (!sk2->sk_reuse || !sk->sk_reuse) &&
+                           (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
+                            sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+                           (*saddr_comp)(sk, sk2))
                                goto fail;
+
+               if (inet_sk(sk)->rcv_saddr) {
+                       hash = hash_port_and_addr(snum,
+                                                 inet_sk(sk)->rcv_saddr);
+                       head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
+
+                       sk_for_each(sk2, node, head)
+                               if (sk2->sk_hash == hash &&
+                                   sk2 != sk &&
+                                   inet_sk(sk2)->num == snum &&
+                                   (!sk2->sk_reuse || !sk->sk_reuse) &&
+                                   (!sk2->sk_bound_dev_if ||
+                                    !sk->sk_bound_dev_if ||
+                                    sk2->sk_bound_dev_if ==
+                                    sk->sk_bound_dev_if) &&
+                                   (*saddr_comp)(sk, sk2))
+                                       goto fail;
+               }
        }
        inet_sk(sk)->num = snum;
        sk->sk_hash = hash;