From 214984901aaf50cc52eebeabf14017edeb3a845f Mon Sep 17 00:00:00 2001 From: Peter Krystad Date: Tue, 21 Jan 2020 16:56:21 -0800 Subject: mptcp: Add shutdown() socket operation Call shutdown on all subflows in use on the given socket, or on the fallback socket. Co-developed-by: Florian Westphal Signed-off-by: Florian Westphal Signed-off-by: Peter Krystad Signed-off-by: Christoph Paasch Signed-off-by: David S. Miller --- net/mptcp/protocol.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'net/mptcp') diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 3f66b6a3bb28..249b2506a66a 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -196,6 +196,29 @@ static int mptcp_init_sock(struct sock *sk) return 0; } +static void mptcp_subflow_shutdown(struct sock *ssk, int how) +{ + lock_sock(ssk); + + switch (ssk->sk_state) { + case TCP_LISTEN: + if (!(how & RCV_SHUTDOWN)) + break; + /* fall through */ + case TCP_SYN_SENT: + tcp_disconnect(ssk, O_NONBLOCK); + break; + default: + ssk->sk_shutdown |= how; + tcp_shutdown(ssk, how); + break; + } + + /* Wake up anyone sleeping in poll. */ + ssk->sk_state_change(ssk); + release_sock(ssk); +} + static void mptcp_close(struct sock *sk, long timeout) { struct mptcp_subflow_context *subflow, *tmp; @@ -273,6 +296,7 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err, *err = -ENOBUFS; local_bh_enable(); release_sock(sk); + mptcp_subflow_shutdown(newsk, SHUT_RDWR + 1); tcp_close(newsk, 0); return NULL; } @@ -544,6 +568,46 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock, return mask; } +static int mptcp_shutdown(struct socket *sock, int how) +{ + struct mptcp_sock *msk = mptcp_sk(sock->sk); + struct mptcp_subflow_context *subflow; + int ret = 0; + + pr_debug("sk=%p, how=%d", msk, how); + + lock_sock(sock->sk); + + if (how == SHUT_WR || how == SHUT_RDWR) + inet_sk_state_store(sock->sk, TCP_FIN_WAIT1); + + how++; + + if ((how & ~SHUTDOWN_MASK) || !how) { + ret = -EINVAL; + goto out_unlock; + } + + if (sock->state == SS_CONNECTING) { + if ((1 << sock->sk->sk_state) & + (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_CLOSE)) + sock->state = SS_DISCONNECTING; + else + sock->state = SS_CONNECTED; + } + + mptcp_for_each_subflow(msk, subflow) { + struct sock *tcp_sk = mptcp_subflow_tcp_sock(subflow); + + mptcp_subflow_shutdown(tcp_sk, how); + } + +out_unlock: + release_sock(sock->sk); + + return ret; +} + static struct proto_ops mptcp_stream_ops; static struct inet_protosw mptcp_protosw = { @@ -564,6 +628,7 @@ void __init mptcp_init(void) mptcp_stream_ops.accept = mptcp_stream_accept; mptcp_stream_ops.getname = mptcp_v4_getname; mptcp_stream_ops.listen = mptcp_listen; + mptcp_stream_ops.shutdown = mptcp_shutdown; mptcp_subflow_init(); @@ -613,6 +678,7 @@ int mptcpv6_init(void) mptcp_v6_stream_ops.accept = mptcp_stream_accept; mptcp_v6_stream_ops.getname = mptcp_v6_getname; mptcp_v6_stream_ops.listen = mptcp_listen; + mptcp_v6_stream_ops.shutdown = mptcp_shutdown; err = inet6_register_protosw(&mptcp_v6_protosw); if (err) -- cgit v1.2.3