summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2016-12-17 11:13:20 -0500
committerDavid S. Miller <davem@davemloft.net>2016-12-17 11:13:20 -0500
commit28055c9710e7ab1479d018224be697f63eac2daa (patch)
treeddae48385faf13e6537d82b1f72cff8553456a9a /net
parent0eb6984f70005e792917d9e51142a57f79b32c91 (diff)
parent0643ee4fd1b79c1af3bd7bc8968dbf5fd047f490 (diff)
Merge branch 'inet_csk_get_port-and-soreusport-fixes'
Tom Herbert says: ==================== inet: Fixes for inet_csk_get_port and soreusport This patch set fixes a couple of issues I noticed while debugging our softlockup issue in inet_csk_get_port. - Don't allow jump into port scan in inet_csk_get_port if function was called with non-zero port number (looking up explicit port number). - When inet_csk_get_port is called with zero port number (ie. perform scan) an reuseport is set on the socket, don't match sockets that also have reuseport set. The intent from the user should be to get a new port number and then explictly bind other sockets to that number using soreuseport. Tested: Ran first patch on production workload with no ill effect. For second patch, ran a little listener application and first demonstrated that unbound sockets with soreuseport can indeed be bound to unrelated soreuseport sockets. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/inet_connection_sock.c16
-rw-r--r--net/ipv6/inet6_connection_sock.c7
2 files changed, 14 insertions, 9 deletions
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index d5d3ead0a6c3..19ea045c50ed 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -45,11 +45,12 @@ void inet_get_local_port_range(struct net *net, int *low, int *high)
EXPORT_SYMBOL(inet_get_local_port_range);
int inet_csk_bind_conflict(const struct sock *sk,
- const struct inet_bind_bucket *tb, bool relax)
+ const struct inet_bind_bucket *tb, bool relax,
+ bool reuseport_ok)
{
struct sock *sk2;
- int reuse = sk->sk_reuse;
- int reuseport = sk->sk_reuseport;
+ bool reuse = sk->sk_reuse;
+ bool reuseport = !!sk->sk_reuseport && reuseport_ok;
kuid_t uid = sock_i_uid((struct sock *)sk);
/*
@@ -105,6 +106,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
struct inet_bind_bucket *tb;
kuid_t uid = sock_i_uid(sk);
u32 remaining, offset;
+ bool reuseport_ok = !!snum;
if (port) {
have_port:
@@ -165,7 +167,8 @@ other_parity_scan:
smallest_size = tb->num_owners;
smallest_port = port;
}
- if (!inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, false))
+ if (!inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, false,
+ reuseport_ok))
goto tb_found;
goto next_port;
}
@@ -206,13 +209,14 @@ tb_found:
sk->sk_reuseport && uid_eq(tb->fastuid, uid))) &&
smallest_size == -1)
goto success;
- if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, true)) {
+ if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, true,
+ reuseport_ok)) {
if ((reuse ||
(tb->fastreuseport > 0 &&
sk->sk_reuseport &&
!rcu_access_pointer(sk->sk_reuseport_cb) &&
uid_eq(tb->fastuid, uid))) &&
- smallest_size != -1 && --attempts >= 0) {
+ !snum && smallest_size != -1 && --attempts >= 0) {
spin_unlock_bh(&head->lock);
goto again;
}
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 1c86c478f578..7396e75e161b 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -29,11 +29,12 @@
#include <net/sock_reuseport.h>
int inet6_csk_bind_conflict(const struct sock *sk,
- const struct inet_bind_bucket *tb, bool relax)
+ const struct inet_bind_bucket *tb, bool relax,
+ bool reuseport_ok)
{
const struct sock *sk2;
- int reuse = sk->sk_reuse;
- int reuseport = sk->sk_reuseport;
+ bool reuse = !!sk->sk_reuse;
+ bool reuseport = !!sk->sk_reuseport && reuseport_ok;
kuid_t uid = sock_i_uid((struct sock *)sk);
/* We must walk the whole port owner list in this case. -DaveM */