xref: /aosp_15_r20/external/webrtc/net/dcsctp/tx/outstanding_data.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #ifndef NET_DCSCTP_TX_OUTSTANDING_DATA_H_
11 #define NET_DCSCTP_TX_OUTSTANDING_DATA_H_
12 
13 #include <map>
14 #include <set>
15 #include <utility>
16 #include <vector>
17 
18 #include "absl/types/optional.h"
19 #include "net/dcsctp/common/sequence_numbers.h"
20 #include "net/dcsctp/packet/chunk/forward_tsn_chunk.h"
21 #include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h"
22 #include "net/dcsctp/packet/chunk/sack_chunk.h"
23 #include "net/dcsctp/packet/data.h"
24 #include "net/dcsctp/public/types.h"
25 
26 namespace dcsctp {
27 
28 // This class keeps track of outstanding data chunks (sent, not yet acked) and
29 // handles acking, nacking, rescheduling and abandoning.
30 class OutstandingData {
31  public:
32   // State for DATA chunks (message fragments) in the queue - used in tests.
33   enum class State {
34     // The chunk has been sent but not received yet (from the sender's point of
35     // view, as no SACK has been received yet that reference this chunk).
36     kInFlight,
37     // A SACK has been received which explicitly marked this chunk as missing -
38     // it's now NACKED and may be retransmitted if NACKED enough times.
39     kNacked,
40     // A chunk that will be retransmitted when possible.
41     kToBeRetransmitted,
42     // A SACK has been received which explicitly marked this chunk as received.
43     kAcked,
44     // A chunk whose message has expired or has been retransmitted too many
45     // times (RFC3758). It will not be retransmitted anymore.
46     kAbandoned,
47   };
48 
49   // Contains variables scoped to a processing of an incoming SACK.
50   struct AckInfo {
AckInfoAckInfo51     explicit AckInfo(UnwrappedTSN cumulative_tsn_ack)
52         : highest_tsn_acked(cumulative_tsn_ack) {}
53 
54     // Bytes acked by increasing cumulative_tsn_ack and gap_ack_blocks.
55     size_t bytes_acked = 0;
56 
57     // Indicates if this SACK indicates that packet loss has occurred. Just
58     // because a packet is missing in the SACK doesn't necessarily mean that
59     // there is packet loss as that packet might be in-flight and received
60     // out-of-order. But when it has been reported missing consecutive times, it
61     // will eventually be considered "lost" and this will be set.
62     bool has_packet_loss = false;
63 
64     // Highest TSN Newly Acknowledged, an SCTP variable.
65     UnwrappedTSN highest_tsn_acked;
66 
67     // The set of lifecycle IDs that were acked using cumulative_tsn_ack.
68     std::vector<LifecycleId> acked_lifecycle_ids;
69     // The set of lifecycle IDs that were acked, but had been abandoned.
70     std::vector<LifecycleId> abandoned_lifecycle_ids;
71   };
72 
OutstandingData(size_t data_chunk_header_size,UnwrappedTSN next_tsn,UnwrappedTSN last_cumulative_tsn_ack,std::function<bool (IsUnordered,StreamID,MID)> discard_from_send_queue)73   OutstandingData(
74       size_t data_chunk_header_size,
75       UnwrappedTSN next_tsn,
76       UnwrappedTSN last_cumulative_tsn_ack,
77       std::function<bool(IsUnordered, StreamID, MID)> discard_from_send_queue)
78       : data_chunk_header_size_(data_chunk_header_size),
79         next_tsn_(next_tsn),
80         last_cumulative_tsn_ack_(last_cumulative_tsn_ack),
81         discard_from_send_queue_(std::move(discard_from_send_queue)) {}
82 
83   AckInfo HandleSack(
84       UnwrappedTSN cumulative_tsn_ack,
85       rtc::ArrayView<const SackChunk::GapAckBlock> gap_ack_blocks,
86       bool is_in_fast_recovery);
87 
88   // Returns as many of the chunks that are eligible for fast retransmissions
89   // and that would fit in a single packet of `max_size`. The eligible chunks
90   // that didn't fit will be marked for (normal) retransmission and will not be
91   // returned if this method is called again.
92   std::vector<std::pair<TSN, Data>> GetChunksToBeFastRetransmitted(
93       size_t max_size);
94 
95   // Given `max_size` of space left in a packet, which chunks can be added to
96   // it?
97   std::vector<std::pair<TSN, Data>> GetChunksToBeRetransmitted(size_t max_size);
98 
outstanding_bytes()99   size_t outstanding_bytes() const { return outstanding_bytes_; }
100 
101   // Returns the number of DATA chunks that are in-flight.
outstanding_items()102   size_t outstanding_items() const { return outstanding_items_; }
103 
104   // Given the current time `now_ms`, expire and abandon outstanding (sent at
105   // least once) chunks that have a limited lifetime.
106   void ExpireOutstandingChunks(TimeMs now);
107 
empty()108   bool empty() const { return outstanding_data_.empty(); }
109 
has_data_to_be_fast_retransmitted()110   bool has_data_to_be_fast_retransmitted() const {
111     return !to_be_fast_retransmitted_.empty();
112   }
113 
has_data_to_be_retransmitted()114   bool has_data_to_be_retransmitted() const {
115     return !to_be_retransmitted_.empty() || !to_be_fast_retransmitted_.empty();
116   }
117 
last_cumulative_tsn_ack()118   UnwrappedTSN last_cumulative_tsn_ack() const {
119     return last_cumulative_tsn_ack_;
120   }
121 
next_tsn()122   UnwrappedTSN next_tsn() const { return next_tsn_; }
123 
124   UnwrappedTSN highest_outstanding_tsn() const;
125 
126   // Schedules `data` to be sent, with the provided partial reliability
127   // parameters. Returns the TSN if the item was actually added and scheduled to
128   // be sent, and absl::nullopt if it shouldn't be sent.
129   absl::optional<UnwrappedTSN> Insert(
130       const Data& data,
131       TimeMs time_sent,
132       MaxRetransmits max_retransmissions = MaxRetransmits::NoLimit(),
133       TimeMs expires_at = TimeMs::InfiniteFuture(),
134       LifecycleId lifecycle_id = LifecycleId::NotSet());
135 
136   // Nacks all outstanding data.
137   void NackAll();
138 
139   // Creates a FORWARD-TSN chunk.
140   ForwardTsnChunk CreateForwardTsn() const;
141 
142   // Creates an I-FORWARD-TSN chunk.
143   IForwardTsnChunk CreateIForwardTsn() const;
144 
145   // Given the current time and a TSN, it returns the measured RTT between when
146   // the chunk was sent and now. It takes into acccount Karn's algorithm, so if
147   // the chunk has ever been retransmitted, it will return absl::nullopt.
148   absl::optional<DurationMs> MeasureRTT(TimeMs now, UnwrappedTSN tsn) const;
149 
150   // Returns the internal state of all queued chunks. This is only used in
151   // unit-tests.
152   std::vector<std::pair<TSN, State>> GetChunkStatesForTesting() const;
153 
154   // Returns true if the next chunk that is not acked by the peer has been
155   // abandoned, which means that a FORWARD-TSN should be sent.
156   bool ShouldSendForwardTsn() const;
157 
158   // Sets the next TSN to be used. This is used in handover.
159   void ResetSequenceNumbers(UnwrappedTSN next_tsn,
160                             UnwrappedTSN last_cumulative_tsn);
161 
162  private:
163   // A fragmented message's DATA chunk while in the retransmission queue, and
164   // its associated metadata.
165   class Item {
166    public:
167     enum class NackAction {
168       kNothing,
169       kRetransmit,
170       kAbandon,
171     };
172 
Item(Data data,TimeMs time_sent,MaxRetransmits max_retransmissions,TimeMs expires_at,LifecycleId lifecycle_id)173     Item(Data data,
174          TimeMs time_sent,
175          MaxRetransmits max_retransmissions,
176          TimeMs expires_at,
177          LifecycleId lifecycle_id)
178         : time_sent_(time_sent),
179           max_retransmissions_(max_retransmissions),
180           expires_at_(expires_at),
181           lifecycle_id_(lifecycle_id),
182           data_(std::move(data)) {}
183 
184     Item(const Item&) = delete;
185     Item& operator=(const Item&) = delete;
186 
time_sent()187     TimeMs time_sent() const { return time_sent_; }
188 
data()189     const Data& data() const { return data_; }
190 
191     // Acks an item.
192     void Ack();
193 
194     // Nacks an item. If it has been nacked enough times, or if `retransmit_now`
195     // is set, it might be marked for retransmission. If the item has reached
196     // its max retransmission value, it will instead be abandoned. The action
197     // performed is indicated as return value.
198     NackAction Nack(bool retransmit_now);
199 
200     // Prepares the item to be retransmitted. Sets it as outstanding and
201     // clears all nack counters.
202     void MarkAsRetransmitted();
203 
204     // Marks this item as abandoned.
205     void Abandon();
206 
is_outstanding()207     bool is_outstanding() const { return ack_state_ == AckState::kUnacked; }
is_acked()208     bool is_acked() const { return ack_state_ == AckState::kAcked; }
is_nacked()209     bool is_nacked() const { return ack_state_ == AckState::kNacked; }
is_abandoned()210     bool is_abandoned() const { return lifecycle_ == Lifecycle::kAbandoned; }
211 
212     // Indicates if this chunk should be retransmitted.
should_be_retransmitted()213     bool should_be_retransmitted() const {
214       return lifecycle_ == Lifecycle::kToBeRetransmitted;
215     }
216     // Indicates if this chunk has ever been retransmitted.
has_been_retransmitted()217     bool has_been_retransmitted() const { return num_retransmissions_ > 0; }
218 
219     // Given the current time, and the current state of this DATA chunk, it will
220     // indicate if it has expired (SCTP Partial Reliability Extension).
221     bool has_expired(TimeMs now) const;
222 
lifecycle_id()223     LifecycleId lifecycle_id() const { return lifecycle_id_; }
224 
225    private:
226     enum class Lifecycle : uint8_t {
227       // The chunk is alive (sent, received, etc)
228       kActive,
229       // The chunk is scheduled to be retransmitted, and will then transition to
230       // become active.
231       kToBeRetransmitted,
232       // The chunk has been abandoned. This is a terminal state.
233       kAbandoned
234     };
235     enum class AckState : uint8_t {
236       // The chunk is in-flight.
237       kUnacked,
238       // The chunk has been received and acknowledged.
239       kAcked,
240       // The chunk has been nacked and is possibly lost.
241       kNacked
242     };
243 
244     // NOTE: This data structure has been optimized for size, by ordering fields
245     // to avoid unnecessary padding.
246 
247     // When the packet was sent, and placed in this queue.
248     const TimeMs time_sent_;
249     // If the message was sent with a maximum number of retransmissions, this is
250     // set to that number. The value zero (0) means that it will never be
251     // retransmitted.
252     const MaxRetransmits max_retransmissions_;
253 
254     // Indicates the life cycle status of this chunk.
255     Lifecycle lifecycle_ = Lifecycle::kActive;
256     // Indicates the presence of this chunk, if it's in flight (Unacked), has
257     // been received (Acked) or is possibly lost (Nacked).
258     AckState ack_state_ = AckState::kUnacked;
259 
260     // The number of times the DATA chunk has been nacked (by having received a
261     // SACK which doesn't include it). Will be cleared on retransmissions.
262     uint8_t nack_count_ = 0;
263     // The number of times the DATA chunk has been retransmitted.
264     uint16_t num_retransmissions_ = 0;
265 
266     // At this exact millisecond, the item is considered expired. If the message
267     // is not to be expired, this is set to the infinite future.
268     const TimeMs expires_at_;
269 
270     // An optional lifecycle id, which may only be set for the last fragment.
271     const LifecycleId lifecycle_id_;
272 
273     // The actual data to send/retransmit.
274     const Data data_;
275   };
276 
277   // Returns how large a chunk will be, serialized, carrying the data
278   size_t GetSerializedChunkSize(const Data& data) const;
279 
280   // Given a `cumulative_tsn_ack` from an incoming SACK, will remove those items
281   // in the retransmission queue up until this value and will update `ack_info`
282   // by setting `bytes_acked_by_cumulative_tsn_ack`.
283   void RemoveAcked(UnwrappedTSN cumulative_tsn_ack, AckInfo& ack_info);
284 
285   // Will mark the chunks covered by the `gap_ack_blocks` from an incoming SACK
286   // as "acked" and update `ack_info` by adding new TSNs to `added_tsns`.
287   void AckGapBlocks(UnwrappedTSN cumulative_tsn_ack,
288                     rtc::ArrayView<const SackChunk::GapAckBlock> gap_ack_blocks,
289                     AckInfo& ack_info);
290 
291   // Mark chunks reported as "missing", as "nacked" or "to be retransmitted"
292   // depending how many times this has happened. Only packets up until
293   // `ack_info.highest_tsn_acked` (highest TSN newly acknowledged) are
294   // nacked/retransmitted. The method will set `ack_info.has_packet_loss`.
295   void NackBetweenAckBlocks(
296       UnwrappedTSN cumulative_tsn_ack,
297       rtc::ArrayView<const SackChunk::GapAckBlock> gap_ack_blocks,
298       bool is_in_fast_recovery,
299       OutstandingData::AckInfo& ack_info);
300 
301   // Process the acknowledgement of the chunk referenced by `iter` and updates
302   // state in `ack_info` and the object's state.
303   void AckChunk(AckInfo& ack_info, std::map<UnwrappedTSN, Item>::iterator iter);
304 
305   // Helper method to process an incoming nack of an item and perform the
306   // correct operations given the action indicated when nacking an item (e.g.
307   // retransmitting or abandoning). The return value indicate if an action was
308   // performed, meaning that packet loss was detected and acted upon. If
309   // `do_fast_retransmit` is set and if the item has been nacked sufficiently
310   // many times so that it should be retransmitted, this will schedule it to be
311   // "fast retransmitted". This is only done just before going into fast
312   // recovery.
313   bool NackItem(UnwrappedTSN tsn,
314                 Item& item,
315                 bool retransmit_now,
316                 bool do_fast_retransmit);
317 
318   // Given that a message fragment, `item` has been abandoned, abandon all other
319   // fragments that share the same message - both never-before-sent fragments
320   // that are still in the SendQueue and outstanding chunks.
321   void AbandonAllFor(const OutstandingData::Item& item);
322 
323   std::vector<std::pair<TSN, Data>> ExtractChunksThatCanFit(
324       std::set<UnwrappedTSN>& chunks,
325       size_t max_size);
326 
327   bool IsConsistent() const;
328 
329   // The size of the data chunk (DATA/I-DATA) header that is used.
330   const size_t data_chunk_header_size_;
331   // Next TSN to used.
332   UnwrappedTSN next_tsn_;
333   // The last cumulative TSN ack number.
334   UnwrappedTSN last_cumulative_tsn_ack_;
335   // Callback when to discard items from the send queue.
336   std::function<bool(IsUnordered, StreamID, MID)> discard_from_send_queue_;
337 
338   std::map<UnwrappedTSN, Item> outstanding_data_;
339   // The number of bytes that are in-flight (sent but not yet acked or nacked).
340   size_t outstanding_bytes_ = 0;
341   // The number of DATA chunks that are in-flight (sent but not yet acked or
342   // nacked).
343   size_t outstanding_items_ = 0;
344   // Data chunks that are eligible for fast retransmission.
345   std::set<UnwrappedTSN> to_be_fast_retransmitted_;
346   // Data chunks that are to be retransmitted.
347   std::set<UnwrappedTSN> to_be_retransmitted_;
348 };
349 }  // namespace dcsctp
350 #endif  // NET_DCSCTP_TX_OUTSTANDING_DATA_H_
351