
The Data Tracker's purpose is to keep track of all received DATA chunks and to ACK/NACK that data, by generating SACK chunks reflecting its view of what has been received and what has been lost. It also contains logic for _when_ to send the SACKs, as that's different depending on e.g. packet loss. Generally, SACKs are sent every second packet on a connection with no packet loss, and can also be sent on a delayed timer. In case partial reliability is used, and the transmitter has decided that some data shouldn't be retransmitted, it will send a FORWARD-TSN chunk, which this class also handles, by "forgetting" about those chunks. Bug: webrtc:12614 Change-Id: Ifafb0c211f6a47872e81830165ab5fc43ee7f366 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213664 Commit-Queue: Victor Boivie <boivie@webrtc.org> Reviewed-by: Tommi <tommi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#33676}
210 lines
6.7 KiB
C++
210 lines
6.7 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 "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;
|
|
|
|
constexpr size_t kArwnd = 10000;
|
|
|
|
class DataTrackerTest : public testing::Test {
|
|
protected:
|
|
DataTrackerTest()
|
|
: timeout_manager_([this]() { return now_; }),
|
|
timer_manager_([this]() { return timeout_manager_.CreateTimeout(); }),
|
|
timer_(timer_manager_.CreateTimer(
|
|
"test/delayed_ack",
|
|
[]() { return absl::nullopt; },
|
|
TimerOptions(DurationMs(0)))),
|
|
buf_("log: ", timer_.get(), /*peer_initial_tsn=*/TSN(11)) {}
|
|
|
|
void Observer(std::initializer_list<uint32_t> tsns) {
|
|
for (const uint32_t tsn : tsns) {
|
|
buf_.Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false));
|
|
}
|
|
}
|
|
|
|
TimeMs now_ = TimeMs(0);
|
|
FakeTimeoutManager timeout_manager_;
|
|
TimerManager timer_manager_;
|
|
std::unique_ptr<Timer> timer_;
|
|
DataTracker buf_;
|
|
};
|
|
|
|
TEST_F(DataTrackerTest, Empty) {
|
|
SackChunk sack = buf_.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 = buf_.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 = buf_.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 = buf_.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 = buf_.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 = buf_.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 = buf_.CreateSelectiveAck(kArwnd);
|
|
EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(11));
|
|
EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty());
|
|
|
|
// Receive old chunk
|
|
Observer({8});
|
|
SackChunk sack2 = buf_.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 = buf_.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 = buf_.CreateSelectiveAck(kArwnd);
|
|
EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(18));
|
|
EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty());
|
|
|
|
// Receive chunk 12 again.
|
|
Observer({12, 19, 20, 21});
|
|
SackChunk sack3 = buf_.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});
|
|
|
|
buf_.HandleForwardTsn(TSN(13));
|
|
|
|
SackChunk sack = buf_.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});
|
|
|
|
buf_.HandleForwardTsn(TSN(13));
|
|
|
|
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
|
|
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(14));
|
|
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
|
|
}
|
|
|
|
TEST_F(DataTrackerTest, ExampleFromRFC3758) {
|
|
buf_.HandleForwardTsn(TSN(102));
|
|
|
|
Observer({102, 104, 105, 107});
|
|
|
|
buf_.HandleForwardTsn(TSN(103));
|
|
|
|
SackChunk sack = buf_.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});
|
|
|
|
buf_.HandleForwardTsn(TSN(100));
|
|
|
|
SackChunk sack = buf_.CreateSelectiveAck(kArwnd);
|
|
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(100));
|
|
EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty());
|
|
}
|
|
|
|
TEST_F(DataTrackerTest, SetsArwndCorrectly) {
|
|
SackChunk sack1 = buf_.CreateSelectiveAck(/*a_rwnd=*/100);
|
|
EXPECT_EQ(sack1.a_rwnd(), 100u);
|
|
|
|
SackChunk sack2 = buf_.CreateSelectiveAck(/*a_rwnd=*/101);
|
|
EXPECT_EQ(sack2.a_rwnd(), 101u);
|
|
}
|
|
|
|
TEST_F(DataTrackerTest, WillIncreaseCumAckTsn) {
|
|
EXPECT_EQ(buf_.last_cumulative_acked_tsn(), TSN(10));
|
|
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(10)));
|
|
EXPECT_TRUE(buf_.will_increase_cum_ack_tsn(TSN(11)));
|
|
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(12)));
|
|
|
|
Observer({11, 12, 13, 14, 15});
|
|
EXPECT_EQ(buf_.last_cumulative_acked_tsn(), TSN(15));
|
|
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(15)));
|
|
EXPECT_TRUE(buf_.will_increase_cum_ack_tsn(TSN(16)));
|
|
EXPECT_FALSE(buf_.will_increase_cum_ack_tsn(TSN(17)));
|
|
}
|
|
|
|
TEST_F(DataTrackerTest, ForceShouldSendSackImmediately) {
|
|
EXPECT_FALSE(buf_.ShouldSendAck());
|
|
|
|
buf_.ForceImmediateSack();
|
|
|
|
EXPECT_TRUE(buf_.ShouldSendAck());
|
|
}
|
|
} // namespace
|
|
} // namespace dcsctp
|