2021-04-03 20:33:43 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
|
|
* tree. An additional intellectual property rights grant can be found
|
|
|
|
|
* in the file PATENTS. All contributing project authors may
|
|
|
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
|
|
|
*/
|
|
|
|
|
#include "net/dcsctp/rx/reassembly_queue.h"
|
|
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <iterator>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "api/array_view.h"
|
2021-09-20 11:35:59 +02:00
|
|
|
#include "net/dcsctp/common/handover_testing.h"
|
2021-04-03 20:33:43 +02:00
|
|
|
#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h"
|
|
|
|
|
#include "net/dcsctp/packet/chunk/forward_tsn_common.h"
|
|
|
|
|
#include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h"
|
|
|
|
|
#include "net/dcsctp/packet/data.h"
|
|
|
|
|
#include "net/dcsctp/public/dcsctp_message.h"
|
|
|
|
|
#include "net/dcsctp/public/types.h"
|
|
|
|
|
#include "net/dcsctp/testing/data_generator.h"
|
|
|
|
|
#include "rtc_base/gunit.h"
|
|
|
|
|
#include "test/gmock.h"
|
|
|
|
|
|
|
|
|
|
namespace dcsctp {
|
|
|
|
|
namespace {
|
|
|
|
|
using ::testing::ElementsAre;
|
2021-09-15 11:59:17 +02:00
|
|
|
using ::testing::SizeIs;
|
2021-05-11 22:54:16 +02:00
|
|
|
using ::testing::UnorderedElementsAre;
|
2021-04-03 20:33:43 +02:00
|
|
|
|
|
|
|
|
// The default maximum size of the Reassembly Queue.
|
|
|
|
|
static constexpr size_t kBufferSize = 10000;
|
|
|
|
|
|
|
|
|
|
static constexpr StreamID kStreamID(1);
|
|
|
|
|
static constexpr SSN kSSN(0);
|
|
|
|
|
static constexpr MID kMID(0);
|
|
|
|
|
static constexpr FSN kFSN(0);
|
|
|
|
|
static constexpr PPID kPPID(53);
|
|
|
|
|
|
|
|
|
|
static constexpr std::array<uint8_t, 4> kShortPayload = {1, 2, 3, 4};
|
|
|
|
|
static constexpr std::array<uint8_t, 4> kMessage2Payload = {5, 6, 7, 8};
|
2021-05-11 22:54:16 +02:00
|
|
|
static constexpr std::array<uint8_t, 6> kSixBytePayload = {1, 2, 3, 4, 5, 6};
|
|
|
|
|
static constexpr std::array<uint8_t, 8> kMediumPayload1 = {1, 2, 3, 4,
|
|
|
|
|
5, 6, 7, 8};
|
|
|
|
|
static constexpr std::array<uint8_t, 8> kMediumPayload2 = {9, 10, 11, 12,
|
|
|
|
|
13, 14, 15, 16};
|
2021-04-03 20:33:43 +02:00
|
|
|
static constexpr std::array<uint8_t, 16> kLongPayload = {
|
|
|
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
|
|
|
|
|
|
|
|
|
MATCHER_P3(SctpMessageIs, stream_id, ppid, expected_payload, "") {
|
|
|
|
|
if (arg.stream_id() != stream_id) {
|
|
|
|
|
*result_listener << "the stream_id is " << *arg.stream_id();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg.ppid() != ppid) {
|
|
|
|
|
*result_listener << "the ppid is " << *arg.ppid();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (std::vector<uint8_t>(arg.payload().begin(), arg.payload().end()) !=
|
|
|
|
|
std::vector<uint8_t>(expected_payload.begin(), expected_payload.end())) {
|
|
|
|
|
*result_listener << "the payload is wrong";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ReassemblyQueueTest : public testing::Test {
|
|
|
|
|
protected:
|
|
|
|
|
ReassemblyQueueTest() {}
|
|
|
|
|
DataGenerator gen_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, EmptyQueue) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, SingleUnorderedChunkMessage) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, LargeUnorderedChunkAllPermutations) {
|
|
|
|
|
std::vector<uint32_t> tsns = {10, 11, 12, 13};
|
|
|
|
|
rtc::ArrayView<const uint8_t> payload(kLongPayload);
|
|
|
|
|
do {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < tsns.size(); i++) {
|
|
|
|
|
auto span = payload.subview((tsns[i] - 10) * 4, 4);
|
|
|
|
|
Data::IsBeginning is_beginning(tsns[i] == 10);
|
|
|
|
|
Data::IsEnd is_end(tsns[i] == 13);
|
|
|
|
|
|
2021-12-01 18:57:22 +00:00
|
|
|
reasm.Add(TSN(tsns[i]),
|
|
|
|
|
Data(kStreamID, kSSN, kMID, kFSN, kPPID,
|
|
|
|
|
std::vector<uint8_t>(span.begin(), span.end()),
|
|
|
|
|
is_beginning, is_end, IsUnordered(false)));
|
2021-04-03 20:33:43 +02:00
|
|
|
if (i < 3) {
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
} else {
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kLongPayload)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (std::next_permutation(std::begin(tsns), std::end(tsns)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, SingleOrderedChunkMessage) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ManySmallOrderedMessages) {
|
|
|
|
|
std::vector<uint32_t> tsns = {10, 11, 12, 13};
|
|
|
|
|
rtc::ArrayView<const uint8_t> payload(kLongPayload);
|
|
|
|
|
do {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
for (size_t i = 0; i < tsns.size(); i++) {
|
|
|
|
|
auto span = payload.subview((tsns[i] - 10) * 4, 4);
|
|
|
|
|
Data::IsBeginning is_beginning(true);
|
|
|
|
|
Data::IsEnd is_end(true);
|
|
|
|
|
|
|
|
|
|
SSN ssn(static_cast<uint16_t>(tsns[i] - 10));
|
2021-12-01 18:57:22 +00:00
|
|
|
reasm.Add(TSN(tsns[i]),
|
|
|
|
|
Data(kStreamID, ssn, kMID, kFSN, kPPID,
|
|
|
|
|
std::vector<uint8_t>(span.begin(), span.end()),
|
|
|
|
|
is_beginning, is_end, IsUnordered(false)));
|
2021-04-03 20:33:43 +02:00
|
|
|
}
|
|
|
|
|
EXPECT_THAT(
|
|
|
|
|
reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, payload.subview(0, 4)),
|
|
|
|
|
SctpMessageIs(kStreamID, kPPID, payload.subview(4, 4)),
|
|
|
|
|
SctpMessageIs(kStreamID, kPPID, payload.subview(8, 4)),
|
|
|
|
|
SctpMessageIs(kStreamID, kPPID, payload.subview(12, 4))));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
} while (std::next_permutation(std::begin(tsns), std::end(tsns)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, RetransmissionInLargeOrdered) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Ordered({1}, "B"));
|
|
|
|
|
reasm.Add(TSN(12), gen_.Ordered({3}));
|
|
|
|
|
reasm.Add(TSN(13), gen_.Ordered({4}));
|
|
|
|
|
reasm.Add(TSN(14), gen_.Ordered({5}));
|
|
|
|
|
reasm.Add(TSN(15), gen_.Ordered({6}));
|
|
|
|
|
reasm.Add(TSN(16), gen_.Ordered({7}));
|
|
|
|
|
reasm.Add(TSN(17), gen_.Ordered({8}));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 7u);
|
|
|
|
|
|
|
|
|
|
// lost and retransmitted
|
|
|
|
|
reasm.Add(TSN(11), gen_.Ordered({2}));
|
|
|
|
|
reasm.Add(TSN(18), gen_.Ordered({9}));
|
|
|
|
|
reasm.Add(TSN(19), gen_.Ordered({10}));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 10u);
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
|
|
|
|
|
reasm.Add(TSN(20), gen_.Ordered({11, 12, 13, 14, 15, 16}, "E"));
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kLongPayload)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ForwardTSNRemoveUnordered) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Unordered({1}, "B"));
|
|
|
|
|
reasm.Add(TSN(12), gen_.Unordered({3}));
|
|
|
|
|
reasm.Add(TSN(13), gen_.Unordered({4}, "E"));
|
|
|
|
|
|
|
|
|
|
reasm.Add(TSN(14), gen_.Unordered({5}, "B"));
|
|
|
|
|
reasm.Add(TSN(15), gen_.Unordered({6}));
|
|
|
|
|
reasm.Add(TSN(17), gen_.Unordered({8}, "E"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 6u);
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
|
|
|
|
|
reasm.Handle(ForwardTsnChunk(TSN(13), {}));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 3u);
|
|
|
|
|
|
|
|
|
|
// The lost chunk comes, but too late.
|
|
|
|
|
reasm.Add(TSN(11), gen_.Unordered({2}));
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 3u);
|
|
|
|
|
|
|
|
|
|
// The second lost chunk comes, message is assembled.
|
|
|
|
|
reasm.Add(TSN(16), gen_.Unordered({7}));
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ForwardTSNRemoveOrdered) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Ordered({1}, "B"));
|
|
|
|
|
reasm.Add(TSN(12), gen_.Ordered({3}));
|
|
|
|
|
reasm.Add(TSN(13), gen_.Ordered({4}, "E"));
|
|
|
|
|
|
|
|
|
|
reasm.Add(TSN(14), gen_.Ordered({5}, "B"));
|
|
|
|
|
reasm.Add(TSN(15), gen_.Ordered({6}));
|
|
|
|
|
reasm.Add(TSN(16), gen_.Ordered({7}));
|
|
|
|
|
reasm.Add(TSN(17), gen_.Ordered({8}, "E"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 7u);
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
|
|
|
|
|
reasm.Handle(ForwardTsnChunk(
|
|
|
|
|
TSN(13), {ForwardTsnChunk::SkippedStream(kStreamID, kSSN)}));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
|
|
|
|
|
// The lost chunk comes, but too late.
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kMessage2Payload)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ForwardTSNRemoveALotOrdered) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Ordered({1}, "B"));
|
|
|
|
|
reasm.Add(TSN(12), gen_.Ordered({3}));
|
|
|
|
|
reasm.Add(TSN(13), gen_.Ordered({4}, "E"));
|
|
|
|
|
|
|
|
|
|
reasm.Add(TSN(15), gen_.Ordered({5}, "B"));
|
|
|
|
|
reasm.Add(TSN(16), gen_.Ordered({6}));
|
|
|
|
|
reasm.Add(TSN(17), gen_.Ordered({7}));
|
|
|
|
|
reasm.Add(TSN(18), gen_.Ordered({8}, "E"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 7u);
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
|
|
|
|
|
reasm.Handle(ForwardTsnChunk(
|
|
|
|
|
TSN(13), {ForwardTsnChunk::SkippedStream(kStreamID, kSSN)}));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
|
|
|
|
|
// The lost chunk comes, but too late.
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kMessage2Payload)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ShouldntDeliverMessagesBeforeInitialTsn) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(5), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ShouldntRedeliverUnorderedMessages) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload)));
|
|
|
|
|
reasm.Add(TSN(10), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ShouldntRedeliverUnorderedMessagesReallyUnordered) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Unordered({1, 2, 3, 4}, "B"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 4u);
|
|
|
|
|
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
|
|
|
|
|
reasm.Add(TSN(12), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 4u);
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload)));
|
|
|
|
|
reasm.Add(TSN(12), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 4u);
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, ShouldntDeliverBeforeForwardedTsn) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Handle(ForwardTsnChunk(TSN(12), {}));
|
|
|
|
|
|
|
|
|
|
reasm.Add(TSN(12), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-15 11:59:17 +02:00
|
|
|
TEST_F(ReassemblyQueueTest, NotReadyForHandoverWhenDeliveredTsnsHaveGap) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Unordered({1, 2, 3, 4}, "B"));
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
|
|
|
|
|
reasm.Add(TSN(12), gen_.Unordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_EQ(
|
|
|
|
|
reasm.GetHandoverReadiness(),
|
|
|
|
|
HandoverReadinessStatus()
|
|
|
|
|
.Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap)
|
|
|
|
|
.Add(
|
|
|
|
|
HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks));
|
|
|
|
|
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload)));
|
|
|
|
|
EXPECT_EQ(
|
|
|
|
|
reasm.GetHandoverReadiness(),
|
|
|
|
|
HandoverReadinessStatus()
|
|
|
|
|
.Add(HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap)
|
|
|
|
|
.Add(
|
|
|
|
|
HandoverUnreadinessReason::kUnorderedStreamHasUnassembledChunks));
|
|
|
|
|
|
|
|
|
|
reasm.Handle(ForwardTsnChunk(TSN(13), {}));
|
|
|
|
|
EXPECT_EQ(reasm.GetHandoverReadiness(), HandoverReadinessStatus());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, NotReadyForHandoverWhenResetStreamIsDeferred) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
DataGeneratorOptions opts;
|
|
|
|
|
opts.message_id = MID(0);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE", opts));
|
|
|
|
|
opts.message_id = MID(1);
|
|
|
|
|
reasm.Add(TSN(11), gen_.Ordered({1, 2, 3, 4}, "BE", opts));
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(), SizeIs(2));
|
|
|
|
|
|
|
|
|
|
reasm.ResetStreams(
|
|
|
|
|
OutgoingSSNResetRequestParameter(
|
|
|
|
|
ReconfigRequestSN(10), ReconfigRequestSN(3), TSN(13), {StreamID(1)}),
|
|
|
|
|
TSN(11));
|
|
|
|
|
EXPECT_EQ(reasm.GetHandoverReadiness(),
|
|
|
|
|
HandoverReadinessStatus().Add(
|
|
|
|
|
HandoverUnreadinessReason::kStreamResetDeferred));
|
|
|
|
|
|
|
|
|
|
opts.message_id = MID(3);
|
|
|
|
|
opts.ppid = PPID(3);
|
|
|
|
|
reasm.Add(TSN(13), gen_.Ordered({1, 2, 3, 4}, "BE", opts));
|
|
|
|
|
reasm.MaybeResetStreamsDeferred(TSN(11));
|
|
|
|
|
|
|
|
|
|
opts.message_id = MID(2);
|
|
|
|
|
opts.ppid = PPID(2);
|
|
|
|
|
reasm.Add(TSN(13), gen_.Ordered({1, 2, 3, 4}, "BE", opts));
|
|
|
|
|
reasm.MaybeResetStreamsDeferred(TSN(15));
|
|
|
|
|
EXPECT_EQ(reasm.GetHandoverReadiness(),
|
|
|
|
|
HandoverReadinessStatus().Add(
|
|
|
|
|
HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap));
|
|
|
|
|
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(), SizeIs(2));
|
|
|
|
|
EXPECT_EQ(reasm.GetHandoverReadiness(),
|
|
|
|
|
HandoverReadinessStatus().Add(
|
|
|
|
|
HandoverUnreadinessReason::kReassemblyQueueDeliveredTSNsGap));
|
|
|
|
|
|
|
|
|
|
reasm.Handle(ForwardTsnChunk(TSN(15), {}));
|
|
|
|
|
EXPECT_EQ(reasm.GetHandoverReadiness(), HandoverReadinessStatus());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, HandoverInInitialState) {
|
|
|
|
|
ReassemblyQueue reasm1("log: ", TSN(10), kBufferSize);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(reasm1.GetHandoverReadiness(), HandoverReadinessStatus());
|
|
|
|
|
DcSctpSocketHandoverState state;
|
|
|
|
|
reasm1.AddHandoverState(state);
|
2021-09-20 11:35:59 +02:00
|
|
|
g_handover_state_transformer_for_test(&state);
|
2021-05-11 22:54:16 +02:00
|
|
|
ReassemblyQueue reasm2("log: ", TSN(100), kBufferSize,
|
|
|
|
|
/*use_message_interleaving=*/false, &state);
|
2021-09-15 11:59:17 +02:00
|
|
|
|
|
|
|
|
reasm2.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_THAT(reasm2.FlushMessages(), SizeIs(1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, HandoverAfterHavingAssembedOneMessage) {
|
|
|
|
|
ReassemblyQueue reasm1("log: ", TSN(10), kBufferSize);
|
|
|
|
|
reasm1.Add(TSN(10), gen_.Ordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_THAT(reasm1.FlushMessages(), SizeIs(1));
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(reasm1.GetHandoverReadiness(), HandoverReadinessStatus());
|
|
|
|
|
DcSctpSocketHandoverState state;
|
|
|
|
|
reasm1.AddHandoverState(state);
|
2021-09-20 11:35:59 +02:00
|
|
|
g_handover_state_transformer_for_test(&state);
|
2021-05-11 22:54:16 +02:00
|
|
|
ReassemblyQueue reasm2("log: ", TSN(100), kBufferSize,
|
|
|
|
|
/*use_message_interleaving=*/false, &state);
|
2021-09-15 11:59:17 +02:00
|
|
|
|
|
|
|
|
reasm2.Add(TSN(11), gen_.Ordered({1, 2, 3, 4}, "BE"));
|
|
|
|
|
EXPECT_THAT(reasm2.FlushMessages(), SizeIs(1));
|
|
|
|
|
}
|
dcsctp: Don't deliver skipped messages
If a FORWARD-TSN contains an ordered skipped stream with a large TSN
but with a too small SSN, it can result in messages being assembled
that should've been skipped. Typically:
Receive DATA, ordered, complete, TSN=10, SID=1, SSN=0
- will be delivered.
Receive DATA, ordered, complete, TSN=43, SID=1, SSN=7
- will stay in queue, due to missing SSN=1,2,3,4,5,6.
Receive FORWARD-TSN, TSN=44, SSN=6
- is invalid, as the SSN should've been 7 or higher.
However, as the TSN isn't used for removing messages in ordered streams,
but just the SSN, the SSN=7 isn't removed but instead will be delivered
as it's the next following SSN after 6. This will trigger internal
consistency checks as a chunk with TSN=43 will be delivered when the
current cumulative TSN is set to 44, which is greater.
This was found when fuzzing, and can only be provoked by a client that
is intentionally misbehaving. Before this fix, there was no harm done,
but it failed consistency checks which fuzzers have enabled. When
bug 13799 was fixed (in a previous commit), this allowed the fuzzers to
find it faster.
Bug: webrtc:13799
Change-Id: I830ef189476e227e1dbe08157d34f96ad6453e30
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/254240
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36157}
2022-03-08 23:05:10 +01:00
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, HandleInconsistentForwardTSN) {
|
|
|
|
|
// Found when fuzzing.
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize);
|
|
|
|
|
// Add TSN=43, SSN=7. Can't be reassembled as previous SSNs aren't known.
|
|
|
|
|
reasm.Add(TSN(43), Data(kStreamID, SSN(7), MID(0), FSN(0), kPPID,
|
|
|
|
|
std::vector<uint8_t>(10), Data::IsBeginning(true),
|
|
|
|
|
Data::IsEnd(true), IsUnordered(false)));
|
|
|
|
|
|
|
|
|
|
// Invalid, as TSN=44 have to have SSN>=7, but peer says 6.
|
|
|
|
|
reasm.Handle(ForwardTsnChunk(
|
|
|
|
|
TSN(44), {ForwardTsnChunk::SkippedStream(kStreamID, SSN(6))}));
|
|
|
|
|
|
|
|
|
|
// Don't assemble SSN=7, as that TSN is skipped.
|
|
|
|
|
EXPECT_FALSE(reasm.HasMessages());
|
|
|
|
|
}
|
2021-05-11 22:54:16 +02:00
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, SingleUnorderedChunkMessageInRfc8260) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize,
|
|
|
|
|
/*use_message_interleaving=*/true);
|
|
|
|
|
reasm.Add(TSN(10), Data(StreamID(1), SSN(0), MID(0), FSN(0), kPPID,
|
|
|
|
|
{1, 2, 3, 4}, Data::IsBeginning(true),
|
|
|
|
|
Data::IsEnd(true), IsUnordered(true)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kShortPayload)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, TwoInterleavedChunks) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize,
|
|
|
|
|
/*use_message_interleaving=*/true);
|
|
|
|
|
reasm.Add(TSN(10), Data(StreamID(1), SSN(0), MID(0), FSN(0), kPPID,
|
|
|
|
|
{1, 2, 3, 4}, Data::IsBeginning(true),
|
|
|
|
|
Data::IsEnd(false), IsUnordered(true)));
|
|
|
|
|
reasm.Add(TSN(11), Data(StreamID(2), SSN(0), MID(0), FSN(0), kPPID,
|
|
|
|
|
{9, 10, 11, 12}, Data::IsBeginning(true),
|
|
|
|
|
Data::IsEnd(false), IsUnordered(true)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 8u);
|
|
|
|
|
reasm.Add(TSN(12), Data(StreamID(1), SSN(0), MID(0), FSN(1), kPPID,
|
|
|
|
|
{5, 6, 7, 8}, Data::IsBeginning(false),
|
|
|
|
|
Data::IsEnd(true), IsUnordered(true)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 4u);
|
|
|
|
|
reasm.Add(TSN(13), Data(StreamID(2), SSN(0), MID(0), FSN(1), kPPID,
|
|
|
|
|
{13, 14, 15, 16}, Data::IsBeginning(false),
|
|
|
|
|
Data::IsEnd(true), IsUnordered(true)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(StreamID(1), kPPID, kMediumPayload1),
|
|
|
|
|
SctpMessageIs(StreamID(2), kPPID, kMediumPayload2)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, UnorderedInterleavedMessagesAllPermutations) {
|
|
|
|
|
std::vector<int> indexes = {0, 1, 2, 3, 4, 5};
|
|
|
|
|
TSN tsns[] = {TSN(10), TSN(11), TSN(12), TSN(13), TSN(14), TSN(15)};
|
|
|
|
|
StreamID stream_ids[] = {StreamID(1), StreamID(2), StreamID(1),
|
|
|
|
|
StreamID(1), StreamID(2), StreamID(2)};
|
|
|
|
|
FSN fsns[] = {FSN(0), FSN(0), FSN(1), FSN(2), FSN(1), FSN(2)};
|
|
|
|
|
rtc::ArrayView<const uint8_t> payload(kSixBytePayload);
|
|
|
|
|
do {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize,
|
|
|
|
|
/*use_message_interleaving=*/true);
|
|
|
|
|
for (int i : indexes) {
|
|
|
|
|
auto span = payload.subview(*fsns[i] * 2, 2);
|
|
|
|
|
Data::IsBeginning is_beginning(fsns[i] == FSN(0));
|
|
|
|
|
Data::IsEnd is_end(fsns[i] == FSN(2));
|
|
|
|
|
reasm.Add(tsns[i], Data(stream_ids[i], SSN(0), MID(0), fsns[i], kPPID,
|
|
|
|
|
std::vector<uint8_t>(span.begin(), span.end()),
|
|
|
|
|
is_beginning, is_end, IsUnordered(true)));
|
|
|
|
|
}
|
|
|
|
|
EXPECT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
UnorderedElementsAre(
|
|
|
|
|
SctpMessageIs(StreamID(1), kPPID, kSixBytePayload),
|
|
|
|
|
SctpMessageIs(StreamID(2), kPPID, kSixBytePayload)));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
} while (std::next_permutation(std::begin(indexes), std::end(indexes)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(ReassemblyQueueTest, IForwardTSNRemoveALotOrdered) {
|
|
|
|
|
ReassemblyQueue reasm("log: ", TSN(10), kBufferSize,
|
|
|
|
|
/*use_message_interleaving=*/true);
|
|
|
|
|
reasm.Add(TSN(10), gen_.Ordered({1}, "B"));
|
|
|
|
|
gen_.Ordered({2}, "");
|
|
|
|
|
reasm.Add(TSN(12), gen_.Ordered({3}, ""));
|
|
|
|
|
reasm.Add(TSN(13), gen_.Ordered({4}, "E"));
|
|
|
|
|
reasm.Add(TSN(15), gen_.Ordered({5}, "B"));
|
|
|
|
|
reasm.Add(TSN(16), gen_.Ordered({6}, ""));
|
|
|
|
|
reasm.Add(TSN(17), gen_.Ordered({7}, ""));
|
|
|
|
|
reasm.Add(TSN(18), gen_.Ordered({8}, "E"));
|
|
|
|
|
|
|
|
|
|
ASSERT_FALSE(reasm.HasMessages());
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 7u);
|
|
|
|
|
|
|
|
|
|
reasm.Handle(
|
|
|
|
|
IForwardTsnChunk(TSN(13), {IForwardTsnChunk::SkippedStream(
|
|
|
|
|
IsUnordered(false), kStreamID, MID(0))}));
|
|
|
|
|
EXPECT_EQ(reasm.queued_bytes(), 0u);
|
|
|
|
|
|
|
|
|
|
// The lost chunk comes, but too late.
|
|
|
|
|
ASSERT_TRUE(reasm.HasMessages());
|
|
|
|
|
EXPECT_THAT(reasm.FlushMessages(),
|
|
|
|
|
ElementsAre(SctpMessageIs(kStreamID, kPPID, kMessage2Payload)));
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-03 20:33:43 +02:00
|
|
|
} // namespace
|
|
|
|
|
} // namespace dcsctp
|