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.