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