1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker #include "net/dcsctp/rx/data_tracker.h"
11*d9f75844SAndroid Build Coastguard Worker
12*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
13*d9f75844SAndroid Build Coastguard Worker #include <cstdint>
14*d9f75844SAndroid Build Coastguard Worker #include <iterator>
15*d9f75844SAndroid Build Coastguard Worker #include <set>
16*d9f75844SAndroid Build Coastguard Worker #include <string>
17*d9f75844SAndroid Build Coastguard Worker #include <utility>
18*d9f75844SAndroid Build Coastguard Worker #include <vector>
19*d9f75844SAndroid Build Coastguard Worker
20*d9f75844SAndroid Build Coastguard Worker #include "absl/algorithm/container.h"
21*d9f75844SAndroid Build Coastguard Worker #include "absl/strings/string_view.h"
22*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h"
23*d9f75844SAndroid Build Coastguard Worker #include "net/dcsctp/common/sequence_numbers.h"
24*d9f75844SAndroid Build Coastguard Worker #include "net/dcsctp/packet/chunk/sack_chunk.h"
25*d9f75844SAndroid Build Coastguard Worker #include "net/dcsctp/timer/timer.h"
26*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
27*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/strings/string_builder.h"
28*d9f75844SAndroid Build Coastguard Worker
29*d9f75844SAndroid Build Coastguard Worker namespace dcsctp {
30*d9f75844SAndroid Build Coastguard Worker
31*d9f75844SAndroid Build Coastguard Worker constexpr size_t DataTracker::kMaxDuplicateTsnReported;
32*d9f75844SAndroid Build Coastguard Worker constexpr size_t DataTracker::kMaxGapAckBlocksReported;
33*d9f75844SAndroid Build Coastguard Worker
Add(UnwrappedTSN tsn)34*d9f75844SAndroid Build Coastguard Worker bool DataTracker::AdditionalTsnBlocks::Add(UnwrappedTSN tsn) {
35*d9f75844SAndroid Build Coastguard Worker // Find any block to expand. It will look for any block that includes (also
36*d9f75844SAndroid Build Coastguard Worker // when expanded) the provided `tsn`. It will return the block that is greater
37*d9f75844SAndroid Build Coastguard Worker // than, or equal to `tsn`.
38*d9f75844SAndroid Build Coastguard Worker auto it = absl::c_lower_bound(
39*d9f75844SAndroid Build Coastguard Worker blocks_, tsn, [&](const TsnRange& elem, const UnwrappedTSN& t) {
40*d9f75844SAndroid Build Coastguard Worker return elem.last.next_value() < t;
41*d9f75844SAndroid Build Coastguard Worker });
42*d9f75844SAndroid Build Coastguard Worker
43*d9f75844SAndroid Build Coastguard Worker if (it == blocks_.end()) {
44*d9f75844SAndroid Build Coastguard Worker // No matching block found. There is no greater than, or equal block - which
45*d9f75844SAndroid Build Coastguard Worker // means that this TSN is greater than any block. It can then be inserted at
46*d9f75844SAndroid Build Coastguard Worker // the end.
47*d9f75844SAndroid Build Coastguard Worker blocks_.emplace_back(tsn, tsn);
48*d9f75844SAndroid Build Coastguard Worker return true;
49*d9f75844SAndroid Build Coastguard Worker }
50*d9f75844SAndroid Build Coastguard Worker
51*d9f75844SAndroid Build Coastguard Worker if (tsn >= it->first && tsn <= it->last) {
52*d9f75844SAndroid Build Coastguard Worker // It's already in this block.
53*d9f75844SAndroid Build Coastguard Worker return false;
54*d9f75844SAndroid Build Coastguard Worker }
55*d9f75844SAndroid Build Coastguard Worker
56*d9f75844SAndroid Build Coastguard Worker if (it->last.next_value() == tsn) {
57*d9f75844SAndroid Build Coastguard Worker // This block can be expanded to the right, or merged with the next.
58*d9f75844SAndroid Build Coastguard Worker auto next_it = it + 1;
59*d9f75844SAndroid Build Coastguard Worker if (next_it != blocks_.end() && tsn.next_value() == next_it->first) {
60*d9f75844SAndroid Build Coastguard Worker // Expanding it would make it adjacent to next block - merge those.
61*d9f75844SAndroid Build Coastguard Worker it->last = next_it->last;
62*d9f75844SAndroid Build Coastguard Worker blocks_.erase(next_it);
63*d9f75844SAndroid Build Coastguard Worker return true;
64*d9f75844SAndroid Build Coastguard Worker }
65*d9f75844SAndroid Build Coastguard Worker
66*d9f75844SAndroid Build Coastguard Worker // Expand to the right
67*d9f75844SAndroid Build Coastguard Worker it->last = tsn;
68*d9f75844SAndroid Build Coastguard Worker return true;
69*d9f75844SAndroid Build Coastguard Worker }
70*d9f75844SAndroid Build Coastguard Worker
71*d9f75844SAndroid Build Coastguard Worker if (it->first == tsn.next_value()) {
72*d9f75844SAndroid Build Coastguard Worker // This block can be expanded to the left. Merging to the left would've been
73*d9f75844SAndroid Build Coastguard Worker // covered by the above "merge to the right". Both blocks (expand a
74*d9f75844SAndroid Build Coastguard Worker // right-most block to the left and expand a left-most block to the right)
75*d9f75844SAndroid Build Coastguard Worker // would match, but the left-most would be returned by std::lower_bound.
76*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(it == blocks_.begin() || (it - 1)->last.next_value() != tsn);
77*d9f75844SAndroid Build Coastguard Worker
78*d9f75844SAndroid Build Coastguard Worker // Expand to the left.
79*d9f75844SAndroid Build Coastguard Worker it->first = tsn;
80*d9f75844SAndroid Build Coastguard Worker return true;
81*d9f75844SAndroid Build Coastguard Worker }
82*d9f75844SAndroid Build Coastguard Worker
83*d9f75844SAndroid Build Coastguard Worker // Need to create a new block in the middle.
84*d9f75844SAndroid Build Coastguard Worker blocks_.emplace(it, tsn, tsn);
85*d9f75844SAndroid Build Coastguard Worker return true;
86*d9f75844SAndroid Build Coastguard Worker }
87*d9f75844SAndroid Build Coastguard Worker
EraseTo(UnwrappedTSN tsn)88*d9f75844SAndroid Build Coastguard Worker void DataTracker::AdditionalTsnBlocks::EraseTo(UnwrappedTSN tsn) {
89*d9f75844SAndroid Build Coastguard Worker // Find the block that is greater than or equals `tsn`.
90*d9f75844SAndroid Build Coastguard Worker auto it = absl::c_lower_bound(
91*d9f75844SAndroid Build Coastguard Worker blocks_, tsn, [&](const TsnRange& elem, const UnwrappedTSN& t) {
92*d9f75844SAndroid Build Coastguard Worker return elem.last < t;
93*d9f75844SAndroid Build Coastguard Worker });
94*d9f75844SAndroid Build Coastguard Worker
95*d9f75844SAndroid Build Coastguard Worker // The block that is found is greater or equal (or possibly ::end, when no
96*d9f75844SAndroid Build Coastguard Worker // block is greater or equal). All blocks before this block can be safely
97*d9f75844SAndroid Build Coastguard Worker // removed. the TSN might be within this block, so possibly truncate it.
98*d9f75844SAndroid Build Coastguard Worker bool tsn_is_within_block = it != blocks_.end() && tsn >= it->first;
99*d9f75844SAndroid Build Coastguard Worker blocks_.erase(blocks_.begin(), it);
100*d9f75844SAndroid Build Coastguard Worker
101*d9f75844SAndroid Build Coastguard Worker if (tsn_is_within_block) {
102*d9f75844SAndroid Build Coastguard Worker blocks_.front().first = tsn.next_value();
103*d9f75844SAndroid Build Coastguard Worker }
104*d9f75844SAndroid Build Coastguard Worker }
105*d9f75844SAndroid Build Coastguard Worker
PopFront()106*d9f75844SAndroid Build Coastguard Worker void DataTracker::AdditionalTsnBlocks::PopFront() {
107*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(!blocks_.empty());
108*d9f75844SAndroid Build Coastguard Worker blocks_.erase(blocks_.begin());
109*d9f75844SAndroid Build Coastguard Worker }
110*d9f75844SAndroid Build Coastguard Worker
IsTSNValid(TSN tsn) const111*d9f75844SAndroid Build Coastguard Worker bool DataTracker::IsTSNValid(TSN tsn) const {
112*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN unwrapped_tsn = tsn_unwrapper_.PeekUnwrap(tsn);
113*d9f75844SAndroid Build Coastguard Worker
114*d9f75844SAndroid Build Coastguard Worker // Note that this method doesn't return `false` for old DATA chunks, as those
115*d9f75844SAndroid Build Coastguard Worker // are actually valid, and receiving those may affect the generated SACK
116*d9f75844SAndroid Build Coastguard Worker // response (by setting "duplicate TSNs").
117*d9f75844SAndroid Build Coastguard Worker
118*d9f75844SAndroid Build Coastguard Worker uint32_t difference =
119*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN::Difference(unwrapped_tsn, last_cumulative_acked_tsn_);
120*d9f75844SAndroid Build Coastguard Worker if (difference > kMaxAcceptedOutstandingFragments) {
121*d9f75844SAndroid Build Coastguard Worker return false;
122*d9f75844SAndroid Build Coastguard Worker }
123*d9f75844SAndroid Build Coastguard Worker return true;
124*d9f75844SAndroid Build Coastguard Worker }
125*d9f75844SAndroid Build Coastguard Worker
Observe(TSN tsn,AnyDataChunk::ImmediateAckFlag immediate_ack)126*d9f75844SAndroid Build Coastguard Worker bool DataTracker::Observe(TSN tsn,
127*d9f75844SAndroid Build Coastguard Worker AnyDataChunk::ImmediateAckFlag immediate_ack) {
128*d9f75844SAndroid Build Coastguard Worker bool is_duplicate = false;
129*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN unwrapped_tsn = tsn_unwrapper_.Unwrap(tsn);
130*d9f75844SAndroid Build Coastguard Worker
131*d9f75844SAndroid Build Coastguard Worker // IsTSNValid must be called prior to calling this method.
132*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(
133*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN::Difference(unwrapped_tsn, last_cumulative_acked_tsn_) <=
134*d9f75844SAndroid Build Coastguard Worker kMaxAcceptedOutstandingFragments);
135*d9f75844SAndroid Build Coastguard Worker
136*d9f75844SAndroid Build Coastguard Worker // Old chunk already seen before?
137*d9f75844SAndroid Build Coastguard Worker if (unwrapped_tsn <= last_cumulative_acked_tsn_) {
138*d9f75844SAndroid Build Coastguard Worker if (duplicate_tsns_.size() < kMaxDuplicateTsnReported) {
139*d9f75844SAndroid Build Coastguard Worker duplicate_tsns_.insert(unwrapped_tsn.Wrap());
140*d9f75844SAndroid Build Coastguard Worker }
141*d9f75844SAndroid Build Coastguard Worker // https://datatracker.ietf.org/doc/html/rfc4960#section-6.2
142*d9f75844SAndroid Build Coastguard Worker // "When a packet arrives with duplicate DATA chunk(s) and with no new DATA
143*d9f75844SAndroid Build Coastguard Worker // chunk(s), the endpoint MUST immediately send a SACK with no delay. If a
144*d9f75844SAndroid Build Coastguard Worker // packet arrives with duplicate DATA chunk(s) bundled with new DATA chunks,
145*d9f75844SAndroid Build Coastguard Worker // the endpoint MAY immediately send a SACK."
146*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate, "duplicate data");
147*d9f75844SAndroid Build Coastguard Worker is_duplicate = true;
148*d9f75844SAndroid Build Coastguard Worker } else {
149*d9f75844SAndroid Build Coastguard Worker if (unwrapped_tsn == last_cumulative_acked_tsn_.next_value()) {
150*d9f75844SAndroid Build Coastguard Worker last_cumulative_acked_tsn_ = unwrapped_tsn;
151*d9f75844SAndroid Build Coastguard Worker // The cumulative acked tsn may be moved even further, if a gap was
152*d9f75844SAndroid Build Coastguard Worker // filled.
153*d9f75844SAndroid Build Coastguard Worker if (!additional_tsn_blocks_.empty() &&
154*d9f75844SAndroid Build Coastguard Worker additional_tsn_blocks_.front().first ==
155*d9f75844SAndroid Build Coastguard Worker last_cumulative_acked_tsn_.next_value()) {
156*d9f75844SAndroid Build Coastguard Worker last_cumulative_acked_tsn_ = additional_tsn_blocks_.front().last;
157*d9f75844SAndroid Build Coastguard Worker additional_tsn_blocks_.PopFront();
158*d9f75844SAndroid Build Coastguard Worker }
159*d9f75844SAndroid Build Coastguard Worker } else {
160*d9f75844SAndroid Build Coastguard Worker bool inserted = additional_tsn_blocks_.Add(unwrapped_tsn);
161*d9f75844SAndroid Build Coastguard Worker if (!inserted) {
162*d9f75844SAndroid Build Coastguard Worker // Already seen before.
163*d9f75844SAndroid Build Coastguard Worker if (duplicate_tsns_.size() < kMaxDuplicateTsnReported) {
164*d9f75844SAndroid Build Coastguard Worker duplicate_tsns_.insert(unwrapped_tsn.Wrap());
165*d9f75844SAndroid Build Coastguard Worker }
166*d9f75844SAndroid Build Coastguard Worker // https://datatracker.ietf.org/doc/html/rfc4960#section-6.2
167*d9f75844SAndroid Build Coastguard Worker // "When a packet arrives with duplicate DATA chunk(s) and with no new
168*d9f75844SAndroid Build Coastguard Worker // DATA chunk(s), the endpoint MUST immediately send a SACK with no
169*d9f75844SAndroid Build Coastguard Worker // delay. If a packet arrives with duplicate DATA chunk(s) bundled with
170*d9f75844SAndroid Build Coastguard Worker // new DATA chunks, the endpoint MAY immediately send a SACK."
171*d9f75844SAndroid Build Coastguard Worker // No need to do this. SACKs are sent immediately on packet loss below.
172*d9f75844SAndroid Build Coastguard Worker is_duplicate = true;
173*d9f75844SAndroid Build Coastguard Worker }
174*d9f75844SAndroid Build Coastguard Worker }
175*d9f75844SAndroid Build Coastguard Worker }
176*d9f75844SAndroid Build Coastguard Worker
177*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc4960#section-6.7
178*d9f75844SAndroid Build Coastguard Worker // "Upon the reception of a new DATA chunk, an endpoint shall examine the
179*d9f75844SAndroid Build Coastguard Worker // continuity of the TSNs received. If the endpoint detects a gap in
180*d9f75844SAndroid Build Coastguard Worker // the received DATA chunk sequence, it SHOULD send a SACK with Gap Ack
181*d9f75844SAndroid Build Coastguard Worker // Blocks immediately. The data receiver continues sending a SACK after
182*d9f75844SAndroid Build Coastguard Worker // receipt of each SCTP packet that doesn't fill the gap."
183*d9f75844SAndroid Build Coastguard Worker if (!additional_tsn_blocks_.empty()) {
184*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate, "packet loss");
185*d9f75844SAndroid Build Coastguard Worker }
186*d9f75844SAndroid Build Coastguard Worker
187*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc7053#section-5.2
188*d9f75844SAndroid Build Coastguard Worker // "Upon receipt of an SCTP packet containing a DATA chunk with the I
189*d9f75844SAndroid Build Coastguard Worker // bit set, the receiver SHOULD NOT delay the sending of the corresponding
190*d9f75844SAndroid Build Coastguard Worker // SACK chunk, i.e., the receiver SHOULD immediately respond with the
191*d9f75844SAndroid Build Coastguard Worker // corresponding SACK chunk."
192*d9f75844SAndroid Build Coastguard Worker if (*immediate_ack) {
193*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate, "immediate-ack bit set");
194*d9f75844SAndroid Build Coastguard Worker }
195*d9f75844SAndroid Build Coastguard Worker
196*d9f75844SAndroid Build Coastguard Worker if (!seen_packet_) {
197*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc4960#section-5.1
198*d9f75844SAndroid Build Coastguard Worker // "After the reception of the first DATA chunk in an association the
199*d9f75844SAndroid Build Coastguard Worker // endpoint MUST immediately respond with a SACK to acknowledge the DATA
200*d9f75844SAndroid Build Coastguard Worker // chunk."
201*d9f75844SAndroid Build Coastguard Worker seen_packet_ = true;
202*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate, "first DATA chunk");
203*d9f75844SAndroid Build Coastguard Worker }
204*d9f75844SAndroid Build Coastguard Worker
205*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc4960#section-6.2
206*d9f75844SAndroid Build Coastguard Worker // "Specifically, an acknowledgement SHOULD be generated for at least
207*d9f75844SAndroid Build Coastguard Worker // every second packet (not every second DATA chunk) received, and SHOULD be
208*d9f75844SAndroid Build Coastguard Worker // generated within 200 ms of the arrival of any unacknowledged DATA chunk."
209*d9f75844SAndroid Build Coastguard Worker if (ack_state_ == AckState::kIdle) {
210*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kBecomingDelayed, "received DATA when idle");
211*d9f75844SAndroid Build Coastguard Worker } else if (ack_state_ == AckState::kDelayed) {
212*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate, "received DATA when already delayed");
213*d9f75844SAndroid Build Coastguard Worker }
214*d9f75844SAndroid Build Coastguard Worker return !is_duplicate;
215*d9f75844SAndroid Build Coastguard Worker }
216*d9f75844SAndroid Build Coastguard Worker
HandleForwardTsn(TSN new_cumulative_ack)217*d9f75844SAndroid Build Coastguard Worker void DataTracker::HandleForwardTsn(TSN new_cumulative_ack) {
218*d9f75844SAndroid Build Coastguard Worker // ForwardTSN is sent to make the receiver (this socket) "forget" about partly
219*d9f75844SAndroid Build Coastguard Worker // received (or not received at all) data, up until `new_cumulative_ack`.
220*d9f75844SAndroid Build Coastguard Worker
221*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN unwrapped_tsn = tsn_unwrapper_.Unwrap(new_cumulative_ack);
222*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN prev_last_cum_ack_tsn = last_cumulative_acked_tsn_;
223*d9f75844SAndroid Build Coastguard Worker
224*d9f75844SAndroid Build Coastguard Worker // Old chunk already seen before?
225*d9f75844SAndroid Build Coastguard Worker if (unwrapped_tsn <= last_cumulative_acked_tsn_) {
226*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc3758#section-3.6
227*d9f75844SAndroid Build Coastguard Worker // "Note, if the "New Cumulative TSN" value carried in the arrived
228*d9f75844SAndroid Build Coastguard Worker // FORWARD TSN chunk is found to be behind or at the current cumulative TSN
229*d9f75844SAndroid Build Coastguard Worker // point, the data receiver MUST treat this FORWARD TSN as out-of-date and
230*d9f75844SAndroid Build Coastguard Worker // MUST NOT update its Cumulative TSN. The receiver SHOULD send a SACK to
231*d9f75844SAndroid Build Coastguard Worker // its peer (the sender of the FORWARD TSN) since such a duplicate may
232*d9f75844SAndroid Build Coastguard Worker // indicate the previous SACK was lost in the network."
233*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate,
234*d9f75844SAndroid Build Coastguard Worker "FORWARD_TSN new_cumulative_tsn was behind");
235*d9f75844SAndroid Build Coastguard Worker return;
236*d9f75844SAndroid Build Coastguard Worker }
237*d9f75844SAndroid Build Coastguard Worker
238*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc3758#section-3.6
239*d9f75844SAndroid Build Coastguard Worker // "When a FORWARD TSN chunk arrives, the data receiver MUST first update
240*d9f75844SAndroid Build Coastguard Worker // its cumulative TSN point to the value carried in the FORWARD TSN chunk, and
241*d9f75844SAndroid Build Coastguard Worker // then MUST further advance its cumulative TSN point locally if possible, as
242*d9f75844SAndroid Build Coastguard Worker // shown by the following example..."
243*d9f75844SAndroid Build Coastguard Worker
244*d9f75844SAndroid Build Coastguard Worker // The `new_cumulative_ack` will become the current
245*d9f75844SAndroid Build Coastguard Worker // `last_cumulative_acked_tsn_`, and if there have been prior "gaps" that are
246*d9f75844SAndroid Build Coastguard Worker // now overlapping with the new value, remove them.
247*d9f75844SAndroid Build Coastguard Worker last_cumulative_acked_tsn_ = unwrapped_tsn;
248*d9f75844SAndroid Build Coastguard Worker additional_tsn_blocks_.EraseTo(unwrapped_tsn);
249*d9f75844SAndroid Build Coastguard Worker
250*d9f75844SAndroid Build Coastguard Worker // See if the `last_cumulative_acked_tsn_` can be moved even further:
251*d9f75844SAndroid Build Coastguard Worker if (!additional_tsn_blocks_.empty() &&
252*d9f75844SAndroid Build Coastguard Worker additional_tsn_blocks_.front().first ==
253*d9f75844SAndroid Build Coastguard Worker last_cumulative_acked_tsn_.next_value()) {
254*d9f75844SAndroid Build Coastguard Worker last_cumulative_acked_tsn_ = additional_tsn_blocks_.front().last;
255*d9f75844SAndroid Build Coastguard Worker additional_tsn_blocks_.PopFront();
256*d9f75844SAndroid Build Coastguard Worker }
257*d9f75844SAndroid Build Coastguard Worker
258*d9f75844SAndroid Build Coastguard Worker RTC_DLOG(LS_VERBOSE) << log_prefix_ << "FORWARD_TSN, cum_ack_tsn="
259*d9f75844SAndroid Build Coastguard Worker << *prev_last_cum_ack_tsn.Wrap() << "->"
260*d9f75844SAndroid Build Coastguard Worker << *new_cumulative_ack << "->"
261*d9f75844SAndroid Build Coastguard Worker << *last_cumulative_acked_tsn_.Wrap();
262*d9f75844SAndroid Build Coastguard Worker
263*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc3758#section-3.6
264*d9f75844SAndroid Build Coastguard Worker // "Any time a FORWARD TSN chunk arrives, for the purposes of sending a
265*d9f75844SAndroid Build Coastguard Worker // SACK, the receiver MUST follow the same rules as if a DATA chunk had been
266*d9f75844SAndroid Build Coastguard Worker // received (i.e., follow the delayed sack rules specified in ..."
267*d9f75844SAndroid Build Coastguard Worker if (ack_state_ == AckState::kIdle) {
268*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kBecomingDelayed,
269*d9f75844SAndroid Build Coastguard Worker "received FORWARD_TSN when idle");
270*d9f75844SAndroid Build Coastguard Worker } else if (ack_state_ == AckState::kDelayed) {
271*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate,
272*d9f75844SAndroid Build Coastguard Worker "received FORWARD_TSN when already delayed");
273*d9f75844SAndroid Build Coastguard Worker }
274*d9f75844SAndroid Build Coastguard Worker }
275*d9f75844SAndroid Build Coastguard Worker
CreateSelectiveAck(size_t a_rwnd)276*d9f75844SAndroid Build Coastguard Worker SackChunk DataTracker::CreateSelectiveAck(size_t a_rwnd) {
277*d9f75844SAndroid Build Coastguard Worker // Note that in SCTP, the receiver side is allowed to discard received data
278*d9f75844SAndroid Build Coastguard Worker // and signal that to the sender, but only chunks that have previously been
279*d9f75844SAndroid Build Coastguard Worker // reported in the gap-ack-blocks. However, this implementation will never do
280*d9f75844SAndroid Build Coastguard Worker // that. So this SACK produced is more like a NR-SACK as explained in
281*d9f75844SAndroid Build Coastguard Worker // https://ieeexplore.ieee.org/document/4697037 and which there is an RFC
282*d9f75844SAndroid Build Coastguard Worker // draft at https://tools.ietf.org/html/draft-tuexen-tsvwg-sctp-multipath-17.
283*d9f75844SAndroid Build Coastguard Worker std::set<TSN> duplicate_tsns;
284*d9f75844SAndroid Build Coastguard Worker duplicate_tsns_.swap(duplicate_tsns);
285*d9f75844SAndroid Build Coastguard Worker
286*d9f75844SAndroid Build Coastguard Worker return SackChunk(last_cumulative_acked_tsn_.Wrap(), a_rwnd,
287*d9f75844SAndroid Build Coastguard Worker CreateGapAckBlocks(), std::move(duplicate_tsns));
288*d9f75844SAndroid Build Coastguard Worker }
289*d9f75844SAndroid Build Coastguard Worker
CreateGapAckBlocks() const290*d9f75844SAndroid Build Coastguard Worker std::vector<SackChunk::GapAckBlock> DataTracker::CreateGapAckBlocks() const {
291*d9f75844SAndroid Build Coastguard Worker const auto& blocks = additional_tsn_blocks_.blocks();
292*d9f75844SAndroid Build Coastguard Worker std::vector<SackChunk::GapAckBlock> gap_ack_blocks;
293*d9f75844SAndroid Build Coastguard Worker gap_ack_blocks.reserve(std::min(blocks.size(), kMaxGapAckBlocksReported));
294*d9f75844SAndroid Build Coastguard Worker for (size_t i = 0; i < blocks.size() && i < kMaxGapAckBlocksReported; ++i) {
295*d9f75844SAndroid Build Coastguard Worker auto start_diff =
296*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN::Difference(blocks[i].first, last_cumulative_acked_tsn_);
297*d9f75844SAndroid Build Coastguard Worker auto end_diff =
298*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN::Difference(blocks[i].last, last_cumulative_acked_tsn_);
299*d9f75844SAndroid Build Coastguard Worker gap_ack_blocks.emplace_back(static_cast<uint16_t>(start_diff),
300*d9f75844SAndroid Build Coastguard Worker static_cast<uint16_t>(end_diff));
301*d9f75844SAndroid Build Coastguard Worker }
302*d9f75844SAndroid Build Coastguard Worker
303*d9f75844SAndroid Build Coastguard Worker return gap_ack_blocks;
304*d9f75844SAndroid Build Coastguard Worker }
305*d9f75844SAndroid Build Coastguard Worker
ShouldSendAck(bool also_if_delayed)306*d9f75844SAndroid Build Coastguard Worker bool DataTracker::ShouldSendAck(bool also_if_delayed) {
307*d9f75844SAndroid Build Coastguard Worker if (ack_state_ == AckState::kImmediate ||
308*d9f75844SAndroid Build Coastguard Worker (also_if_delayed && (ack_state_ == AckState::kBecomingDelayed ||
309*d9f75844SAndroid Build Coastguard Worker ack_state_ == AckState::kDelayed))) {
310*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kIdle, "sending SACK");
311*d9f75844SAndroid Build Coastguard Worker return true;
312*d9f75844SAndroid Build Coastguard Worker }
313*d9f75844SAndroid Build Coastguard Worker
314*d9f75844SAndroid Build Coastguard Worker return false;
315*d9f75844SAndroid Build Coastguard Worker }
316*d9f75844SAndroid Build Coastguard Worker
will_increase_cum_ack_tsn(TSN tsn) const317*d9f75844SAndroid Build Coastguard Worker bool DataTracker::will_increase_cum_ack_tsn(TSN tsn) const {
318*d9f75844SAndroid Build Coastguard Worker UnwrappedTSN unwrapped = tsn_unwrapper_.PeekUnwrap(tsn);
319*d9f75844SAndroid Build Coastguard Worker return unwrapped == last_cumulative_acked_tsn_.next_value();
320*d9f75844SAndroid Build Coastguard Worker }
321*d9f75844SAndroid Build Coastguard Worker
ForceImmediateSack()322*d9f75844SAndroid Build Coastguard Worker void DataTracker::ForceImmediateSack() {
323*d9f75844SAndroid Build Coastguard Worker ack_state_ = AckState::kImmediate;
324*d9f75844SAndroid Build Coastguard Worker }
325*d9f75844SAndroid Build Coastguard Worker
HandleDelayedAckTimerExpiry()326*d9f75844SAndroid Build Coastguard Worker void DataTracker::HandleDelayedAckTimerExpiry() {
327*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kImmediate, "delayed ack timer expired");
328*d9f75844SAndroid Build Coastguard Worker }
329*d9f75844SAndroid Build Coastguard Worker
ObservePacketEnd()330*d9f75844SAndroid Build Coastguard Worker void DataTracker::ObservePacketEnd() {
331*d9f75844SAndroid Build Coastguard Worker if (ack_state_ == AckState::kBecomingDelayed) {
332*d9f75844SAndroid Build Coastguard Worker UpdateAckState(AckState::kDelayed, "packet end");
333*d9f75844SAndroid Build Coastguard Worker }
334*d9f75844SAndroid Build Coastguard Worker }
335*d9f75844SAndroid Build Coastguard Worker
UpdateAckState(AckState new_state,absl::string_view reason)336*d9f75844SAndroid Build Coastguard Worker void DataTracker::UpdateAckState(AckState new_state, absl::string_view reason) {
337*d9f75844SAndroid Build Coastguard Worker if (new_state != ack_state_) {
338*d9f75844SAndroid Build Coastguard Worker RTC_DLOG(LS_VERBOSE) << log_prefix_ << "State changed from "
339*d9f75844SAndroid Build Coastguard Worker << ToString(ack_state_) << " to "
340*d9f75844SAndroid Build Coastguard Worker << ToString(new_state) << " due to " << reason;
341*d9f75844SAndroid Build Coastguard Worker if (ack_state_ == AckState::kDelayed) {
342*d9f75844SAndroid Build Coastguard Worker delayed_ack_timer_.Stop();
343*d9f75844SAndroid Build Coastguard Worker } else if (new_state == AckState::kDelayed) {
344*d9f75844SAndroid Build Coastguard Worker delayed_ack_timer_.Start();
345*d9f75844SAndroid Build Coastguard Worker }
346*d9f75844SAndroid Build Coastguard Worker ack_state_ = new_state;
347*d9f75844SAndroid Build Coastguard Worker }
348*d9f75844SAndroid Build Coastguard Worker }
349*d9f75844SAndroid Build Coastguard Worker
ToString(AckState ack_state)350*d9f75844SAndroid Build Coastguard Worker absl::string_view DataTracker::ToString(AckState ack_state) {
351*d9f75844SAndroid Build Coastguard Worker switch (ack_state) {
352*d9f75844SAndroid Build Coastguard Worker case AckState::kIdle:
353*d9f75844SAndroid Build Coastguard Worker return "IDLE";
354*d9f75844SAndroid Build Coastguard Worker case AckState::kBecomingDelayed:
355*d9f75844SAndroid Build Coastguard Worker return "BECOMING_DELAYED";
356*d9f75844SAndroid Build Coastguard Worker case AckState::kDelayed:
357*d9f75844SAndroid Build Coastguard Worker return "DELAYED";
358*d9f75844SAndroid Build Coastguard Worker case AckState::kImmediate:
359*d9f75844SAndroid Build Coastguard Worker return "IMMEDIATE";
360*d9f75844SAndroid Build Coastguard Worker }
361*d9f75844SAndroid Build Coastguard Worker }
362*d9f75844SAndroid Build Coastguard Worker
GetHandoverReadiness() const363*d9f75844SAndroid Build Coastguard Worker HandoverReadinessStatus DataTracker::GetHandoverReadiness() const {
364*d9f75844SAndroid Build Coastguard Worker HandoverReadinessStatus status;
365*d9f75844SAndroid Build Coastguard Worker if (!additional_tsn_blocks_.empty()) {
366*d9f75844SAndroid Build Coastguard Worker status.Add(HandoverUnreadinessReason::kDataTrackerTsnBlocksPending);
367*d9f75844SAndroid Build Coastguard Worker }
368*d9f75844SAndroid Build Coastguard Worker return status;
369*d9f75844SAndroid Build Coastguard Worker }
370*d9f75844SAndroid Build Coastguard Worker
AddHandoverState(DcSctpSocketHandoverState & state)371*d9f75844SAndroid Build Coastguard Worker void DataTracker::AddHandoverState(DcSctpSocketHandoverState& state) {
372*d9f75844SAndroid Build Coastguard Worker state.rx.last_cumulative_acked_tsn = last_cumulative_acked_tsn().value();
373*d9f75844SAndroid Build Coastguard Worker state.rx.seen_packet = seen_packet_;
374*d9f75844SAndroid Build Coastguard Worker }
375*d9f75844SAndroid Build Coastguard Worker
RestoreFromState(const DcSctpSocketHandoverState & state)376*d9f75844SAndroid Build Coastguard Worker void DataTracker::RestoreFromState(const DcSctpSocketHandoverState& state) {
377*d9f75844SAndroid Build Coastguard Worker // Validate that the component is in pristine state.
378*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(additional_tsn_blocks_.empty());
379*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(duplicate_tsns_.empty());
380*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(!seen_packet_);
381*d9f75844SAndroid Build Coastguard Worker
382*d9f75844SAndroid Build Coastguard Worker seen_packet_ = state.rx.seen_packet;
383*d9f75844SAndroid Build Coastguard Worker last_cumulative_acked_tsn_ =
384*d9f75844SAndroid Build Coastguard Worker tsn_unwrapper_.Unwrap(TSN(state.rx.last_cumulative_acked_tsn));
385*d9f75844SAndroid Build Coastguard Worker }
386*d9f75844SAndroid Build Coastguard Worker } // namespace dcsctp
387