blob: 6146d2a839a9c9556aca89e20880e578ab7a3848 [file] [log] [blame]
/*
* 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 <stddef.h>
#include <stdint.h>
#include <cstdint>
#include <set>
#include <string>
#include <vector>
#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/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 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 = 256;
explicit DataTracker(absl::string_view log_prefix,
Timer* delayed_ack_timer,
TSN peer_initial_tsn)
: log_prefix_(std::string(log_prefix) + "dtrack: "),
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.
void 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
void 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());
}
// 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();
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,
};
std::vector<SackChunk::GapAckBlock> CreateGapAckBlocks() const;
void UpdateAckState(AckState new_state, absl::string_view reason);
static absl::string_view ToString(AckState ack_state);
const std::string log_prefix_;
// If a packet has ever been seen.
bool seen_packet_ = false;
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_`.
std::set<UnwrappedTSN> additional_tsns_;
std::set<UnwrappedTSN> duplicates_;
};
} // namespace dcsctp
#endif // NET_DCSCTP_RX_DATA_TRACKER_H_