/* * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef NET_DCSCTP_RX_DATA_TRACKER_H_ #define NET_DCSCTP_RX_DATA_TRACKER_H_ #include #include #include #include #include #include #include #include "absl/strings/string_view.h" #include "net/dcsctp/common/sequence_numbers.h" #include "net/dcsctp/packet/chunk/data_common.h" #include "net/dcsctp/packet/chunk/sack_chunk.h" #include "net/dcsctp/packet/data.h" #include "net/dcsctp/public/dcsctp_handover_state.h" #include "net/dcsctp/timer/timer.h" namespace dcsctp { // Keeps track of received DATA chunks and handles all logic for _when_ to // create SACKs and also _how_ to generate them. // // It only uses TSNs to track delivery and doesn't need to be aware of streams. // // SACKs are optimally sent every second packet on connections with no packet // loss. When packet loss is detected, it's sent for every packet. When SACKs // are not sent directly, a timer is used to send a SACK delayed (by RTO/2, or // 200ms, whatever is smallest). class DataTracker { public: // The maximum number of duplicate TSNs that will be reported in a SACK. static constexpr size_t kMaxDuplicateTsnReported = 20; // The maximum number of gap-ack-blocks that will be reported in a SACK. static constexpr size_t kMaxGapAckBlocksReported = 20; // The maximum number of accepted in-flight DATA chunks. This indicates the // maximum difference from this buffer's last cumulative ack TSN, and any // received data. Data received beyond this limit will be dropped, which will // force the transmitter to send data that actually increases the last // cumulative acked TSN. static constexpr uint32_t kMaxAcceptedOutstandingFragments = 100000; DataTracker(absl::string_view log_prefix, Timer* delayed_ack_timer, TSN peer_initial_tsn) : log_prefix_(log_prefix), seen_packet_(false), delayed_ack_timer_(*delayed_ack_timer), last_cumulative_acked_tsn_( tsn_unwrapper_.Unwrap(TSN(*peer_initial_tsn - 1))) {} // Indicates if the provided TSN is valid. If this return false, the data // should be dropped and not added to any other buffers, which essentially // means that there is intentional packet loss. bool IsTSNValid(TSN tsn) const; // Call for every incoming data chunk. Returns `true` if `tsn` was seen for // the first time, and `false` if it has been seen before (a duplicate `tsn`). bool Observe(TSN tsn, AnyDataChunk::ImmediateAckFlag immediate_ack = AnyDataChunk::ImmediateAckFlag(false)); // Called at the end of processing an SCTP packet. void ObservePacketEnd(); // Called for incoming FORWARD-TSN/I-FORWARD-TSN chunks. Indicates if the // chunk had any effect. bool HandleForwardTsn(TSN new_cumulative_ack); // Indicates if a SACK should be sent. There may be other reasons to send a // SACK, but if this function indicates so, it should be sent as soon as // possible. Calling this function will make it clear a flag so that if it's // called again, it will probably return false. // // If the delayed ack timer is running, this method will return false _unless_ // `also_if_delayed` is set to true. Then it will return true as well. bool ShouldSendAck(bool also_if_delayed = false); // Returns the last cumulative ack TSN - the last seen data chunk's TSN // value before any packet loss was detected. TSN last_cumulative_acked_tsn() const { return TSN(last_cumulative_acked_tsn_.Wrap()); } bool IsLaterThanCumulativeAckedTsn(TSN tsn) const { return tsn_unwrapper_.PeekUnwrap(tsn) > last_cumulative_acked_tsn_; } // Returns true if the received `tsn` would increase the cumulative ack TSN. bool will_increase_cum_ack_tsn(TSN tsn) const; // Forces `ShouldSendSack` to return true. void ForceImmediateSack(); // Note that this will clear `duplicates_`, so every SackChunk that is // consumed must be sent. SackChunk CreateSelectiveAck(size_t a_rwnd); void HandleDelayedAckTimerExpiry(); HandoverReadinessStatus GetHandoverReadiness() const; void AddHandoverState(DcSctpSocketHandoverState& state); void RestoreFromState(const DcSctpSocketHandoverState& state); private: enum class AckState { // No need to send an ACK. kIdle, // Has received data chunks (but not yet end of packet). kBecomingDelayed, // Has received data chunks and the end of a packet. Delayed ack timer is // running and a SACK will be sent on expiry, or if DATA is sent, or after // next packet with data. kDelayed, // Send a SACK immediately after handling this packet. kImmediate, }; // Represents ranges of TSNs that have been received that are not directly // following the last cumulative acked TSN. This information is returned to // the sender in the "gap ack blocks" in the SACK chunk. The blocks are always // non-overlapping and non-adjacent. class AdditionalTsnBlocks { public: // Represents an inclusive range of received TSNs, i.e. [first, last]. struct TsnRange { TsnRange(UnwrappedTSN first, UnwrappedTSN last) : first(first), last(last) {} UnwrappedTSN first; UnwrappedTSN last; }; // Adds a TSN to the set. This will try to expand any existing block and // might merge blocks to ensure that all blocks are non-adjacent. If a // current block can't be expanded, a new block is created. // // The return value indicates if `tsn` was added. If false is returned, the // `tsn` was already represented in one of the blocks. bool Add(UnwrappedTSN tsn); // Erases all TSNs up to, and including `tsn`. This will remove all blocks // that are completely below `tsn` and may truncate a block where `tsn` is // within that block. In that case, the frontmost block's start TSN will be // the next following tsn after `tsn`. void EraseTo(UnwrappedTSN tsn); // Removes the first block. Must not be called on an empty set. void PopFront(); const std::vector& blocks() const { return blocks_; } bool empty() const { return blocks_.empty(); } const TsnRange& front() const { return blocks_.front(); } private: // A sorted vector of non-overlapping and non-adjacent blocks. std::vector blocks_; }; std::vector CreateGapAckBlocks() const; void UpdateAckState(AckState new_state, absl::string_view reason); static absl::string_view ToString(AckState ack_state); const absl::string_view log_prefix_; // If a packet has ever been seen. bool seen_packet_; Timer& delayed_ack_timer_; AckState ack_state_ = AckState::kIdle; UnwrappedTSN::Unwrapper tsn_unwrapper_; // All TSNs up until (and including) this value have been seen. UnwrappedTSN last_cumulative_acked_tsn_; // Received TSNs that are not directly following `last_cumulative_acked_tsn_`. AdditionalTsnBlocks additional_tsn_blocks_; std::set duplicate_tsns_; }; } // namespace dcsctp #endif // NET_DCSCTP_RX_DATA_TRACKER_H_