258 lines
10 KiB
C++
258 lines
10 KiB
C++
/*
|
|
* 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_TX_RETRANSMISSION_QUEUE_H_
|
|
#define NET_DCSCTP_TX_RETRANSMISSION_QUEUE_H_
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/types/optional.h"
|
|
#include "api/array_view.h"
|
|
#include "net/dcsctp/common/sequence_numbers.h"
|
|
#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h"
|
|
#include "net/dcsctp/packet/chunk/iforward_tsn_chunk.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/public/dcsctp_options.h"
|
|
#include "net/dcsctp/public/dcsctp_socket.h"
|
|
#include "net/dcsctp/timer/timer.h"
|
|
#include "net/dcsctp/tx/outstanding_data.h"
|
|
#include "net/dcsctp/tx/retransmission_timeout.h"
|
|
#include "net/dcsctp/tx/send_queue.h"
|
|
|
|
namespace dcsctp {
|
|
|
|
// The RetransmissionQueue manages all DATA/I-DATA chunks that are in-flight and
|
|
// schedules them to be retransmitted if necessary. Chunks are retransmitted
|
|
// when they have been lost for a number of consecutive SACKs, or when the
|
|
// retransmission timer, `t3_rtx` expires.
|
|
//
|
|
// As congestion control is tightly connected with the state of transmitted
|
|
// packets, that's also managed here to limit the amount of data that is
|
|
// in-flight (sent, but not yet acknowledged).
|
|
class RetransmissionQueue {
|
|
public:
|
|
static constexpr size_t kMinimumFragmentedPayload = 10;
|
|
using State = OutstandingData::State;
|
|
// Creates a RetransmissionQueue which will send data using `my_initial_tsn`
|
|
// (or a value from `DcSctpSocketHandoverState` if given) as the first TSN
|
|
// to use for sent fragments. It will poll data from `send_queue`. When SACKs
|
|
// are received, it will estimate the RTT, and call `on_new_rtt`. When an
|
|
// outstanding chunk has been ACKed, it will call
|
|
// `on_clear_retransmission_counter` and will also use `t3_rtx`, which is the
|
|
// SCTP retransmission timer to manage retransmissions.
|
|
RetransmissionQueue(absl::string_view log_prefix,
|
|
DcSctpSocketCallbacks* callbacks,
|
|
TSN my_initial_tsn,
|
|
size_t a_rwnd,
|
|
SendQueue& send_queue,
|
|
std::function<void(DurationMs rtt)> on_new_rtt,
|
|
std::function<void()> on_clear_retransmission_counter,
|
|
Timer& t3_rtx,
|
|
const DcSctpOptions& options,
|
|
bool supports_partial_reliability = true,
|
|
bool use_message_interleaving = false);
|
|
|
|
// Handles a received SACK. Returns true if the `sack` was processed and
|
|
// false if it was discarded due to received out-of-order and not relevant.
|
|
bool HandleSack(TimeMs now, const SackChunk& sack);
|
|
|
|
// Handles an expired retransmission timer.
|
|
void HandleT3RtxTimerExpiry();
|
|
|
|
bool has_data_to_be_fast_retransmitted() const {
|
|
return outstanding_data_.has_data_to_be_fast_retransmitted();
|
|
}
|
|
|
|
// Returns a list of chunks to "fast retransmit" that would fit in one SCTP
|
|
// packet with `bytes_in_packet` bytes available. The current value
|
|
// of `cwnd` is ignored.
|
|
std::vector<std::pair<TSN, Data>> GetChunksForFastRetransmit(
|
|
size_t bytes_in_packet);
|
|
|
|
// Returns a list of chunks to send that would fit in one SCTP packet with
|
|
// `bytes_remaining_in_packet` bytes available. This may be further limited by
|
|
// the congestion control windows. Note that `ShouldSendForwardTSN` must be
|
|
// called prior to this method, to abandon expired chunks, as this method will
|
|
// not expire any chunks.
|
|
std::vector<std::pair<TSN, Data>> GetChunksToSend(
|
|
TimeMs now,
|
|
size_t bytes_remaining_in_packet);
|
|
|
|
// Returns the internal state of all queued chunks. This is only used in
|
|
// unit-tests.
|
|
std::vector<std::pair<TSN, OutstandingData::State>> GetChunkStatesForTesting()
|
|
const {
|
|
return outstanding_data_.GetChunkStatesForTesting();
|
|
}
|
|
|
|
// Returns the next TSN that will be allocated for sent DATA chunks.
|
|
TSN next_tsn() const { return outstanding_data_.next_tsn().Wrap(); }
|
|
|
|
// Returns the size of the congestion window, in bytes. This is the number of
|
|
// bytes that may be in-flight.
|
|
size_t cwnd() const { return cwnd_; }
|
|
|
|
// Overrides the current congestion window size.
|
|
void set_cwnd(size_t cwnd) { cwnd_ = cwnd; }
|
|
|
|
// Returns the current receiver window size.
|
|
size_t rwnd() const { return rwnd_; }
|
|
|
|
// Returns the number of bytes of packets that are in-flight.
|
|
size_t outstanding_bytes() const {
|
|
return outstanding_data_.outstanding_bytes();
|
|
}
|
|
|
|
// Returns the number of DATA chunks that are in-flight.
|
|
size_t outstanding_items() const {
|
|
return outstanding_data_.outstanding_items();
|
|
}
|
|
|
|
// Indicates if the congestion control algorithm allows data to be sent.
|
|
bool can_send_data() const;
|
|
|
|
// Given the current time `now`, it will evaluate if there are chunks that
|
|
// have expired and that need to be discarded. It returns true if a
|
|
// FORWARD-TSN should be sent.
|
|
bool ShouldSendForwardTsn(TimeMs now);
|
|
|
|
// Creates a FORWARD-TSN chunk.
|
|
ForwardTsnChunk CreateForwardTsn() const {
|
|
return outstanding_data_.CreateForwardTsn();
|
|
}
|
|
|
|
// Creates an I-FORWARD-TSN chunk.
|
|
IForwardTsnChunk CreateIForwardTsn() const {
|
|
return outstanding_data_.CreateIForwardTsn();
|
|
}
|
|
|
|
// See the SendQueue for a longer description of these methods related
|
|
// to stream resetting.
|
|
void PrepareResetStream(StreamID stream_id);
|
|
bool HasStreamsReadyToBeReset() const;
|
|
std::vector<StreamID> GetStreamsReadyToBeReset() const {
|
|
return send_queue_.GetStreamsReadyToBeReset();
|
|
}
|
|
void CommitResetStreams();
|
|
void RollbackResetStreams();
|
|
|
|
HandoverReadinessStatus GetHandoverReadiness() const;
|
|
|
|
void AddHandoverState(DcSctpSocketHandoverState& state);
|
|
void RestoreFromState(const DcSctpSocketHandoverState& state);
|
|
|
|
private:
|
|
enum class CongestionAlgorithmPhase {
|
|
kSlowStart,
|
|
kCongestionAvoidance,
|
|
};
|
|
|
|
bool IsConsistent() const;
|
|
|
|
// Returns how large a chunk will be, serialized, carrying the data
|
|
size_t GetSerializedChunkSize(const Data& data) const;
|
|
|
|
// Indicates if the congestion control algorithm is in "fast recovery".
|
|
bool is_in_fast_recovery() const {
|
|
return fast_recovery_exit_tsn_.has_value();
|
|
}
|
|
|
|
// Indicates if the provided SACK is valid given what has previously been
|
|
// received. If it returns false, the SACK is most likely a duplicate of
|
|
// something already seen, so this returning false doesn't necessarily mean
|
|
// that the SACK is illegal.
|
|
bool IsSackValid(const SackChunk& sack) const;
|
|
|
|
// When a SACK chunk is received, this method will be called which _may_ call
|
|
// into the `RetransmissionTimeout` to update the RTO.
|
|
void UpdateRTT(TimeMs now, UnwrappedTSN cumulative_tsn_ack);
|
|
|
|
// If the congestion control is in "fast recovery mode", this may be exited
|
|
// now.
|
|
void MaybeExitFastRecovery(UnwrappedTSN cumulative_tsn_ack);
|
|
|
|
// If chunks have been ACKed, stop the retransmission timer.
|
|
void StopT3RtxTimerOnIncreasedCumulativeTsnAck(
|
|
UnwrappedTSN cumulative_tsn_ack);
|
|
|
|
// Update the congestion control algorithm given as the cumulative ack TSN
|
|
// value has increased, as reported in an incoming SACK chunk.
|
|
void HandleIncreasedCumulativeTsnAck(size_t outstanding_bytes,
|
|
size_t total_bytes_acked);
|
|
// Update the congestion control algorithm, given as packet loss has been
|
|
// detected, as reported in an incoming SACK chunk.
|
|
void HandlePacketLoss(UnwrappedTSN highest_tsn_acked);
|
|
// Update the view of the receiver window size.
|
|
void UpdateReceiverWindow(uint32_t a_rwnd);
|
|
// If there is data sent and not ACKED, ensure that the retransmission timer
|
|
// is running.
|
|
void StartT3RtxTimerIfOutstandingData();
|
|
|
|
// Returns the current congestion control algorithm phase.
|
|
CongestionAlgorithmPhase phase() const {
|
|
return (cwnd_ <= ssthresh_)
|
|
? CongestionAlgorithmPhase::kSlowStart
|
|
: CongestionAlgorithmPhase::kCongestionAvoidance;
|
|
}
|
|
|
|
// Returns the number of bytes that may be sent in a single packet according
|
|
// to the congestion control algorithm.
|
|
size_t max_bytes_to_send() const;
|
|
|
|
DcSctpSocketCallbacks& callbacks_;
|
|
const DcSctpOptions options_;
|
|
// The minimum bytes required to be available in the congestion window to
|
|
// allow packets to be sent - to avoid sending too small packets.
|
|
const size_t min_bytes_required_to_send_;
|
|
// If the peer supports RFC3758 - SCTP Partial Reliability Extension.
|
|
const bool partial_reliability_;
|
|
const std::string log_prefix_;
|
|
// The size of the data chunk (DATA/I-DATA) header that is used.
|
|
const size_t data_chunk_header_size_;
|
|
// Called when a new RTT measurement has been done
|
|
const std::function<void(DurationMs rtt)> on_new_rtt_;
|
|
// Called when a SACK has been seen that cleared the retransmission counter.
|
|
const std::function<void()> on_clear_retransmission_counter_;
|
|
// The retransmission counter.
|
|
Timer& t3_rtx_;
|
|
// Unwraps TSNs
|
|
UnwrappedTSN::Unwrapper tsn_unwrapper_;
|
|
|
|
// Congestion Window. Number of bytes that may be in-flight (sent, not acked).
|
|
size_t cwnd_;
|
|
// Receive Window. Number of bytes available in the receiver's RX buffer.
|
|
size_t rwnd_;
|
|
// Slow Start Threshold. See RFC4960.
|
|
size_t ssthresh_;
|
|
// Partial Bytes Acked. See RFC4960.
|
|
size_t partial_bytes_acked_;
|
|
// If set, fast recovery is enabled until this TSN has been cumulative
|
|
// acked.
|
|
absl::optional<UnwrappedTSN> fast_recovery_exit_tsn_ = absl::nullopt;
|
|
|
|
// The send queue.
|
|
SendQueue& send_queue_;
|
|
// All the outstanding data chunks that are in-flight and that have not been
|
|
// cumulative acked. Note that it also contains chunks that have been acked in
|
|
// gap ack blocks.
|
|
OutstandingData outstanding_data_;
|
|
};
|
|
} // namespace dcsctp
|
|
|
|
#endif // NET_DCSCTP_TX_RETRANSMISSION_QUEUE_H_
|