Skip to content

Commit acdcecc

Browse files
wdebruijdavem330
authored andcommitted
udp: correct reuseport selection with connected sockets
UDP reuseport groups can hold a mix unconnected and connected sockets. Ensure that connections only receive all traffic to their 4-tuple. Fast reuseport returns on the first reuseport match on the assumption that all matches are equal. Only if connections are present, return to the previous behavior of scoring all sockets. Record if connections are present and if so (1) treat such connected sockets as an independent match from the group, (2) only return 2-tuple matches from reuseport and (3) do not return on the first 2-tuple reuseport match to allow for a higher scoring match later. New field has_conns is set without locks. No other fields in the bitmap are modified at runtime and the field is only ever set unconditionally, so an RMW cannot miss a change. Fixes: e32ea7e ("soreuseport: fast reuseport UDP socket selection") Link: http://lkml.kernel.org/r/CA+FuTSfRP09aJNYRt04SS6qj22ViiOEWaWmLAwX0psk8-PGNxw@mail.gmail.com Signed-off-by: Willem de Bruijn <willemb@google.com> Acked-by: Paolo Abeni <pabeni@redhat.com> Acked-by: Craig Gallek <kraig@google.com> Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 05a8248 commit acdcecc

File tree

6 files changed

+42
-7
lines changed

6 files changed

+42
-7
lines changed

include/net/sock_reuseport.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ struct sock_reuseport {
2121
unsigned int synq_overflow_ts;
2222
/* ID stays the same even after the size of socks[] grows. */
2323
unsigned int reuseport_id;
24-
bool bind_inany;
24+
unsigned int bind_inany:1;
25+
unsigned int has_conns:1;
2526
struct bpf_prog __rcu *prog; /* optional BPF sock selector */
2627
struct sock *socks[0]; /* array of sock pointers */
2728
};
@@ -37,6 +38,23 @@ extern struct sock *reuseport_select_sock(struct sock *sk,
3738
extern int reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog);
3839
extern int reuseport_detach_prog(struct sock *sk);
3940

41+
static inline bool reuseport_has_conns(struct sock *sk, bool set)
42+
{
43+
struct sock_reuseport *reuse;
44+
bool ret = false;
45+
46+
rcu_read_lock();
47+
reuse = rcu_dereference(sk->sk_reuseport_cb);
48+
if (reuse) {
49+
if (set)
50+
reuse->has_conns = 1;
51+
ret = reuse->has_conns;
52+
}
53+
rcu_read_unlock();
54+
55+
return ret;
56+
}
57+
4058
int reuseport_get_id(struct sock_reuseport *reuse);
4159

4260
#endif /* _SOCK_REUSEPORT_H */

net/core/sock_reuseport.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,19 @@ struct sock *reuseport_select_sock(struct sock *sk,
295295

296296
select_by_hash:
297297
/* no bpf or invalid bpf result: fall back to hash usage */
298-
if (!sk2)
299-
sk2 = reuse->socks[reciprocal_scale(hash, socks)];
298+
if (!sk2) {
299+
int i, j;
300+
301+
i = j = reciprocal_scale(hash, socks);
302+
while (reuse->socks[i]->sk_state == TCP_ESTABLISHED) {
303+
i++;
304+
if (i >= reuse->num_socks)
305+
i = 0;
306+
if (i == j)
307+
goto out;
308+
}
309+
sk2 = reuse->socks[i];
310+
}
300311
}
301312

302313
out:

net/ipv4/datagram.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <net/sock.h>
1616
#include <net/route.h>
1717
#include <net/tcp_states.h>
18+
#include <net/sock_reuseport.h>
1819

1920
int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
2021
{
@@ -69,6 +70,7 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
6970
}
7071
inet->inet_daddr = fl4->daddr;
7172
inet->inet_dport = usin->sin_port;
73+
reuseport_has_conns(sk, true);
7274
sk->sk_state = TCP_ESTABLISHED;
7375
sk_set_txhash(sk);
7476
inet->inet_id = jiffies;

net/ipv4/udp.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,12 +423,13 @@ static struct sock *udp4_lib_lookup2(struct net *net,
423423
score = compute_score(sk, net, saddr, sport,
424424
daddr, hnum, dif, sdif);
425425
if (score > badness) {
426-
if (sk->sk_reuseport) {
426+
if (sk->sk_reuseport &&
427+
sk->sk_state != TCP_ESTABLISHED) {
427428
hash = udp_ehashfn(net, daddr, hnum,
428429
saddr, sport);
429430
result = reuseport_select_sock(sk, hash, skb,
430431
sizeof(struct udphdr));
431-
if (result)
432+
if (result && !reuseport_has_conns(sk, false))
432433
return result;
433434
}
434435
badness = score;

net/ipv6/datagram.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <net/ip6_route.h>
2828
#include <net/tcp_states.h>
2929
#include <net/dsfield.h>
30+
#include <net/sock_reuseport.h>
3031

3132
#include <linux/errqueue.h>
3233
#include <linux/uaccess.h>
@@ -254,6 +255,7 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
254255
goto out;
255256
}
256257

258+
reuseport_has_conns(sk, true);
257259
sk->sk_state = TCP_ESTABLISHED;
258260
sk_set_txhash(sk);
259261
out:

net/ipv6/udp.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,14 @@ static struct sock *udp6_lib_lookup2(struct net *net,
158158
score = compute_score(sk, net, saddr, sport,
159159
daddr, hnum, dif, sdif);
160160
if (score > badness) {
161-
if (sk->sk_reuseport) {
161+
if (sk->sk_reuseport &&
162+
sk->sk_state != TCP_ESTABLISHED) {
162163
hash = udp6_ehashfn(net, daddr, hnum,
163164
saddr, sport);
164165

165166
result = reuseport_select_sock(sk, hash, skb,
166167
sizeof(struct udphdr));
167-
if (result)
168+
if (result && !reuseport_has_conns(sk, false))
168169
return result;
169170
}
170171
result = sk;

0 commit comments

Comments
 (0)