webrtc_m130/net/dcsctp/rx/data_tracker_test.cc
Victor Boivie 568bc23208 dcsctp: Don't reassemble already received chunks
This is a solution to some problems that have been found locally when
running the fuzzer for a long time. The fuzzer keeps on fuzzing, and has
found a way to trigger a consistency check to fail when a client
intentionally sends different messages - unordered and ordered - using
the same TSNs. As the reassembly queue has different handling of ordered
and unordered chunks due to how they are reassembled, it will not notice
if it receives two different chunks with the same TSN. They will both go
to their respective reassembly streams, as those are separate by design.

The data tracker - which keeps track of all received DATA chunks as it
needs to generate SACKs, has a global understanding of all received
chunks. By having it indicate if this is a duplicate received chunk, the
socket can avoid forwarding both chunks to the reassembly queue; only
one chunk will get there.

Bug: None
Change-Id: I602a8552a9a4c853684fcf105309ec3d8073f2c2
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/256110
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36316}
2022-03-24 10:39:03 +00:00

739 lines
27 KiB
C++

/*
* 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/data_tracker.h"
#include <cstdint>
#include <initializer_list>
#include <memory>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/task_queue/task_queue_base.h"
#include "net/dcsctp/common/handover_testing.h"
#include "net/dcsctp/packet/chunk/sack_chunk.h"
#include "net/dcsctp/timer/fake_timeout.h"
#include "net/dcsctp/timer/timer.h"
#include "rtc_base/gunit.h"
#include "test/gmock.h"
namespace dcsctp {
namespace {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
constexpr size_t kArwnd = 10000;
constexpr TSN kInitialTSN(11);
class DataTrackerTest : public testing::Test {
protected:
DataTrackerTest()
: timeout_manager_([this]() { return now_; }),
timer_manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) {
return timeout_manager_.CreateTimeout(precision);
}),
timer_(timer_manager_.CreateTimer(
"test/delayed_ack",
[]() { return absl::nullopt; },
TimerOptions(DurationMs(0)))),
tracker_(
std::make_unique<DataTracker>("log: ", timer_.get(), kInitialTSN)) {
}
void Observer(std::initializer_list<uint32_t> tsns,
bool expect_as_duplicate = false) {
for (const uint32_t tsn : tsns) {
if (expect_as_duplicate) {
EXPECT_FALSE(
tracker_->Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false)));
} else {
EXPECT_TRUE(
tracker_->Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false)));
}
}
}
void HandoverTracker() {
EXPECT_TRUE(tracker_->GetHandoverReadiness().IsReady());
DcSctpSocketHandoverState state;
tracker_->AddHandoverState(state);
g_handover_state_transformer_for_test(&state);
tracker_ = std::make_unique<DataTracker>("log: ", timer_.get(), kInitialTSN,
&state);
}
TimeMs now_ = TimeMs(0);
FakeTimeoutManager timeout_manager_;
TimerManager timer_manager_;
std::unique_ptr<Timer> timer_;
std::unique_ptr<DataTracker> tracker_;
};
TEST_F(DataTrackerTest, Empty) {
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ObserverSingleInOrderPacket) {
Observer({11});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ObserverManyInOrderMovesCumulativeTsnAck) {
Observer({11, 12, 13});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(13));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ObserveOutOfOrderMovesCumulativeTsnAck) {
Observer({12, 13, 14, 11});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, SingleGap) {
Observer({12});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, ExampleFromRFC4960Section334) {
Observer({11, 12, 14, 15, 17});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(12));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 3),
SackChunk::GapAckBlock(5, 5)));
EXPECT_THAT(sack.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, AckAlreadyReceivedChunk) {
Observer({11});
SackChunk sack1 = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty());
// Receive old chunk
Observer({8}, /*expect_as_duplicate=*/true);
SackChunk sack2 = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, DoubleSendRetransmittedChunk) {
Observer({11, 13, 14, 15});
SackChunk sack1 = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack1.gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(2, 4)));
// Fill in the hole.
Observer({12, 16, 17, 18});
SackChunk sack2 = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(18));
EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty());
// Receive chunk 12 again.
Observer({12}, /*expect_as_duplicate=*/true);
Observer({19, 20, 21});
SackChunk sack3 = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack3.cumulative_tsn_ack(), TSN(21));
EXPECT_THAT(sack3.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, ForwardTsnSimple) {
// Messages (11, 12, 13), (14, 15) - first message expires.
Observer({11, 12, 15});
tracker_->HandleForwardTsn(TSN(13));
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(13));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
}
TEST_F(DataTrackerTest, ForwardTsnSkipsFromGapBlock) {
// Messages (11, 12, 13), (14, 15) - first message expires.
Observer({11, 12, 14});
tracker_->HandleForwardTsn(TSN(13));
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, ExampleFromRFC3758) {
tracker_->HandleForwardTsn(TSN(102));
Observer({102}, /*expect_as_duplicate=*/true);
Observer({104, 105, 107});
tracker_->HandleForwardTsn(TSN(103));
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(105));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
}
TEST_F(DataTrackerTest, EmptyAllAcks) {
Observer({11, 13, 14, 15});
tracker_->HandleForwardTsn(TSN(100));
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(100));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, SetsArwndCorrectly) {
SackChunk sack1 = tracker_->CreateSelectiveAck(/*a_rwnd=*/100);
EXPECT_EQ(sack1.a_rwnd(), 100u);
SackChunk sack2 = tracker_->CreateSelectiveAck(/*a_rwnd=*/101);
EXPECT_EQ(sack2.a_rwnd(), 101u);
}
TEST_F(DataTrackerTest, WillIncreaseCumAckTsn) {
EXPECT_EQ(tracker_->last_cumulative_acked_tsn(), TSN(10));
EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(10)));
EXPECT_TRUE(tracker_->will_increase_cum_ack_tsn(TSN(11)));
EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(12)));
Observer({11, 12, 13, 14, 15});
EXPECT_EQ(tracker_->last_cumulative_acked_tsn(), TSN(15));
EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(15)));
EXPECT_TRUE(tracker_->will_increase_cum_ack_tsn(TSN(16)));
EXPECT_FALSE(tracker_->will_increase_cum_ack_tsn(TSN(17)));
}
TEST_F(DataTrackerTest, ForceShouldSendSackImmediately) {
EXPECT_FALSE(tracker_->ShouldSendAck());
tracker_->ForceImmediateSack();
EXPECT_TRUE(tracker_->ShouldSendAck());
}
TEST_F(DataTrackerTest, WillAcceptValidTSNs) {
// The initial TSN is always one more than the last, which is our base.
TSN last_tsn = TSN(*kInitialTSN - 1);
int limit = static_cast<int>(DataTracker::kMaxAcceptedOutstandingFragments);
for (int i = -limit; i <= limit; ++i) {
EXPECT_TRUE(tracker_->IsTSNValid(TSN(*last_tsn + i)));
}
}
TEST_F(DataTrackerTest, WillNotAcceptInvalidTSNs) {
// The initial TSN is always one more than the last, which is our base.
TSN last_tsn = TSN(*kInitialTSN - 1);
size_t limit = DataTracker::kMaxAcceptedOutstandingFragments;
EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn + limit + 1)));
EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn - (limit + 1))));
EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn + 0x8000000)));
EXPECT_FALSE(tracker_->IsTSNValid(TSN(*last_tsn - 0x8000000)));
}
TEST_F(DataTrackerTest, ReportSingleDuplicateTsns) {
Observer({11, 12});
Observer({11}, /*expect_as_duplicate=*/true);
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(12));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), UnorderedElementsAre(TSN(11)));
}
TEST_F(DataTrackerTest, ReportMultipleDuplicateTsns) {
Observer({11, 12, 13, 14});
Observer({12, 13, 12, 13}, /*expect_as_duplicate=*/true);
Observer({15, 16});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(16));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(), UnorderedElementsAre(TSN(12), TSN(13)));
}
TEST_F(DataTrackerTest, ReportDuplicateTsnsInGapAckBlocks) {
Observer({11, /*12,*/ 13, 14});
Observer({13, 14}, /*expect_as_duplicate=*/true);
Observer({15, 16});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 5)));
EXPECT_THAT(sack.duplicate_tsns(), UnorderedElementsAre(TSN(13), TSN(14)));
}
TEST_F(DataTrackerTest, ClearsDuplicateTsnsAfterCreatingSack) {
Observer({11, 12, 13, 14});
Observer({12, 13, 12, 13}, /*expect_as_duplicate=*/true);
Observer({15, 16});
SackChunk sack1 = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(16));
EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack1.duplicate_tsns(), UnorderedElementsAre(TSN(12), TSN(13)));
Observer({17});
SackChunk sack2 = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(17));
EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack2.duplicate_tsns(), IsEmpty());
}
TEST_F(DataTrackerTest, LimitsNumberOfDuplicatesReported) {
for (size_t i = 0; i < DataTracker::kMaxDuplicateTsnReported + 10; ++i) {
TSN tsn(11 + i);
tracker_->Observe(tsn, AnyDataChunk::ImmediateAckFlag(false));
tracker_->Observe(tsn, AnyDataChunk::ImmediateAckFlag(false));
}
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
EXPECT_THAT(sack.duplicate_tsns(),
SizeIs(DataTracker::kMaxDuplicateTsnReported));
}
TEST_F(DataTrackerTest, LimitsNumberOfGapAckBlocksReported) {
for (size_t i = 0; i < DataTracker::kMaxGapAckBlocksReported + 10; ++i) {
TSN tsn(11 + i * 2);
tracker_->Observe(tsn, AnyDataChunk::ImmediateAckFlag(false));
}
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack.gap_ack_blocks(),
SizeIs(DataTracker::kMaxGapAckBlocksReported));
}
TEST_F(DataTrackerTest, SendsSackForFirstPacketObserved) {
Observer({11});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
}
TEST_F(DataTrackerTest, SendsSackEverySecondPacketWhenThereIsNoPacketLoss) {
Observer({11});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({12});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
Observer({13});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({14});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
Observer({15});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
}
TEST_F(DataTrackerTest, SendsSackEveryPacketOnPacketLoss) {
Observer({11});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({13});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({14});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({15});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({16});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
// Fill the hole.
Observer({12});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
// Goes back to every second packet
Observer({17});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({18});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
}
TEST_F(DataTrackerTest, SendsSackOnDuplicateDataChunks) {
Observer({11});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({11}, /*expect_as_duplicate=*/true);
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({12});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
// Goes back to every second packet
Observer({13});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
// Duplicate again
Observer({12}, /*expect_as_duplicate=*/true);
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
}
TEST_F(DataTrackerTest, GapAckBlockAddSingleBlock) {
Observer({12});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
}
TEST_F(DataTrackerTest, GapAckBlockAddsAnother) {
Observer({12});
Observer({14});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2),
SackChunk::GapAckBlock(4, 4)));
}
TEST_F(DataTrackerTest, GapAckBlockAddsDuplicate) {
Observer({12});
Observer({12}, /*expect_as_duplicate=*/true);
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2)));
EXPECT_THAT(sack.duplicate_tsns(), ElementsAre(TSN(12)));
}
TEST_F(DataTrackerTest, GapAckBlockExpandsToRight) {
Observer({12});
Observer({13});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 3)));
}
TEST_F(DataTrackerTest, GapAckBlockExpandsToRightWithOther) {
Observer({12});
Observer({20});
Observer({30});
Observer({21});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(2, 2), //
SackChunk::GapAckBlock(10, 11), //
SackChunk::GapAckBlock(20, 20)));
}
TEST_F(DataTrackerTest, GapAckBlockExpandsToLeft) {
Observer({13});
Observer({12});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 3)));
}
TEST_F(DataTrackerTest, GapAckBlockExpandsToLeftWithOther) {
Observer({12});
Observer({21});
Observer({30});
Observer({20});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(2, 2), //
SackChunk::GapAckBlock(10, 11), //
SackChunk::GapAckBlock(20, 20)));
}
TEST_F(DataTrackerTest, GapAckBlockExpandsToLRightAndMerges) {
Observer({12});
Observer({20});
Observer({22});
Observer({30});
Observer({21});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(sack.gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(2, 2), //
SackChunk::GapAckBlock(10, 12), //
SackChunk::GapAckBlock(20, 20)));
}
TEST_F(DataTrackerTest, GapAckBlockMergesManyBlocksIntoOne) {
Observer({22});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 12)));
Observer({30});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 12), //
SackChunk::GapAckBlock(20, 20)));
Observer({24});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 12), //
SackChunk::GapAckBlock(14, 14), //
SackChunk::GapAckBlock(20, 20)));
Observer({28});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 12), //
SackChunk::GapAckBlock(14, 14), //
SackChunk::GapAckBlock(18, 18), //
SackChunk::GapAckBlock(20, 20)));
Observer({26});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 12), //
SackChunk::GapAckBlock(14, 14), //
SackChunk::GapAckBlock(16, 16), //
SackChunk::GapAckBlock(18, 18), //
SackChunk::GapAckBlock(20, 20)));
Observer({29});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 12), //
SackChunk::GapAckBlock(14, 14), //
SackChunk::GapAckBlock(16, 16), //
SackChunk::GapAckBlock(18, 20)));
Observer({23});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 14), //
SackChunk::GapAckBlock(16, 16), //
SackChunk::GapAckBlock(18, 20)));
Observer({27});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 14), //
SackChunk::GapAckBlock(16, 20)));
Observer({25});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(12, 20)));
Observer({20});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(10, 10), //
SackChunk::GapAckBlock(12, 20)));
Observer({32});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(10, 10), //
SackChunk::GapAckBlock(12, 20), //
SackChunk::GapAckBlock(22, 22)));
Observer({21});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(10, 20), //
SackChunk::GapAckBlock(22, 22)));
Observer({31});
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(10, 22)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveBeforeCumAckTsn) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(8));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(10));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(2, 4), //
SackChunk::GapAckBlock(10, 12),
SackChunk::GapAckBlock(20, 21)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveBeforeFirstBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(11));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(6, 8), //
SackChunk::GapAckBlock(16, 17)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveAtBeginningOfFirstBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(12));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(6, 8), //
SackChunk::GapAckBlock(16, 17)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveAtMiddleOfFirstBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(13));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(6, 8), //
SackChunk::GapAckBlock(16, 17)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveAtEndOfFirstBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(14));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(14));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(6, 8), //
SackChunk::GapAckBlock(16, 17)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveRightAfterFirstBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(18));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(18));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(2, 4), //
SackChunk::GapAckBlock(12, 13)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveRightBeforeSecondBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(19));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(8, 9)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveRightAtStartOfSecondBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(20));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(8, 9)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveRightAtMiddleOfSecondBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(21));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(8, 9)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveRightAtEndOfSecondBlock) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(22));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(22));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(),
ElementsAre(SackChunk::GapAckBlock(8, 9)));
}
TEST_F(DataTrackerTest, GapAckBlockRemoveeFarAfterAllBlocks) {
Observer({12, 13, 14, 20, 21, 22, 30, 31});
tracker_->HandleForwardTsn(TSN(40));
EXPECT_EQ(tracker_->CreateSelectiveAck(kArwnd).cumulative_tsn_ack(), TSN(40));
EXPECT_THAT(tracker_->CreateSelectiveAck(kArwnd).gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest, HandoverEmpty) {
HandoverTracker();
Observer({11});
SackChunk sack = tracker_->CreateSelectiveAck(kArwnd);
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11));
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
}
TEST_F(DataTrackerTest,
HandoverWhileSendingSackEverySecondPacketWhenThereIsNoPacketLoss) {
Observer({11});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
HandoverTracker();
Observer({12});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
Observer({13});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
Observer({14});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
Observer({15});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_FALSE(timer_->is_running());
}
TEST_F(DataTrackerTest, HandoverWhileSendingSackEveryPacketOnPacketLoss) {
Observer({11});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
Observer({13});
EXPECT_EQ(tracker_->GetHandoverReadiness(),
HandoverReadinessStatus().Add(
HandoverUnreadinessReason::kDataTrackerTsnBlocksPending));
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
Observer({14});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
EXPECT_EQ(tracker_->GetHandoverReadiness(),
HandoverReadinessStatus(
HandoverUnreadinessReason::kDataTrackerTsnBlocksPending));
Observer({15});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
Observer({16});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
// Fill the hole.
Observer({12});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
// Goes back to every second packet
Observer({17});
tracker_->ObservePacketEnd();
EXPECT_TRUE(tracker_->ShouldSendAck());
HandoverTracker();
Observer({18});
tracker_->ObservePacketEnd();
EXPECT_FALSE(tracker_->ShouldSendAck());
EXPECT_TRUE(timer_->is_running());
}
} // namespace
} // namespace dcsctp