From 7087b83d80be56df28d646e4dd3a782332aa4003 Mon Sep 17 00:00:00 2001 From: Harald Alvestrand Date: Thu, 11 Mar 2021 17:21:13 +0000 Subject: [PATCH] Test that SCTP succeeds with one MTU and fails with a lower MTU This pair of tests will ensure that the SCTP layer's response to MTU size changes has not been modified. Bug: webrtc:12495 Change-Id: If9776ad399871e9f01b38715594b732e156118ff Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/211246 Reviewed-by: Tommi Commit-Queue: Harald Alvestrand Cr-Commit-Position: refs/heads/master@{#33459} --- pc/data_channel_integrationtest.cc | 116 +++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/pc/data_channel_integrationtest.cc b/pc/data_channel_integrationtest.cc index 91c8870443..d4a259b608 100644 --- a/pc/data_channel_integrationtest.cc +++ b/pc/data_channel_integrationtest.cc @@ -397,6 +397,122 @@ TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannel) { kDefaultTimeout); } +// This test sets up a call between two parties with an SCTP +// data channel only, and sends messages of various sizes. +TEST_P(DataChannelIntegrationTest, + EndToEndCallWithSctpDataChannelVariousSizes) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + // Expect that data channel created on caller side will show up for callee as + // well. + caller()->CreateDataChannel(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + // Caller data channel should already exist (it created one). Callee data + // channel may not exist yet, since negotiation happens in-band, not in SDP. + ASSERT_NE(nullptr, caller()->data_channel()); + ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout); + EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout); + EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout); + + for (int message_size = 1; message_size < 100000; message_size *= 2) { + std::string data(message_size, 'a'); + caller()->data_channel()->Send(DataBuffer(data)); + EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(), + kDefaultTimeout); + callee()->data_channel()->Send(DataBuffer(data)); + EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(), + kDefaultTimeout); + } + // Specifically probe the area around the MTU size. + for (int message_size = 1100; message_size < 1300; message_size += 1) { + std::string data(message_size, 'a'); + caller()->data_channel()->Send(DataBuffer(data)); + EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(), + kDefaultTimeout); + callee()->data_channel()->Send(DataBuffer(data)); + EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(), + kDefaultTimeout); + } +} + +TEST_P(DataChannelIntegrationTest, + EndToEndCallWithSctpDataChannelLowestSafeMtu) { + // The lowest payload size limit that's tested and found safe for this + // application. Note that this is not the safe limit under all conditions; + // in particular, the default is not the largest DTLS signature, and + // this test does not use TURN. + const size_t kLowestSafePayloadSizeLimit = 1225; + + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + // Expect that data channel created on caller side will show up for callee as + // well. + caller()->CreateDataChannel(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + // Caller data channel should already exist (it created one). Callee data + // channel may not exist yet, since negotiation happens in-band, not in SDP. + ASSERT_NE(nullptr, caller()->data_channel()); + ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout); + EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout); + EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout); + + virtual_socket_server()->set_max_udp_payload(kLowestSafePayloadSizeLimit); + for (int message_size = 1140; message_size < 1240; message_size += 1) { + std::string data(message_size, 'a'); + caller()->data_channel()->Send(DataBuffer(data)); + ASSERT_EQ_WAIT(data, callee()->data_observer()->last_message(), + kDefaultTimeout); + callee()->data_channel()->Send(DataBuffer(data)); + ASSERT_EQ_WAIT(data, caller()->data_observer()->last_message(), + kDefaultTimeout); + } +} + +// This test verifies that lowering the MTU of the connection will cause +// the datachannel to not transmit reliably. +// The purpose of this test is to ensure that we know how a too-small MTU +// error manifests itself. +TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelHarmfulMtu) { + // The lowest payload size limit that's tested and found safe for this + // application in this configuration (see test above). + const size_t kLowestSafePayloadSizeLimit = 1225; + // The size of the smallest message that fails to be delivered. + const size_t kMessageSizeThatIsNotDelivered = 1157; + + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->CreateDataChannel(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + ASSERT_NE(nullptr, caller()->data_channel()); + ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout); + EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout); + EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout); + + virtual_socket_server()->set_max_udp_payload(kLowestSafePayloadSizeLimit - 1); + // Probe for an undelivered or slowly delivered message. The exact + // size limit seems to be dependent on the message history, so make the + // code easily able to find the current value. + bool failure_seen = false; + for (size_t message_size = 1110; message_size < 1400; message_size++) { + const size_t message_count = + callee()->data_observer()->received_message_count(); + const std::string data(message_size, 'a'); + caller()->data_channel()->Send(DataBuffer(data)); + // Wait a very short time for the message to be delivered. + WAIT(callee()->data_observer()->received_message_count() > message_count, + 10); + if (callee()->data_observer()->received_message_count() == message_count) { + ASSERT_EQ(kMessageSizeThatIsNotDelivered, message_size); + failure_seen = true; + break; + } + } + ASSERT_TRUE(failure_seen); +} + // Ensure that when the callee closes an SCTP data channel, the closing // procedure results in the data channel being closed for the caller as well. TEST_P(DataChannelIntegrationTest, CalleeClosesSctpDataChannel) {