diff options
author | David S. Miller <davem@davemloft.net> | 2020-07-04 17:56:22 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-07-04 17:56:22 -0700 |
commit | bdd2ed272151d89f8dc81a5a4046e3f8273b970d (patch) | |
tree | a301f522c1a2dbe95775508b036e31dad3e26e76 | |
parent | f551e2fdaf81b7b561bb5dc590da13f21c4c1295 (diff) | |
parent | c9b95a13598750e2840d99322f844ec0ff9e6246 (diff) |
Merge branch 'mptcp-add-REUSEADDR-REUSEPORT-V6ONLY-setsockopt-support'
Florian Westphal says:
====================
mptcp: add REUSEADDR/REUSEPORT/V6ONLY setsockopt support
restarting an mptcp-patched sshd yields following error:
sshd: error: Bind to port 22 on 0.0.0.0 failed: Address already in use.
sshd: error: setsockopt IPV6_V6ONLY: Operation not supported
sshd: error: Bind to port 22 on :: failed: Address already in use.
sshd: fatal: Cannot bind any address.
This series adds support for the needed setsockopts:
First patch skips the generic SOL_SOCKET handler for MPTCP:
in mptcp case, the setsockopt needs to alter the tcp socket, not the mptcp
parent socket.
Second patch adds minimal SOL_SOCKET support: REUSEPORT and REUSEADDR.
Rest is still handled by the generic SOL_SOCKET code.
Last patch adds IPV6ONLY support. This makes ipv6 work for openssh:
It creates two listening sockets, before this patch, binding the ipv6
socket will fail because the port is already bound by the ipv4 one.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/mptcp/protocol.c | 64 | ||||
-rw-r--r-- | net/socket.c | 13 |
2 files changed, 76 insertions, 1 deletions
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index fa137a9c42d1..3ab060e30038 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1601,6 +1601,64 @@ static void mptcp_destroy(struct sock *sk) sk_sockets_allocated_dec(sk); } +static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname, + char __user *optval, unsigned int optlen) +{ + struct sock *sk = (struct sock *)msk; + struct socket *ssock; + int ret; + + switch (optname) { + case SO_REUSEPORT: + case SO_REUSEADDR: + lock_sock(sk); + ssock = __mptcp_nmpc_socket(msk); + if (!ssock) { + release_sock(sk); + return -EINVAL; + } + + ret = sock_setsockopt(ssock, SOL_SOCKET, optname, optval, optlen); + if (ret == 0) { + if (optname == SO_REUSEPORT) + sk->sk_reuseport = ssock->sk->sk_reuseport; + else if (optname == SO_REUSEADDR) + sk->sk_reuse = ssock->sk->sk_reuse; + } + release_sock(sk); + return ret; + } + + return sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, optval, optlen); +} + +static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname, + char __user *optval, unsigned int optlen) +{ + struct sock *sk = (struct sock *)msk; + int ret = -EOPNOTSUPP; + struct socket *ssock; + + switch (optname) { + case IPV6_V6ONLY: + lock_sock(sk); + ssock = __mptcp_nmpc_socket(msk); + if (!ssock) { + release_sock(sk); + return -EINVAL; + } + + ret = tcp_setsockopt(ssock->sk, SOL_IPV6, optname, optval, optlen); + if (ret == 0) + sk->sk_ipv6only = ssock->sk->sk_ipv6only; + + release_sock(sk); + break; + } + + return ret; +} + static int mptcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen) { @@ -1609,6 +1667,9 @@ static int mptcp_setsockopt(struct sock *sk, int level, int optname, pr_debug("msk=%p", msk); + if (level == SOL_SOCKET) + return mptcp_setsockopt_sol_socket(msk, optname, optval, optlen); + /* @@ the meaning of setsockopt() when the socket is connected and * there are multiple subflows is not yet defined. It is up to the * MPTCP-level socket to configure the subflows until the subflow @@ -1621,6 +1682,9 @@ static int mptcp_setsockopt(struct sock *sk, int level, int optname, if (ssk) return tcp_setsockopt(ssk, level, optname, optval, optlen); + if (level == SOL_IPV6) + return mptcp_setsockopt_v6(msk, optname, optval, optlen); + return -EOPNOTSUPP; } diff --git a/net/socket.c b/net/socket.c index 976426d03f09..d87812a9ed4b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2080,6 +2080,17 @@ SYSCALL_DEFINE4(recv, int, fd, void __user *, ubuf, size_t, size, return __sys_recvfrom(fd, ubuf, size, flags, NULL, NULL); } +static bool sock_use_custom_sol_socket(const struct socket *sock) +{ + const struct sock *sk = sock->sk; + + /* Use sock->ops->setsockopt() for MPTCP */ + return IS_ENABLED(CONFIG_MPTCP) && + sk->sk_protocol == IPPROTO_MPTCP && + sk->sk_type == SOCK_STREAM && + (sk->sk_family == AF_INET || sk->sk_family == AF_INET6); +} + /* * Set a socket option. Because we don't know the option lengths we have * to pass the user mode parameter for the protocols to sort out. @@ -2118,7 +2129,7 @@ static int __sys_setsockopt(int fd, int level, int optname, optval = (char __user __force *)kernel_optval; } - if (level == SOL_SOCKET) + if (level == SOL_SOCKET && !sock_use_custom_sol_socket(sock)) err = sock_setsockopt(sock, level, optname, optval, optlen); |