xref: /aosp_15_r20/external/webrtc/net/dcsctp/rx/data_tracker.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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