mptcp: fix unblocking connect()
authorPaolo Abeni <pabeni@redhat.com>
Fri, 29 May 2020 15:43:29 +0000 (17:43 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sun, 31 May 2020 04:39:13 +0000 (21:39 -0700)
Currently unblocking connect() on MPTCP sockets fails frequently.
If mptcp_stream_connect() is invoked to complete a previously
attempted unblocking connection, it will still try to create
the first subflow via __mptcp_socket_create(). If the 3whs is
completed and the 'can_ack' flag is already set, the latter
will fail with -EINVAL.

This change addresses the issue checking for pending connect and
delegating the completion to the first subflow. Additionally
do msk addresses and sk_state changes only when needed.

Fixes: 2303f994b3e1 ("mptcp: Associate MPTCP context with TCP socket")
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/mptcp/protocol.c

index c8675d2..8959a74 100644 (file)
@@ -1712,6 +1712,14 @@ static int mptcp_stream_connect(struct socket *sock, struct sockaddr *uaddr,
        int err;
 
        lock_sock(sock->sk);
+       if (sock->state != SS_UNCONNECTED && msk->subflow) {
+               /* pending connection or invalid state, let existing subflow
+                * cope with that
+                */
+               ssock = msk->subflow;
+               goto do_connect;
+       }
+
        ssock = __mptcp_socket_create(msk, TCP_SYN_SENT);
        if (IS_ERR(ssock)) {
                err = PTR_ERR(ssock);
@@ -1726,9 +1734,17 @@ static int mptcp_stream_connect(struct socket *sock, struct sockaddr *uaddr,
                mptcp_subflow_ctx(ssock->sk)->request_mptcp = 0;
 #endif
 
+do_connect:
        err = ssock->ops->connect(ssock, uaddr, addr_len, flags);
-       inet_sk_state_store(sock->sk, inet_sk_state_load(ssock->sk));
-       mptcp_copy_inaddrs(sock->sk, ssock->sk);
+       sock->state = ssock->state;
+
+       /* on successful connect, the msk state will be moved to established by
+        * subflow_finish_connect()
+        */
+       if (!err || err == EINPROGRESS)
+               mptcp_copy_inaddrs(sock->sk, ssock->sk);
+       else
+               inet_sk_state_store(sock->sk, inet_sk_state_load(ssock->sk));
 
 unlock:
        release_sock(sock->sk);