dcsctp: Ignore shutdown API while shutting down According to RFC 9260 Section 9.2, once an endpoint has reached the SHUTDOWN-RECEIVED state, it MUST ignore ULP shutdown requests. This CL ensures that Shutdown() is ignored if the socket is in any of the shutdown states (Sent, AckSent, Received, or Pending). Bug: webrtc:500087337 Change-Id: I58459c6de1725a62f672ed2d47a1e68c5709416e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/462020 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Victor Boivie <boivie@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47367}
diff --git a/net/dcsctp/socket/dcsctp_socket.cc b/net/dcsctp/socket/dcsctp_socket.cc index cc883ba..514782f 100644 --- a/net/dcsctp/socket/dcsctp_socket.cc +++ b/net/dcsctp/socket/dcsctp_socket.cc
@@ -466,7 +466,9 @@ // TODO(webrtc:12739): Remove this check, as it just hides the problem that // the socket can transition from ShutdownSent to ShutdownPending, or // ShutdownAckSent to ShutdownPending which is illegal. - if (state_ != State::kShutdownSent && state_ != State::kShutdownAckSent) { + if (state_ != State::kShutdownSent && state_ != State::kShutdownAckSent && + state_ != State::kShutdownReceived && + state_ != State::kShutdownPending) { SetState(State::kShutdownPending, "Shutdown called"); t1_init_->Stop(); t1_cookie_->Stop();
diff --git a/net/dcsctp/socket/dcsctp_socket_test.cc b/net/dcsctp/socket/dcsctp_socket_test.cc index 7d50182..30f9b36 100644 --- a/net/dcsctp/socket/dcsctp_socket_test.cc +++ b/net/dcsctp/socket/dcsctp_socket_test.cc
@@ -39,8 +39,8 @@ #include "net/dcsctp/packet/chunk/init_chunk.h" #include "net/dcsctp/packet/chunk/reconfig_chunk.h" #include "net/dcsctp/packet/chunk/sack_chunk.h" -#include "net/dcsctp/packet/chunk/shutdown_chunk.h" #include "net/dcsctp/packet/chunk/shutdown_ack_chunk.h" +#include "net/dcsctp/packet/chunk/shutdown_chunk.h" #include "net/dcsctp/packet/data.h" #include "net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.h" #include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h" @@ -802,6 +802,45 @@ HasChunks(ElementsAre(IsChunkType(ShutdownAckChunk::kType)))); } +TEST(DcSctpSocketTest, ShutdownIgnoredInShutdownReceived) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + ConnectSockets(a, z); + + z.socket.Shutdown(); + + // A reads SHUTDOWN, produces SHUTDOWN ACK. + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), + HasChunks(ElementsAre(IsChunkType(ShutdownAckChunk::kType)))); + EXPECT_EQ(a.socket.state(), SocketState::kShuttingDown); + + // Second shutdown while already shutting down, must be ignored. + a.socket.Shutdown(); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); +} + +TEST(DcSctpSocketTest, ShutdownIgnoredInShutdownPending) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + ConnectSockets(a, z); + + // Send a message that will remain outstanding (unacknowledged) which + // transitions the socket to SHUTDOWN-PENDING instead of SHUTDOWN-SENT. + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + a.socket.Shutdown(); + + EXPECT_THAT(a.cb.ConsumeSentPacket(), + HasChunks(ElementsAre(IsChunkType(DataChunk::kType)))); + EXPECT_EQ(a.socket.state(), SocketState::kShuttingDown); + + // Second shutdown while already shutting down, must be ignored. + a.socket.Shutdown(); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); +} + TEST(DcSctpSocketTest, EstablishConnectionWhileSendingData) { SocketUnderTest a("A"); SocketUnderTest z("Z");