Thread (4 messages) 4 messages, 2 authors, 2024-06-07

Re: SCTP doesn't seem to let you 'cancel' a blocking accept()

From: Xin Long <lucien.xin@gmail.com>
Date: 2024-06-06 20:15:40
Also in: linux-sctp
Subsystem: networking [general], sctp protocol, the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Marcelo Ricardo Leitner, Xin Long, Linus Torvalds

On Mon, Jun 3, 2024 at 11:42 AM David Laight [off-list ref] wrote:
In a multithreaded program it is reasonable to have a thread blocked in accept().
With TCP a subsequent shutdown(listen_fd, SHUT_RDWR) causes the accept to fail.
But nothing happens for SCTP.

I think the 'magic' happens when tcp_disconnect() calls inet_csk_listen_stop(sk)
but sctp_disconnect() is an empty function and nothing happens.

I can't see any calls to inet_csk_listen_stop() in the sctp code - so I suspect
it isn't possible at all.
I guess SCTP doesn't take action due to the description
in rfc6458#section-4.1.7:

      SHUT_RD:  Disables further receive operations.  No SCTP protocol
         action is taken.
This all relates to a very old (pre git) comment in inet_shutdown() that
shutdown needs to act on listening and connecting sockets until the VFS
layer is 'fixed' (presumably to let close() through - not going to happen.)
didn't know that, it's better to have it on some standard doc.
I also suspect that a blocking connect() can't be cancelled either?
For connecting socket, it calls sctp_shutdown() where SHUT_WR causes
the asoc to enter SHUTDOWN_SENT and cancel the blocking connect().
Clearly the application can avoid the issue by using poll() and an
extra eventfd() for the wakeup - but it is all a faff for code that
otherwise straight forward.
I will try to prepare a patch to solve this for sctp accept() like:
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index c67679a41044..f270a0a4c65d 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4834,10 +4834,13 @@ int sctp_inet_connect(struct socket *sock,
struct sockaddr *uaddr,
        return sctp_connect(sock->sk, uaddr, addr_len, flags);
 }

-/* FIXME: Write comments. */
 static int sctp_disconnect(struct sock *sk, int flags)
 {
-       return -EOPNOTSUPP; /* STUB */
+       if (!sctp_style(sk, TCP))
+               return -EOPNOTSUPP;
+
+       sk->sk_shutdown |= RCV_SHUTDOWN;
+       return 0;
 }

 /* 4.1.4 accept() - TCP Style Syntax
@@ -4866,7 +4869,7 @@ static struct sock *sctp_accept(struct sock *sk,
int flags, int *err, bool kern)
                goto out;
        }

-       if (!sctp_sstate(sk, LISTENING)) {
+       if (!sctp_sstate(sk, LISTENING) || (sk->sk_shutdown & RCV_SHUTDOWN)) {
                error = -EINVAL;
                goto out;
        }
@@ -9392,7 +9395,7 @@ static int sctp_wait_for_accept(struct sock *sk,
long timeo)
                }

                err = -EINVAL;
-               if (!sctp_sstate(sk, LISTENING))
+               if (!sctp_sstate(sk, LISTENING) || (sk->sk_shutdown &
RCV_SHUTDOWN))
                        break;

                err = 0;

Thanks.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help