| From foo@baz Sun 01 Mar 2020 10:24:06 AM CET |
| From: Willem de Bruijn <willemb@google.com> |
| Date: Wed, 19 Feb 2020 14:16:32 -0500 |
| Subject: udp: rehash on disconnect |
| |
| From: Willem de Bruijn <willemb@google.com> |
| |
| [ Upstream commit 303d0403b8c25e994e4a6e45389e173cf8706fb5 ] |
| |
| As of the below commit, udp sockets bound to a specific address can |
| coexist with one bound to the any addr for the same port. |
| |
| The commit also phased out the use of socket hashing based only on |
| port (hslot), in favor of always hashing on {addr, port} (hslot2). |
| |
| The change broke the following behavior with disconnect (AF_UNSPEC): |
| |
| server binds to 0.0.0.0:1337 |
| server connects to 127.0.0.1:80 |
| server disconnects |
| client connects to 127.0.0.1:1337 |
| client sends "hello" |
| server reads "hello" // times out, packet did not find sk |
| |
| On connect the server acquires a specific source addr suitable for |
| routing to its destination. On disconnect it reverts to the any addr. |
| |
| The connect call triggers a rehash to a different hslot2. On |
| disconnect, add the same to return to the original hslot2. |
| |
| Skip this step if the socket is going to be unhashed completely. |
| |
| Fixes: 4cdeeee9252a ("net: udp: prefer listeners bound to an address") |
| Reported-by: Pavel Roskin <plroskin@gmail.com> |
| Signed-off-by: Willem de Bruijn <willemb@google.com> |
| Reviewed-by: Eric Dumazet <edumazet@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/udp.c | 6 +++++- |
| 1 file changed, 5 insertions(+), 1 deletion(-) |
| |
| --- a/net/ipv4/udp.c |
| +++ b/net/ipv4/udp.c |
| @@ -1856,8 +1856,12 @@ int __udp_disconnect(struct sock *sk, in |
| inet->inet_dport = 0; |
| sock_rps_reset_rxhash(sk); |
| sk->sk_bound_dev_if = 0; |
| - if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) |
| + if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) { |
| inet_reset_saddr(sk); |
| + if (sk->sk_prot->rehash && |
| + (sk->sk_userlocks & SOCK_BINDPORT_LOCK)) |
| + sk->sk_prot->rehash(sk); |
| + } |
| |
| if (!(sk->sk_userlocks & SOCK_BINDPORT_LOCK)) { |
| sk->sk_prot->unhash(sk); |