| // Copyright 2013 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "quiche/quic/core/quic_sent_packet_manager.h" | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "quiche/quic/core/congestion_control/loss_detection_interface.h" | |
| #include "quiche/quic/core/congestion_control/pacing_sender.h" | |
| #include "quiche/quic/core/congestion_control/send_algorithm_interface.h" | |
| #include "quiche/quic/core/congestion_control/uber_loss_algorithm.h" | |
| #include "quiche/quic/core/crypto/crypto_protocol.h" | |
| #include "quiche/quic/core/crypto/quic_random.h" | |
| #include "quiche/quic/core/frames/quic_ack_frame.h" | |
| #include "quiche/quic/core/frames/quic_ack_frequency_frame.h" | |
| #include "quiche/quic/core/frames/quic_frame.h" | |
| #include "quiche/quic/core/proto/cached_network_parameters_proto.h" | |
| #include "quiche/quic/core/quic_bandwidth.h" | |
| #include "quiche/quic/core/quic_connection_stats.h" | |
| #include "quiche/quic/core/quic_constants.h" | |
| #include "quiche/quic/core/quic_packet_number.h" | |
| #include "quiche/quic/core/quic_packets.h" | |
| #include "quiche/quic/core/quic_receive_time.h" | |
| #include "quiche/quic/core/quic_tag.h" | |
| #include "quiche/quic/core/quic_time.h" | |
| #include "quiche/quic/core/quic_transmission_info.h" | |
| #include "quiche/quic/core/quic_types.h" | |
| #include "quiche/quic/core/quic_utils.h" | |
| #include "quiche/quic/platform/api/quic_bug_tracker.h" | |
| #include "quiche/quic/platform/api/quic_flag_utils.h" | |
| #include "quiche/quic/platform/api/quic_flags.h" | |
| #include "quiche/quic/platform/api/quic_logging.h" | |
| #include "quiche/common/platform/api/quiche_logging.h" | |
| #include "quiche/common/print_elements.h" | |
| namespace quic { | |
| namespace { | |
| static const int64_t kDefaultRetransmissionTimeMs = 500; | |
| // Ensure the handshake timer isn't faster than 10ms. | |
| // This limits the tenth retransmitted packet to 10s after the initial CHLO. | |
| static const int64_t kMinHandshakeTimeoutMs = 10; | |
| // Sends up to two tail loss probes before firing an RTO, | |
| // per draft RFC draft-dukkipati-tcpm-tcp-loss-probe. | |
| static const size_t kDefaultMaxTailLossProbes = 2; | |
| // The multiplier for calculating PTO timeout before any RTT sample is | |
| // available. | |
| static const float kPtoMultiplierWithoutRttSamples = 3; | |
| // Returns true of retransmissions of the specified type should retransmit | |
| // the frames directly (as opposed to resulting in a loss notification). | |
| inline bool ShouldForceRetransmission(TransmissionType transmission_type) { | |
| return transmission_type == HANDSHAKE_RETRANSMISSION || | |
| transmission_type == PTO_RETRANSMISSION; | |
| } | |
| // If pacing rate is accurate, > 2 burst token is not likely to help first ACK | |
| // to arrive earlier, and overly large burst token could cause incast packet | |
| // losses. | |
| static const uint32_t kConservativeUnpacedBurst = 2; | |
| // The default number of PTOs to trigger path degrading. | |
| static const uint32_t kNumProbeTimeoutsForPathDegradingDelay = 4; | |
| // QUIC overhead returned if there is not enough data to provide an estimate. 5% | |
| // is roughly the number one can get from a simulated unit test. | |
| constexpr float kDefaultOverhead = 0.05f; | |
| // Minimum number of data bytes sent before the packet manager can provide an | |
| // estimate of the QUIC overhead. | |
| constexpr QuicByteCount kMinBytesForOverheadMeasurement = | |
| kDefaultMaxPacketSize * 20; | |
| } // namespace | |
| #define ENDPOINT \ | |
| (unacked_packets_.perspective() == Perspective::IS_SERVER ? "Server: " \ | |
| : "Client: ") | |
| QuicSentPacketManager::QuicSentPacketManager( | |
| Perspective perspective, const QuicClock* clock, QuicRandom* random, | |
| QuicConnectionStats* stats, CongestionControlType congestion_control_type) | |
| : unacked_packets_(perspective), | |
| clock_(clock), | |
| random_(random), | |
| stats_(stats), | |
| debug_delegate_(nullptr), | |
| network_change_visitor_(nullptr), | |
| initial_congestion_window_(kInitialCongestionWindow), | |
| loss_algorithm_(&uber_loss_algorithm_), | |
| consecutive_crypto_retransmission_count_(0), | |
| pending_timer_transmission_count_(0), | |
| using_pacing_(false), | |
| conservative_handshake_retransmits_(false), | |
| largest_mtu_acked_(0), | |
| handshake_finished_(false), | |
| peer_max_ack_delay_( | |
| QuicTime::Delta::FromMilliseconds(kDefaultPeerDelayedAckTimeMs)), | |
| rtt_updated_(false), | |
| acked_packets_iter_(last_ack_frame_.packets.rbegin()), | |
| consecutive_pto_count_(0), | |
| handshake_mode_disabled_(false), | |
| handshake_packet_acked_(false), | |
| zero_rtt_packet_acked_(false), | |
| one_rtt_packet_acked_(false), | |
| num_ptos_for_path_degrading_(kNumProbeTimeoutsForPathDegradingDelay), | |
| ignore_pings_(false), | |
| ignore_ack_delay_(false), | |
| measure_overhead_(false) { | |
| SetSendAlgorithm(congestion_control_type); | |
| } | |
| QuicSentPacketManager::~QuicSentPacketManager() {} | |
| void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { | |
| const Perspective perspective = unacked_packets_.perspective(); | |
| if (config.HasReceivedInitialRoundTripTimeUs() && | |
| config.ReceivedInitialRoundTripTimeUs() > 0) { | |
| if (!config.HasClientSentConnectionOption(kNRTT, perspective)) { | |
| SetInitialRtt(QuicTime::Delta::FromMicroseconds( | |
| config.ReceivedInitialRoundTripTimeUs()), | |
| /*trusted=*/false); | |
| } | |
| } else if (config.HasInitialRoundTripTimeUsToSend() && | |
| config.GetInitialRoundTripTimeUsToSend() > 0) { | |
| SetInitialRtt(QuicTime::Delta::FromMicroseconds( | |
| config.GetInitialRoundTripTimeUsToSend()), | |
| /*trusted=*/false); | |
| } | |
| if (config.HasReceivedMaxAckDelayMs()) { | |
| peer_max_ack_delay_ = | |
| QuicTime::Delta::FromMilliseconds(config.ReceivedMaxAckDelayMs()); | |
| } | |
| // TODO(b/389762349): set peer_min_ack_delay_ if | |
| // config.HasReceivedMinAckDelayDraft10Ms(). Set | |
| // use_smoothed_rtt_in_ack_delay_ based on | |
| // config.HasClientSentConnectionOption(kAFF1, perspective). | |
| if (config.HasClientSentConnectionOption(kMAD0, perspective)) { | |
| ignore_ack_delay_ = true; | |
| } | |
| // Configure congestion control. | |
| if (config.HasClientRequestedIndependentOption(kTBBR, perspective)) { | |
| SetSendAlgorithm(kBBR); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kB2ON, perspective)) { | |
| SetSendAlgorithm(kBBRv2); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kRENO, perspective)) { | |
| SetSendAlgorithm(kRenoBytes); | |
| } else if (config.HasClientRequestedIndependentOption(kBYTE, perspective) || | |
| (GetQuicReloadableFlag(quic_default_to_bbr) && | |
| config.HasClientRequestedIndependentOption(kQBIC, perspective))) { | |
| SetSendAlgorithm(kCubicBytes); | |
| } | |
| if (perspective == Perspective::IS_CLIENT) { | |
| if (config.HasClientRequestedIndependentOption(kPRGC, perspective)) { | |
| SetSendAlgorithm(kPragueCubic); | |
| } else if (config.HasClientRequestedIndependentOption(kCQBC, perspective)) { | |
| SetSendAlgorithm(kCubicBytes); | |
| } | |
| } | |
| // Initial window. | |
| if (config.HasClientRequestedIndependentOption(kIW03, perspective)) { | |
| initial_congestion_window_ = 3; | |
| send_algorithm_->SetInitialCongestionWindowInPackets(3); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kIW10, perspective)) { | |
| initial_congestion_window_ = 10; | |
| send_algorithm_->SetInitialCongestionWindowInPackets(10); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kIW20, perspective)) { | |
| initial_congestion_window_ = 20; | |
| send_algorithm_->SetInitialCongestionWindowInPackets(20); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kIW50, perspective)) { | |
| initial_congestion_window_ = 50; | |
| send_algorithm_->SetInitialCongestionWindowInPackets(50); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kIW2X, perspective)) { | |
| initial_congestion_window_ *= 2; | |
| send_algorithm_->SetInitialCongestionWindowInPackets( | |
| initial_congestion_window_); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kBWS5, perspective)) { | |
| initial_congestion_window_ = 10; | |
| send_algorithm_->SetInitialCongestionWindowInPackets(10); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kIGNP, perspective)) { | |
| ignore_pings_ = true; | |
| } | |
| using_pacing_ = !GetQuicFlag(quic_disable_pacing_for_perf_tests); | |
| // Configure loss detection. | |
| if (config.HasClientRequestedIndependentOption(kILD0, perspective)) { | |
| uber_loss_algorithm_.SetReorderingShift(kDefaultIetfLossDelayShift); | |
| uber_loss_algorithm_.DisableAdaptiveReorderingThreshold(); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kILD1, perspective)) { | |
| uber_loss_algorithm_.SetReorderingShift(kDefaultLossDelayShift); | |
| uber_loss_algorithm_.DisableAdaptiveReorderingThreshold(); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kILD2, perspective)) { | |
| uber_loss_algorithm_.EnableAdaptiveReorderingThreshold(); | |
| uber_loss_algorithm_.SetReorderingShift(kDefaultIetfLossDelayShift); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kILD3, perspective)) { | |
| uber_loss_algorithm_.SetReorderingShift(kDefaultLossDelayShift); | |
| uber_loss_algorithm_.EnableAdaptiveReorderingThreshold(); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kILD4, perspective)) { | |
| uber_loss_algorithm_.SetReorderingShift(kDefaultLossDelayShift); | |
| uber_loss_algorithm_.EnableAdaptiveReorderingThreshold(); | |
| uber_loss_algorithm_.EnableAdaptiveTimeThreshold(); | |
| } | |
| if (config.HasClientRequestedIndependentOption(kRUNT, perspective)) { | |
| uber_loss_algorithm_.DisablePacketThresholdForRuntPackets(); | |
| } | |
| if (config.HasClientSentConnectionOption(kCONH, perspective)) { | |
| conservative_handshake_retransmits_ = true; | |
| } | |
| if (config.HasClientSentConnectionOption(kRNIB, perspective)) { | |
| pacing_sender_.set_remove_non_initial_burst(); | |
| } | |
| // Path degradation experiments | |
| if (config.HasClientRequestedIndependentOption(kPDE2, perspective)) { | |
| num_ptos_for_path_degrading_ = 2; | |
| } | |
| if (config.HasClientRequestedIndependentOption(kPDE3, perspective)) { | |
| num_ptos_for_path_degrading_ = 3; | |
| } | |
| // kPDE4 is the default. We have the experiments with kPDE4. | |
| if (config.HasClientRequestedIndependentOption(kPDE5, perspective)) { | |
| num_ptos_for_path_degrading_ = 5; | |
| } | |
| send_algorithm_->SetFromConfig(config, perspective); | |
| loss_algorithm_->SetFromConfig(config, perspective); | |
| if (network_change_visitor_ != nullptr) { | |
| network_change_visitor_->OnCongestionChange(); | |
| } | |
| if (debug_delegate_ != nullptr) { | |
| DebugDelegate::SendParameters parameters; | |
| parameters.congestion_control_type = | |
| send_algorithm_->GetCongestionControlType(); | |
| parameters.use_pacing = using_pacing_; | |
| parameters.initial_congestion_window = initial_congestion_window_; | |
| debug_delegate_->OnConfigProcessed(parameters); | |
| } | |
| } | |
| void QuicSentPacketManager::ApplyConnectionOptions( | |
| const QuicTagVector& connection_options) { | |
| std::optional<CongestionControlType> cc_type; | |
| if (ContainsQuicTag(connection_options, kB2ON)) { | |
| cc_type = kBBRv2; | |
| } else if (ContainsQuicTag(connection_options, kTBBR)) { | |
| cc_type = kBBR; | |
| } else if (ContainsQuicTag(connection_options, kRENO)) { | |
| cc_type = kRenoBytes; | |
| } else if (ContainsQuicTag(connection_options, kQBIC)) { | |
| cc_type = kCubicBytes; | |
| } | |
| // This function is only used in server experiments, so do not apply the | |
| // client-only PRGC tag. | |
| QUICHE_DCHECK(unacked_packets_.perspective() == Perspective::IS_SERVER); | |
| if (cc_type.has_value()) { | |
| SetSendAlgorithm(*cc_type); | |
| } | |
| send_algorithm_->ApplyConnectionOptions(connection_options); | |
| } | |
| void QuicSentPacketManager::ResumeConnectionState( | |
| const CachedNetworkParameters& cached_network_params, | |
| bool max_bandwidth_resumption) { | |
| QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond( | |
| max_bandwidth_resumption | |
| ? cached_network_params.max_bandwidth_estimate_bytes_per_second() | |
| : cached_network_params.bandwidth_estimate_bytes_per_second()); | |
| QuicTime::Delta rtt = | |
| QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms()); | |
| // This calls the old AdjustNetworkParameters interface, and fills certain | |
| // fields in SendAlgorithmInterface::NetworkParams | |
| // (e.g., quic_bbr_fix_pacing_rate) using GFE flags. | |
| SendAlgorithmInterface::NetworkParams params( | |
| bandwidth, rtt, /*allow_cwnd_to_decrease = */ false); | |
| // The rtt is trusted because it's a min_rtt measured from a previous | |
| // connection with the same network path between client and server. | |
| params.is_rtt_trusted = true; | |
| AdjustNetworkParameters(params); | |
| } | |
| void QuicSentPacketManager::AdjustNetworkParameters( | |
| const SendAlgorithmInterface::NetworkParams& params) { | |
| const QuicBandwidth& bandwidth = params.bandwidth; | |
| const QuicTime::Delta& rtt = params.rtt; | |
| if (!rtt.IsZero()) { | |
| if (params.is_rtt_trusted) { | |
| // Always set initial rtt if it's trusted. | |
| SetInitialRtt(rtt, /*trusted=*/true); | |
| } else if (rtt_stats_.initial_rtt() == | |
| QuicTime::Delta::FromMilliseconds(kInitialRttMs)) { | |
| // Only set initial rtt if we are using the default. This avoids | |
| // overwriting a trusted initial rtt by an untrusted one. | |
| SetInitialRtt(rtt, /*trusted=*/false); | |
| } | |
| } | |
| const QuicByteCount old_cwnd = send_algorithm_->GetCongestionWindow(); | |
| if (GetQuicReloadableFlag(quic_conservative_bursts) && using_pacing_ && | |
| !bandwidth.IsZero()) { | |
| QUIC_RELOADABLE_FLAG_COUNT(quic_conservative_bursts); | |
| pacing_sender_.SetBurstTokens(kConservativeUnpacedBurst); | |
| } | |
| SendAlgorithmInterface::NetworkParams new_params = params; | |
| if (params.clamp_cwnd_and_rtt_before_send_algorithm) { | |
| static constexpr QuicBandwidth kAbsurdlyLargeMaxBandwidth = | |
| QuicBandwidth::FromKBytesPerSecond(2 * 1024 * 1024); // 16 Gbps | |
| static constexpr QuicTime::Delta kAbsurdlyLargeMinRtt = | |
| QuicTime::Delta::FromMilliseconds(512 * 1024); // ~8.7 minutes | |
| // These constants are safe to multiply with `QuicByteCount | |
| // operator*(QuicBandwidth, QuicTime::Delta)`. Therefore, the clamped | |
| // bandwidth and rtt are also safe to multiply. | |
| static_assert( | |
| kAbsurdlyLargeMaxBandwidth.ToBytesPerPeriodSafe(kAbsurdlyLargeMinRtt) | |
| .has_value()); | |
| new_params.bandwidth = | |
| std::min(params.bandwidth, kAbsurdlyLargeMaxBandwidth); | |
| new_params.rtt = std::min(params.rtt, kAbsurdlyLargeMinRtt); | |
| } | |
| send_algorithm_->AdjustNetworkParameters(new_params); | |
| if (debug_delegate_ != nullptr) { | |
| debug_delegate_->OnAdjustNetworkParameters( | |
| new_params.bandwidth, | |
| new_params.rtt.IsZero() ? rtt_stats_.MinOrInitialRtt() : new_params.rtt, | |
| old_cwnd, send_algorithm_->GetCongestionWindow()); | |
| } | |
| } | |
| void QuicSentPacketManager::SetLossDetectionTuner( | |
| std::unique_ptr<LossDetectionTunerInterface> tuner) { | |
| uber_loss_algorithm_.SetLossDetectionTuner(std::move(tuner)); | |
| } | |
| void QuicSentPacketManager::OnConfigNegotiated() { | |
| loss_algorithm_->OnConfigNegotiated(); | |
| } | |
| void QuicSentPacketManager::OnConnectionClosed() { | |
| loss_algorithm_->OnConnectionClosed(); | |
| } | |
| void QuicSentPacketManager::SetHandshakeConfirmed() { | |
| if (!handshake_finished_) { | |
| handshake_finished_ = true; | |
| NeuterHandshakePackets(); | |
| } | |
| } | |
| void QuicSentPacketManager::PostProcessNewlyAckedPackets( | |
| QuicPacketNumber ack_packet_number, EncryptionLevel ack_decrypted_level, | |
| const QuicAckFrame& ack_frame, QuicTime ack_receive_time, bool rtt_updated, | |
| QuicByteCount prior_bytes_in_flight, | |
| std::optional<QuicEcnCounts> ecn_counts) { | |
| unacked_packets_.NotifyAggregatedStreamFrameAcked( | |
| last_ack_frame_.ack_delay_time); | |
| InvokeLossDetection(ack_receive_time); | |
| MaybeInvokeCongestionEvent( | |
| rtt_updated, prior_bytes_in_flight, ack_receive_time, ecn_counts, | |
| peer_ack_ecn_counts_[QuicUtils::GetPacketNumberSpace( | |
| ack_decrypted_level)]); | |
| unacked_packets_.RemoveObsoletePackets(); | |
| sustained_bandwidth_recorder_.RecordEstimate( | |
| send_algorithm_->InRecovery(), send_algorithm_->InSlowStart(), | |
| send_algorithm_->BandwidthEstimate(), ack_receive_time, clock_->WallNow(), | |
| rtt_stats_.smoothed_rtt()); | |
| // Anytime we are making forward progress and have a new RTT estimate, reset | |
| // the backoff counters. | |
| if (rtt_updated) { | |
| // Records the max consecutive PTO before forward progress has been made. | |
| if (consecutive_pto_count_ > | |
| stats_->max_consecutive_rto_with_forward_progress) { | |
| stats_->max_consecutive_rto_with_forward_progress = | |
| consecutive_pto_count_; | |
| } | |
| // Reset all retransmit counters any time a new packet is acked. | |
| consecutive_pto_count_ = 0; | |
| consecutive_crypto_retransmission_count_ = 0; | |
| } | |
| if (debug_delegate_ != nullptr) { | |
| debug_delegate_->OnIncomingAck( | |
| ack_packet_number, ack_decrypted_level, ack_frame, ack_receive_time, | |
| LargestAcked(ack_frame), rtt_updated, GetLeastUnacked()); | |
| } | |
| // Remove packets below least unacked from all_packets_acked_ and | |
| // last_ack_frame_. | |
| last_ack_frame_.packets.RemoveUpTo(unacked_packets_.GetLeastUnacked()); | |
| last_ack_frame_.received_packet_times.clear(); | |
| } | |
| void QuicSentPacketManager::MaybeInvokeCongestionEvent( | |
| bool rtt_updated, QuicByteCount prior_in_flight, QuicTime event_time, | |
| std::optional<QuicEcnCounts> ecn_counts, | |
| const QuicEcnCounts& previous_counts) { | |
| if (!rtt_updated && packets_acked_.empty() && packets_lost_.empty()) { | |
| return; | |
| } | |
| const bool overshooting_detected = | |
| stats_->overshooting_detected_with_network_parameters_adjusted; | |
| // A connection should send at most one flavor of ECT, so only one variable | |
| // is necessary. | |
| QuicPacketCount newly_acked_ect = 0, newly_acked_ce = 0; | |
| if (ecn_counts.has_value()) { | |
| newly_acked_ect = ecn_counts->ect1 - previous_counts.ect1; | |
| if (newly_acked_ect == 0) { | |
| newly_acked_ect = ecn_counts->ect0 - previous_counts.ect0; | |
| } else { | |
| QUIC_BUG_IF(quic_bug_518619343_04, | |
| ecn_counts->ect0 - previous_counts.ect0) | |
| << "Sent ECT(0) and ECT(1) newly acked in the same ACK."; | |
| } | |
| newly_acked_ce = ecn_counts->ce - previous_counts.ce; | |
| } | |
| if (using_pacing_) { | |
| pacing_sender_.OnCongestionEvent(rtt_updated, prior_in_flight, event_time, | |
| packets_acked_, packets_lost_, | |
| newly_acked_ect, newly_acked_ce); | |
| } else { | |
| send_algorithm_->OnCongestionEvent(rtt_updated, prior_in_flight, event_time, | |
| packets_acked_, packets_lost_, | |
| newly_acked_ect, newly_acked_ce); | |
| } | |
| if (debug_delegate_ != nullptr && !overshooting_detected && | |
| stats_->overshooting_detected_with_network_parameters_adjusted) { | |
| debug_delegate_->OnOvershootingDetected(); | |
| } | |
| packets_acked_.clear(); | |
| packets_lost_.clear(); | |
| if (network_change_visitor_ != nullptr) { | |
| network_change_visitor_->OnCongestionChange(); | |
| } | |
| } | |
| void QuicSentPacketManager::MarkInitialPacketsForRetransmission() { | |
| if (unacked_packets_.empty()) { | |
| return; | |
| } | |
| QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); | |
| QuicPacketNumber largest_sent_packet = unacked_packets_.largest_sent_packet(); | |
| for (; packet_number <= largest_sent_packet; ++packet_number) { | |
| QuicTransmissionInfo* transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| if (transmission_info->encryption_level == ENCRYPTION_INITIAL) { | |
| if (transmission_info->in_flight) { | |
| unacked_packets_.RemoveFromInFlight(transmission_info); | |
| } | |
| if (unacked_packets_.HasRetransmittableFrames(*transmission_info)) { | |
| MarkForRetransmission(packet_number, ALL_INITIAL_RETRANSMISSION); | |
| } | |
| } | |
| } | |
| } | |
| void QuicSentPacketManager::MarkZeroRttPacketsForRetransmission() { | |
| if (unacked_packets_.empty()) { | |
| return; | |
| } | |
| QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); | |
| QuicPacketNumber largest_sent_packet = unacked_packets_.largest_sent_packet(); | |
| for (; packet_number <= largest_sent_packet; ++packet_number) { | |
| QuicTransmissionInfo* transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| if (transmission_info->encryption_level == ENCRYPTION_ZERO_RTT) { | |
| if (transmission_info->in_flight) { | |
| // Remove 0-RTT packets and packets of the wrong version from flight, | |
| // because neither can be processed by the peer. | |
| unacked_packets_.RemoveFromInFlight(transmission_info); | |
| } | |
| if (unacked_packets_.HasRetransmittableFrames(*transmission_info)) { | |
| MarkForRetransmission(packet_number, ALL_ZERO_RTT_RETRANSMISSION); | |
| } | |
| } | |
| } | |
| } | |
| void QuicSentPacketManager::NeuterUnencryptedPackets() { | |
| for (QuicPacketNumber packet_number : | |
| unacked_packets_.NeuterUnencryptedPackets()) { | |
| send_algorithm_->OnPacketNeutered(packet_number); | |
| } | |
| if (handshake_mode_disabled_) { | |
| consecutive_pto_count_ = 0; | |
| uber_loss_algorithm_.ResetLossDetection(INITIAL_DATA); | |
| } | |
| } | |
| void QuicSentPacketManager::NeuterHandshakePackets() { | |
| for (QuicPacketNumber packet_number : | |
| unacked_packets_.NeuterHandshakePackets()) { | |
| send_algorithm_->OnPacketNeutered(packet_number); | |
| } | |
| if (handshake_mode_disabled_) { | |
| consecutive_pto_count_ = 0; | |
| uber_loss_algorithm_.ResetLossDetection(HANDSHAKE_DATA); | |
| } | |
| } | |
| bool QuicSentPacketManager::ShouldAddMaxAckDelay( | |
| PacketNumberSpace space) const { | |
| // Do not include max_ack_delay when PTO is armed for Initial or Handshake | |
| // packet number spaces. | |
| return !supports_multiple_packet_number_spaces() || space == APPLICATION_DATA; | |
| } | |
| QuicTime QuicSentPacketManager::GetEarliestPacketSentTimeForPto( | |
| PacketNumberSpace* packet_number_space) const { | |
| QUICHE_DCHECK(supports_multiple_packet_number_spaces()); | |
| QuicTime earliest_sent_time = QuicTime::Zero(); | |
| for (int8_t i = 0; i < NUM_PACKET_NUMBER_SPACES; ++i) { | |
| const QuicTime sent_time = unacked_packets_.GetLastInFlightPacketSentTime( | |
| static_cast<PacketNumberSpace>(i)); | |
| if (!handshake_finished_ && i == APPLICATION_DATA) { | |
| // Do not arm PTO for application data until handshake gets confirmed. | |
| continue; | |
| } | |
| if (!sent_time.IsInitialized() || (earliest_sent_time.IsInitialized() && | |
| earliest_sent_time <= sent_time)) { | |
| continue; | |
| } | |
| earliest_sent_time = sent_time; | |
| *packet_number_space = static_cast<PacketNumberSpace>(i); | |
| } | |
| return earliest_sent_time; | |
| } | |
| void QuicSentPacketManager::MarkForRetransmission( | |
| QuicPacketNumber packet_number, TransmissionType transmission_type) { | |
| QuicTransmissionInfo* transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| // Packets without retransmittable frames can only be marked for loss | |
| // retransmission. | |
| QUIC_BUG_IF(quic_bug_12552_2, transmission_type != LOSS_RETRANSMISSION && | |
| !unacked_packets_.HasRetransmittableFrames( | |
| *transmission_info)) | |
| << "packet number " << packet_number | |
| << " transmission_type: " << transmission_type << " transmission_info " | |
| << transmission_info->DebugString(); | |
| if (ShouldForceRetransmission(transmission_type)) { | |
| if (!unacked_packets_.RetransmitFrames( | |
| QuicFrames(transmission_info->retransmittable_frames), | |
| transmission_type)) { | |
| // Do not set packet state if the data is not fully retransmitted. | |
| // This should only happen if packet payload size decreases which can be | |
| // caused by: | |
| // 1) connection tries to opportunistically retransmit data | |
| // when sending a packet of a different packet number space, or | |
| // 2) path MTU decreases, or | |
| // 3) packet header size increases (e.g., packet number length | |
| // increases). | |
| QUIC_CODE_COUNT(quic_retransmit_frames_failed); | |
| return; | |
| } | |
| QUIC_CODE_COUNT(quic_retransmit_frames_succeeded); | |
| } else { | |
| unacked_packets_.NotifyFramesLost(*transmission_info, transmission_type); | |
| if (!transmission_info->retransmittable_frames.empty()) { | |
| if (transmission_type == LOSS_RETRANSMISSION) { | |
| // Record the first packet sent after loss, which allows to wait 1 | |
| // more RTT before giving up on this lost packet. | |
| transmission_info->first_sent_after_loss = | |
| unacked_packets_.largest_sent_packet() + 1; | |
| } else { | |
| // Clear the recorded first packet sent after loss when version or | |
| // encryption changes. | |
| transmission_info->first_sent_after_loss.Clear(); | |
| } | |
| } | |
| } | |
| // Get the latest transmission_info here as it can be invalidated after | |
| // HandleRetransmission adding new sent packets into unacked_packets_. | |
| transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| // Update packet state according to transmission type. | |
| transmission_info->state = | |
| QuicUtils::RetransmissionTypeToPacketState(transmission_type); | |
| } | |
| void QuicSentPacketManager::RecordOneSpuriousRetransmission( | |
| const QuicTransmissionInfo& info) { | |
| stats_->bytes_spuriously_retransmitted += info.bytes_sent; | |
| ++stats_->packets_spuriously_retransmitted; | |
| if (debug_delegate_ != nullptr) { | |
| debug_delegate_->OnSpuriousPacketRetransmission(info.transmission_type, | |
| info.bytes_sent); | |
| } | |
| } | |
| void QuicSentPacketManager::MarkPacketHandled(QuicPacketNumber packet_number, | |
| QuicTime ack_receive_time, | |
| QuicTime::Delta ack_delay_time, | |
| QuicTransmissionInfo*& info) { | |
| if (info->has_ack_frequency) { | |
| for (const auto& frame : info->retransmittable_frames) { | |
| if (frame.type == ACK_FREQUENCY_FRAME) { | |
| OnAckFrequencyFrameAcked(*frame.ack_frequency_frame); | |
| } | |
| } | |
| } | |
| // Try to aggregate acked stream frames if acked packet is not a | |
| // retransmission. | |
| if (info->transmission_type == NOT_RETRANSMISSION) { | |
| unacked_packets_.MaybeAggregateAckedStreamFrame(packet_number, | |
| ack_delay_time, info); | |
| } else { | |
| unacked_packets_.NotifyAggregatedStreamFrameAcked(ack_delay_time); | |
| info = unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| const bool new_data_acked = | |
| unacked_packets_.NotifyFramesAcked(packet_number, ack_delay_time, info); | |
| if (!new_data_acked && info->transmission_type != NOT_RETRANSMISSION) { | |
| // Record as a spurious retransmission if this packet is a | |
| // retransmission and no new data gets acked. | |
| QUIC_DVLOG(1) << "Detect spurious retransmitted packet " << packet_number | |
| << " transmission type: " << info->transmission_type; | |
| RecordOneSpuriousRetransmission(*info); | |
| } | |
| } | |
| if (info->state == LOST) { | |
| // Record as a spurious loss as a packet previously declared lost gets | |
| // acked. | |
| const PacketNumberSpace packet_number_space = | |
| unacked_packets_.GetPacketNumberSpace(info->encryption_level); | |
| const QuicPacketNumber previous_largest_acked = | |
| supports_multiple_packet_number_spaces() | |
| ? unacked_packets_.GetLargestAckedOfPacketNumberSpace( | |
| packet_number_space) | |
| : unacked_packets_.largest_acked(); | |
| QUIC_DVLOG(1) << "Packet " << packet_number | |
| << " was detected lost spuriously, " | |
| "previous_largest_acked: " | |
| << previous_largest_acked; | |
| loss_algorithm_->SpuriousLossDetected(unacked_packets_, rtt_stats_, | |
| ack_receive_time, packet_number, | |
| previous_largest_acked); | |
| ++stats_->packet_spuriously_detected_lost; | |
| } | |
| if (network_change_visitor_ != nullptr && | |
| info->bytes_sent > largest_mtu_acked_) { | |
| largest_mtu_acked_ = info->bytes_sent; | |
| network_change_visitor_->OnPathMtuIncreased(largest_mtu_acked_); | |
| } | |
| unacked_packets_.RemoveFromInFlight(info); | |
| unacked_packets_.RemoveRetransmittability(info); | |
| info->state = ACKED; | |
| } | |
| bool QuicSentPacketManager::CanSendAckFrequency() const { | |
| return !peer_min_ack_delay_.IsInfinite() && handshake_finished_; | |
| } | |
| // TODO(martinduke): Update to include reordering threshold. | |
| QuicAckFrequencyFrame QuicSentPacketManager::GetUpdatedAckFrequencyFrame() | |
| const { | |
| QuicAckFrequencyFrame frame; | |
| if (!CanSendAckFrequency()) { | |
| QUIC_BUG(quic_bug_10750_1) | |
| << "New AckFrequencyFrame is created while it shouldn't."; | |
| return frame; | |
| } | |
| frame.ack_eliciting_threshold = kMaxRetransmittablePacketsBeforeAck; | |
| auto rtt = use_smoothed_rtt_in_ack_delay_ ? rtt_stats_.SmoothedOrInitialRtt() | |
| : rtt_stats_.MinOrInitialRtt(); | |
| frame.requested_max_ack_delay = rtt * kPeerAckDecimationDelay; | |
| frame.requested_max_ack_delay = | |
| std::max(frame.requested_max_ack_delay, peer_min_ack_delay_); | |
| // TODO(haoyuewang) Remove this once kDefaultMinAckDelayTimeMs is updated to | |
| // 5 ms on the client side. | |
| frame.requested_max_ack_delay = | |
| std::max(frame.requested_max_ack_delay, | |
| QuicTime::Delta::FromMilliseconds(kDefaultMinAckDelayTimeMs)); | |
| return frame; | |
| } | |
| void QuicSentPacketManager::RecordEcnMarkingSent(QuicEcnCodepoint ecn_codepoint, | |
| EncryptionLevel level) { | |
| PacketNumberSpace space = QuicUtils::GetPacketNumberSpace(level); | |
| switch (ecn_codepoint) { | |
| case ECN_NOT_ECT: | |
| break; | |
| case ECN_ECT0: | |
| ++ect0_packets_sent_[space]; | |
| break; | |
| case ECN_ECT1: | |
| ++ect1_packets_sent_[space]; | |
| break; | |
| case ECN_CE: | |
| // Test only: endpoints MUST NOT send CE. As CE reports will have to | |
| // correspond to either an ECT(0) or an ECT(1) packet to be valid, just | |
| // increment both to avoid validation failure. | |
| ++ect0_packets_sent_[space]; | |
| ++ect1_packets_sent_[space]; | |
| break; | |
| } | |
| } | |
| bool QuicSentPacketManager::OnPacketSent( | |
| SerializedPacket* mutable_packet, QuicTime sent_time, | |
| TransmissionType transmission_type, | |
| HasRetransmittableData has_retransmittable_data, bool measure_rtt, | |
| QuicEcnCodepoint ecn_codepoint) { | |
| const SerializedPacket& packet = *mutable_packet; | |
| QuicPacketNumber packet_number = packet.packet_number; | |
| QUICHE_DCHECK_LE(FirstSendingPacketNumber(), packet_number); | |
| QUICHE_DCHECK(!unacked_packets_.IsUnacked(packet_number)); | |
| QUIC_BUG_IF(quic_bug_10750_2, packet.encrypted_length == 0) | |
| << "Cannot send empty packets."; | |
| if (pending_timer_transmission_count_ > 0) { | |
| --pending_timer_transmission_count_; | |
| } | |
| UpdateOverheadMeasurements(packet); | |
| bool in_flight = has_retransmittable_data == HAS_RETRANSMITTABLE_DATA; | |
| if (ignore_pings_ && mutable_packet->retransmittable_frames.size() == 1 && | |
| mutable_packet->retransmittable_frames[0].type == PING_FRAME) { | |
| // Dot not use PING only packet for RTT measure or congestion control. | |
| in_flight = false; | |
| measure_rtt = false; | |
| } | |
| if (using_pacing_) { | |
| pacing_sender_.OnPacketSent(sent_time, unacked_packets_.bytes_in_flight(), | |
| packet_number, packet.encrypted_length, | |
| has_retransmittable_data); | |
| } else { | |
| send_algorithm_->OnPacketSent(sent_time, unacked_packets_.bytes_in_flight(), | |
| packet_number, packet.encrypted_length, | |
| has_retransmittable_data); | |
| } | |
| // Deallocate datagram data in QuicDatagramFrame immediately after packet | |
| // sent. | |
| if (packet.has_datagram) { | |
| for (auto& frame : mutable_packet->retransmittable_frames) { | |
| if (frame.type == DATAGRAM_FRAME) { | |
| frame.datagram_frame->datagram_data.clear(); | |
| frame.datagram_frame->datagram_length = 0; | |
| } | |
| } | |
| } | |
| if (packet.has_ack_frequency) { | |
| for (const auto& frame : packet.retransmittable_frames) { | |
| if (frame.type == ACK_FREQUENCY_FRAME) { | |
| OnAckFrequencyFrameSent(*frame.ack_frequency_frame); | |
| } | |
| } | |
| } | |
| RecordEcnMarkingSent(ecn_codepoint, packet.encryption_level); | |
| unacked_packets_.AddSentPacket(mutable_packet, transmission_type, sent_time, | |
| in_flight, measure_rtt, ecn_codepoint); | |
| // Reset the retransmission timer anytime a pending packet is sent. | |
| return in_flight; | |
| } | |
| const QuicTransmissionInfo& QuicSentPacketManager::AddDispatcherSentPacket( | |
| const DispatcherSentPacket& packet) { | |
| QUIC_DVLOG(1) << "QuicSPM: Adding dispatcher sent packet " | |
| << packet.packet_number << ", size: " << packet.bytes_sent | |
| << ", sent_time: " << packet.sent_time | |
| << ", largest_acked: " << packet.largest_acked; | |
| if (using_pacing_) { | |
| pacing_sender_.OnPacketSent( | |
| packet.sent_time, unacked_packets_.bytes_in_flight(), | |
| packet.packet_number, packet.bytes_sent, NO_RETRANSMITTABLE_DATA); | |
| } else { | |
| send_algorithm_->OnPacketSent( | |
| packet.sent_time, unacked_packets_.bytes_in_flight(), | |
| packet.packet_number, packet.bytes_sent, NO_RETRANSMITTABLE_DATA); | |
| } | |
| return unacked_packets_.AddDispatcherSentPacket(packet); | |
| } | |
| QuicSentPacketManager::RetransmissionTimeoutMode | |
| QuicSentPacketManager::OnRetransmissionTimeout() { | |
| QUICHE_DCHECK(unacked_packets_.HasInFlightPackets() || | |
| (handshake_mode_disabled_ && !handshake_finished_)); | |
| QUICHE_DCHECK_EQ(0u, pending_timer_transmission_count_); | |
| // Handshake retransmission, timer based loss detection, TLP, and RTO are | |
| // implemented with a single alarm. The handshake alarm is set when the | |
| // handshake has not completed, the loss alarm is set when the loss detection | |
| // algorithm says to, and the TLP and RTO alarms are set after that. | |
| // The TLP alarm is always set to run for under an RTO. | |
| switch (GetRetransmissionMode()) { | |
| case HANDSHAKE_MODE: | |
| QUICHE_DCHECK(!handshake_mode_disabled_); | |
| ++stats_->crypto_retransmit_count; | |
| RetransmitCryptoPackets(); | |
| return HANDSHAKE_MODE; | |
| case LOSS_MODE: { | |
| ++stats_->loss_timeout_count; | |
| QuicByteCount prior_in_flight = unacked_packets_.bytes_in_flight(); | |
| const QuicTime now = clock_->Now(); | |
| InvokeLossDetection(now); | |
| MaybeInvokeCongestionEvent(false, prior_in_flight, now, | |
| std::optional<QuicEcnCounts>(), | |
| peer_ack_ecn_counts_[APPLICATION_DATA]); | |
| return LOSS_MODE; | |
| } | |
| case PTO_MODE: | |
| QUIC_DVLOG(1) << ENDPOINT << "PTO mode"; | |
| ++stats_->pto_count; | |
| if (handshake_mode_disabled_ && !handshake_finished_) { | |
| ++stats_->crypto_retransmit_count; | |
| } | |
| ++consecutive_pto_count_; | |
| pending_timer_transmission_count_ = 1; | |
| return PTO_MODE; | |
| } | |
| QUIC_BUG(quic_bug_10750_3) | |
| << "Unknown retransmission mode " << GetRetransmissionMode(); | |
| return GetRetransmissionMode(); | |
| } | |
| void QuicSentPacketManager::RetransmitCryptoPackets() { | |
| QUICHE_DCHECK_EQ(HANDSHAKE_MODE, GetRetransmissionMode()); | |
| ++consecutive_crypto_retransmission_count_; | |
| bool packet_retransmitted = false; | |
| std::vector<QuicPacketNumber> crypto_retransmissions; | |
| if (!unacked_packets_.empty()) { | |
| QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); | |
| QuicPacketNumber largest_sent_packet = | |
| unacked_packets_.largest_sent_packet(); | |
| for (; packet_number <= largest_sent_packet; ++packet_number) { | |
| QuicTransmissionInfo* transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| // Only retransmit frames which are in flight, and therefore have been | |
| // sent. | |
| if (!transmission_info->in_flight || | |
| transmission_info->state != OUTSTANDING || | |
| !transmission_info->has_crypto_handshake || | |
| !unacked_packets_.HasRetransmittableFrames(*transmission_info)) { | |
| continue; | |
| } | |
| packet_retransmitted = true; | |
| crypto_retransmissions.push_back(packet_number); | |
| ++pending_timer_transmission_count_; | |
| } | |
| } | |
| QUICHE_DCHECK(packet_retransmitted) | |
| << "No crypto packets found to retransmit."; | |
| for (QuicPacketNumber retransmission : crypto_retransmissions) { | |
| MarkForRetransmission(retransmission, HANDSHAKE_RETRANSMISSION); | |
| } | |
| } | |
| bool QuicSentPacketManager::MaybeRetransmitOldestPacket(TransmissionType type) { | |
| if (!unacked_packets_.empty()) { | |
| QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); | |
| QuicPacketNumber largest_sent_packet = | |
| unacked_packets_.largest_sent_packet(); | |
| for (; packet_number <= largest_sent_packet; ++packet_number) { | |
| QuicTransmissionInfo* transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| // Only retransmit frames which are in flight, and therefore have been | |
| // sent. | |
| if (!transmission_info->in_flight || | |
| transmission_info->state != OUTSTANDING || | |
| !unacked_packets_.HasRetransmittableFrames(*transmission_info)) { | |
| continue; | |
| } | |
| MarkForRetransmission(packet_number, type); | |
| return true; | |
| } | |
| } | |
| QUIC_DVLOG(1) | |
| << "No retransmittable packets, so RetransmitOldestPacket failed."; | |
| return false; | |
| } | |
| void QuicSentPacketManager::MaybeSendProbePacket() { | |
| if (pending_timer_transmission_count_ == 0) { | |
| return; | |
| } | |
| PacketNumberSpace packet_number_space; | |
| if (supports_multiple_packet_number_spaces()) { | |
| // Find out the packet number space to send probe packets. | |
| if (!GetEarliestPacketSentTimeForPto(&packet_number_space) | |
| .IsInitialized()) { | |
| QUIC_BUG_IF(quic_earliest_sent_time_not_initialized, | |
| unacked_packets_.perspective() == Perspective::IS_SERVER) | |
| << "earliest_sent_time not initialized when trying to send PTO " | |
| "retransmissions"; | |
| return; | |
| } | |
| } | |
| std::vector<QuicPacketNumber> probing_packets; | |
| if (!unacked_packets_.empty()) { | |
| QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); | |
| QuicPacketNumber largest_sent_packet = | |
| unacked_packets_.largest_sent_packet(); | |
| for (; packet_number <= largest_sent_packet; ++packet_number) { | |
| QuicTransmissionInfo* transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| if (transmission_info->state == OUTSTANDING && | |
| unacked_packets_.HasRetransmittableFrames(*transmission_info) && | |
| (!supports_multiple_packet_number_spaces() || | |
| unacked_packets_.GetPacketNumberSpace( | |
| transmission_info->encryption_level) == packet_number_space)) { | |
| QUICHE_DCHECK(transmission_info->in_flight); | |
| probing_packets.push_back(packet_number); | |
| if (probing_packets.size() == pending_timer_transmission_count_) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| for (QuicPacketNumber retransmission : probing_packets) { | |
| QUIC_DVLOG(1) << ENDPOINT << "Marking " << retransmission | |
| << " for probing retransmission"; | |
| MarkForRetransmission(retransmission, PTO_RETRANSMISSION); | |
| } | |
| // It is possible that there is not enough outstanding data for probing. | |
| } | |
| void QuicSentPacketManager::EnableIetfPtoAndLossDetection() { | |
| // Disable handshake mode. | |
| handshake_mode_disabled_ = true; | |
| } | |
| void QuicSentPacketManager::RetransmitDataOfSpaceIfAny( | |
| PacketNumberSpace space) { | |
| QUICHE_DCHECK(supports_multiple_packet_number_spaces()); | |
| if (!unacked_packets_.GetLastInFlightPacketSentTime(space).IsInitialized()) { | |
| // No in flight data of space. | |
| return; | |
| } | |
| if (unacked_packets_.empty()) { | |
| return; | |
| } | |
| QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); | |
| QuicPacketNumber largest_sent_packet = unacked_packets_.largest_sent_packet(); | |
| for (; packet_number <= largest_sent_packet; ++packet_number) { | |
| QuicTransmissionInfo* transmission_info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet_number); | |
| if (transmission_info->state == OUTSTANDING && | |
| unacked_packets_.HasRetransmittableFrames(*transmission_info) && | |
| unacked_packets_.GetPacketNumberSpace( | |
| transmission_info->encryption_level) == space) { | |
| QUICHE_DCHECK(transmission_info->in_flight); | |
| if (pending_timer_transmission_count_ == 0) { | |
| pending_timer_transmission_count_ = 1; | |
| } | |
| MarkForRetransmission(packet_number, PTO_RETRANSMISSION); | |
| return; | |
| } | |
| } | |
| } | |
| QuicSentPacketManager::RetransmissionTimeoutMode | |
| QuicSentPacketManager::GetRetransmissionMode() const { | |
| QUICHE_DCHECK(unacked_packets_.HasInFlightPackets() || | |
| (handshake_mode_disabled_ && !handshake_finished_)); | |
| if (!handshake_mode_disabled_ && !handshake_finished_ && | |
| unacked_packets_.HasPendingCryptoPackets()) { | |
| return HANDSHAKE_MODE; | |
| } | |
| if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) { | |
| return LOSS_MODE; | |
| } | |
| return PTO_MODE; | |
| } | |
| void QuicSentPacketManager::InvokeLossDetection(QuicTime time) { | |
| if (!packets_acked_.empty()) { | |
| QUICHE_DCHECK_LE(packets_acked_.front().packet_number, | |
| packets_acked_.back().packet_number); | |
| largest_newly_acked_ = packets_acked_.back().packet_number; | |
| } | |
| LossDetectionInterface::DetectionStats detection_stats = | |
| loss_algorithm_->DetectLosses(unacked_packets_, time, rtt_stats_, | |
| largest_newly_acked_, packets_acked_, | |
| &packets_lost_); | |
| if (detection_stats.sent_packets_max_sequence_reordering > | |
| stats_->sent_packets_max_sequence_reordering) { | |
| stats_->sent_packets_max_sequence_reordering = | |
| detection_stats.sent_packets_max_sequence_reordering; | |
| } | |
| stats_->sent_packets_num_borderline_time_reorderings += | |
| detection_stats.sent_packets_num_borderline_time_reorderings; | |
| stats_->total_loss_detection_response_time += | |
| detection_stats.total_loss_detection_response_time; | |
| for (const LostPacket& packet : packets_lost_) { | |
| QuicTransmissionInfo* info = | |
| unacked_packets_.GetMutableTransmissionInfo(packet.packet_number); | |
| ++stats_->packets_lost; | |
| if (debug_delegate_ != nullptr) { | |
| debug_delegate_->OnPacketLoss(packet.packet_number, | |
| info->encryption_level, LOSS_RETRANSMISSION, | |
| time); | |
| } | |
| unacked_packets_.RemoveFromInFlight(info); | |
| MarkForRetransmission(packet.packet_number, LOSS_RETRANSMISSION); | |
| } | |
| } | |
| bool QuicSentPacketManager::MaybeUpdateRTT(QuicPacketNumber largest_acked, | |
| QuicTime::Delta ack_delay_time, | |
| QuicTime ack_receive_time) { | |
| // We rely on ack_delay_time to compute an RTT estimate, so we | |
| // only update rtt when the largest observed gets acked and the acked packet | |
| // is not useless. | |
| if (!unacked_packets_.IsUnacked(largest_acked)) { | |
| return false; | |
| } | |
| // We calculate the RTT based on the highest ACKed packet number, the lower | |
| // packet numbers will include the ACK aggregation delay. | |
| const QuicTransmissionInfo& transmission_info = | |
| unacked_packets_.GetTransmissionInfo(largest_acked); | |
| // Ensure the packet has a valid sent time. | |
| if (transmission_info.sent_time == QuicTime::Zero()) { | |
| QUIC_BUG(quic_bug_10750_4) | |
| << "Acked packet has zero sent time, largest_acked:" << largest_acked; | |
| return false; | |
| } | |
| if (transmission_info.state == NOT_CONTRIBUTING_RTT) { | |
| return false; | |
| } | |
| if (transmission_info.sent_time > ack_receive_time) { | |
| QUIC_CODE_COUNT(quic_receive_acked_before_sending); | |
| } | |
| QuicTime::Delta send_delta = ack_receive_time - transmission_info.sent_time; | |
| const bool min_rtt_available = !rtt_stats_.min_rtt().IsZero(); | |
| const bool rtt_updated = | |
| rtt_stats_.UpdateRtt(send_delta, ack_delay_time, ack_receive_time); | |
| if (rtt_updated && network_change_visitor_ != nullptr) { | |
| network_change_visitor_->OnRttSampleAvailable( | |
| QuicRttSample{.latest_rtt = rtt_stats_.latest_rtt()}); | |
| } | |
| if (!min_rtt_available && !rtt_stats_.min_rtt().IsZero()) { | |
| loss_algorithm_->OnMinRttAvailable(); | |
| } | |
| return true; | |
| } | |
| QuicTime::Delta QuicSentPacketManager::TimeUntilSend(QuicTime now) const { | |
| // The TLP logic is entirely contained within QuicSentPacketManager, so the | |
| // send algorithm does not need to be consulted. | |
| if (pending_timer_transmission_count_ > 0) { | |
| return QuicTime::Delta::Zero(); | |
| } | |
| if (using_pacing_) { | |
| return pacing_sender_.TimeUntilSend(now, | |
| unacked_packets_.bytes_in_flight()); | |
| } | |
| return send_algorithm_->CanSend(unacked_packets_.bytes_in_flight()) | |
| ? QuicTime::Delta::Zero() | |
| : QuicTime::Delta::Infinite(); | |
| } | |
| const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { | |
| if (!unacked_packets_.HasInFlightPackets() && | |
| PeerCompletedAddressValidation()) { | |
| return QuicTime::Zero(); | |
| } | |
| if (pending_timer_transmission_count_ > 0) { | |
| // Do not set the timer if there is any credit left. | |
| return QuicTime::Zero(); | |
| } | |
| switch (GetRetransmissionMode()) { | |
| case HANDSHAKE_MODE: | |
| return unacked_packets_.GetLastCryptoPacketSentTime() + | |
| GetCryptoRetransmissionDelay(); | |
| case LOSS_MODE: | |
| return loss_algorithm_->GetLossTimeout(); | |
| case PTO_MODE: { | |
| if (!supports_multiple_packet_number_spaces()) { | |
| if (unacked_packets_.HasInFlightPackets() && | |
| consecutive_pto_count_ == 0) { | |
| // Arm 1st PTO with earliest in flight sent time, and make sure at | |
| // least kFirstPtoSrttMultiplier * RTT has been passed since last | |
| // in flight packet. | |
| return std::max( | |
| clock_->ApproximateNow(), | |
| std::max(unacked_packets_.GetFirstInFlightTransmissionInfo() | |
| ->sent_time + | |
| GetProbeTimeoutDelay(NUM_PACKET_NUMBER_SPACES), | |
| unacked_packets_.GetLastInFlightPacketSentTime() + | |
| kFirstPtoSrttMultiplier * | |
| rtt_stats_.SmoothedOrInitialRtt())); | |
| } | |
| // Ensure PTO never gets set to a time in the past. | |
| return std::max(clock_->ApproximateNow(), | |
| unacked_packets_.GetLastInFlightPacketSentTime() + | |
| GetProbeTimeoutDelay(NUM_PACKET_NUMBER_SPACES)); | |
| } | |
| PacketNumberSpace packet_number_space = NUM_PACKET_NUMBER_SPACES; | |
| // earliest_right_edge is the earliest sent time of the last in flight | |
| // packet of all packet number spaces. | |
| QuicTime earliest_right_edge = | |
| GetEarliestPacketSentTimeForPto(&packet_number_space); | |
| if (!earliest_right_edge.IsInitialized()) { | |
| // Arm PTO from now if there is no in flight packets. | |
| earliest_right_edge = clock_->ApproximateNow(); | |
| } | |
| if (packet_number_space == APPLICATION_DATA && | |
| consecutive_pto_count_ == 0) { | |
| const QuicTransmissionInfo* first_application_info = | |
| unacked_packets_.GetFirstInFlightTransmissionInfoOfSpace( | |
| APPLICATION_DATA); | |
| if (first_application_info != nullptr) { | |
| // Arm 1st PTO with earliest in flight sent time, and make sure at | |
| // least kFirstPtoSrttMultiplier * RTT has been passed since last | |
| // in flight packet. Only do this for application data. | |
| return std::max( | |
| clock_->ApproximateNow(), | |
| std::max( | |
| first_application_info->sent_time + | |
| GetProbeTimeoutDelay(packet_number_space), | |
| earliest_right_edge + kFirstPtoSrttMultiplier * | |
| rtt_stats_.SmoothedOrInitialRtt())); | |
| } | |
| } | |
| return std::max( | |
| clock_->ApproximateNow(), | |
| earliest_right_edge + GetProbeTimeoutDelay(packet_number_space)); | |
| } | |
| } | |
| QUICHE_DCHECK(false); | |
| return QuicTime::Zero(); | |
| } | |
| const QuicTime::Delta QuicSentPacketManager::GetPathDegradingDelay() const { | |
| QUICHE_DCHECK_GT(num_ptos_for_path_degrading_, 0); | |
| return num_ptos_for_path_degrading_ * GetPtoDelay(); | |
| } | |
| const QuicTime::Delta QuicSentPacketManager::GetNetworkBlackholeDelay( | |
| int8_t num_rtos_for_blackhole_detection) const { | |
| return GetNConsecutiveRetransmissionTimeoutDelay( | |
| kDefaultMaxTailLossProbes + num_rtos_for_blackhole_detection); | |
| } | |
| QuicTime::Delta QuicSentPacketManager::GetMtuReductionDelay( | |
| int8_t num_rtos_for_blackhole_detection) const { | |
| return GetNetworkBlackholeDelay(num_rtos_for_blackhole_detection / 2); | |
| } | |
| const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay() | |
| const { | |
| // This is equivalent to the TailLossProbeDelay, but slightly more aggressive | |
| // because crypto handshake messages don't incur a delayed ack time. | |
| QuicTime::Delta srtt = rtt_stats_.SmoothedOrInitialRtt(); | |
| int64_t delay_ms; | |
| if (conservative_handshake_retransmits_) { | |
| // Using the delayed ack time directly could cause conservative handshake | |
| // retransmissions to actually be more aggressive than the default. | |
| delay_ms = std::max(peer_max_ack_delay_.ToMilliseconds(), | |
| static_cast<int64_t>(2 * srtt.ToMilliseconds())); | |
| } else { | |
| delay_ms = std::max(kMinHandshakeTimeoutMs, | |
| static_cast<int64_t>(1.5 * srtt.ToMilliseconds())); | |
| } | |
| return QuicTime::Delta::FromMilliseconds( | |
| delay_ms << consecutive_crypto_retransmission_count_); | |
| } | |
| const QuicTime::Delta QuicSentPacketManager::GetProbeTimeoutDelay( | |
| PacketNumberSpace space) const { | |
| if (rtt_stats_.smoothed_rtt().IsZero()) { | |
| // Respect kMinHandshakeTimeoutMs to avoid a potential amplification attack. | |
| QUIC_BUG_IF(quic_bug_12552_6, rtt_stats_.initial_rtt().IsZero()); | |
| return std::max(kPtoMultiplierWithoutRttSamples * rtt_stats_.initial_rtt(), | |
| QuicTime::Delta::FromMilliseconds(kMinHandshakeTimeoutMs)) * | |
| (1 << consecutive_pto_count_); | |
| } | |
| QuicTime::Delta pto_delay = | |
| rtt_stats_.smoothed_rtt() + | |
| std::max(kPtoRttvarMultiplier * rtt_stats_.mean_deviation(), | |
| kAlarmGranularity) + | |
| (ShouldAddMaxAckDelay(space) ? peer_max_ack_delay_ | |
| : QuicTime::Delta::Zero()); | |
| return pto_delay * (1 << consecutive_pto_count_); | |
| } | |
| QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const { | |
| if (send_algorithm_->GetCongestionControlType() == kBBR || | |
| send_algorithm_->GetCongestionControlType() == kBBRv2 || | |
| send_algorithm_->GetCongestionControlType() == kBBRv3) { | |
| return stats_->slowstart_duration.GetTotalElapsedTime( | |
| clock_->ApproximateNow()); | |
| } | |
| return QuicTime::Delta::Infinite(); | |
| } | |
| QuicByteCount QuicSentPacketManager::GetAvailableCongestionWindowInBytes() | |
| const { | |
| QuicByteCount congestion_window = GetCongestionWindowInBytes(); | |
| QuicByteCount bytes_in_flight = GetBytesInFlight(); | |
| return congestion_window - std::min(congestion_window, bytes_in_flight); | |
| } | |
| std::string QuicSentPacketManager::GetDebugState() const { | |
| return send_algorithm_->GetDebugState(); | |
| } | |
| void QuicSentPacketManager::SetSendAlgorithm( | |
| CongestionControlType congestion_control_type) { | |
| if (send_algorithm_ && | |
| send_algorithm_->GetCongestionControlType() == congestion_control_type) { | |
| return; | |
| } | |
| SetSendAlgorithm(SendAlgorithmInterface::Create( | |
| clock_, &rtt_stats_, &unacked_packets_, congestion_control_type, random_, | |
| stats_, initial_congestion_window_, send_algorithm_.get())); | |
| } | |
| void QuicSentPacketManager::SetSendAlgorithm( | |
| SendAlgorithmInterface* send_algorithm) { | |
| if (debug_delegate_ != nullptr && send_algorithm != nullptr) { | |
| debug_delegate_->OnSendAlgorithmChanged( | |
| send_algorithm->GetCongestionControlType()); | |
| } | |
| send_algorithm_.reset(send_algorithm); | |
| pacing_sender_.set_sender(send_algorithm); | |
| } | |
| std::unique_ptr<SendAlgorithmInterface> | |
| QuicSentPacketManager::OnConnectionMigration(bool reset_send_algorithm) { | |
| consecutive_pto_count_ = 0; | |
| rtt_stats_.OnConnectionMigration(); | |
| if (!reset_send_algorithm) { | |
| send_algorithm_->OnConnectionMigration(); | |
| return nullptr; | |
| } | |
| std::unique_ptr<SendAlgorithmInterface> old_send_algorithm = | |
| std::move(send_algorithm_); | |
| SetSendAlgorithm(old_send_algorithm->GetCongestionControlType()); | |
| // Treat all in flight packets sent to the old peer address as lost and | |
| // retransmit them. | |
| QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked(); | |
| for (auto it = unacked_packets_.begin(); it != unacked_packets_.end(); | |
| ++it, ++packet_number) { | |
| if (it->in_flight) { | |
| // Proactively retransmit any packet which is in flight on the old path. | |
| // As a result, these packets will not contribute to congestion control. | |
| unacked_packets_.RemoveFromInFlight(packet_number); | |
| // Retransmitting these packets with PATH_CHANGE_RETRANSMISSION will mark | |
| // them as useless, thus not contributing to RTT stats. | |
| if (unacked_packets_.HasRetransmittableFrames(packet_number)) { | |
| MarkForRetransmission(packet_number, PATH_RETRANSMISSION); | |
| QUICHE_DCHECK_EQ(it->state, NOT_CONTRIBUTING_RTT); | |
| } | |
| old_send_algorithm->OnPacketNeutered(packet_number); | |
| } | |
| it->state = NOT_CONTRIBUTING_RTT; | |
| } | |
| return old_send_algorithm; | |
| } | |
| void QuicSentPacketManager::OnAckFrameStart(QuicPacketNumber largest_acked, | |
| QuicTime::Delta ack_delay_time, | |
| QuicTime ack_receive_time) { | |
| QUICHE_DCHECK(packets_acked_.empty()); | |
| QUICHE_DCHECK_LE(largest_acked, unacked_packets_.largest_sent_packet()); | |
| // Ignore peer_max_ack_delay and use received ack_delay during | |
| // handshake when supporting multiple packet number spaces. | |
| if (!supports_multiple_packet_number_spaces() || handshake_finished_) { | |
| if (ack_delay_time > peer_max_ack_delay()) { | |
| ack_delay_time = peer_max_ack_delay(); | |
| } | |
| if (ignore_ack_delay_) { | |
| ack_delay_time = QuicTime::Delta::Zero(); | |
| } | |
| } | |
| rtt_updated_ = | |
| MaybeUpdateRTT(largest_acked, ack_delay_time, ack_receive_time); | |
| last_ack_frame_.ack_delay_time = ack_delay_time; | |
| acked_packets_iter_ = last_ack_frame_.packets.rbegin(); | |
| } | |
| void QuicSentPacketManager::OnAckRange(QuicPacketNumber start, | |
| QuicPacketNumber end) { | |
| if (!last_ack_frame_.largest_acked.IsInitialized() || | |
| end > last_ack_frame_.largest_acked + 1) { | |
| // Largest acked increases. | |
| unacked_packets_.IncreaseLargestAcked(end - 1); | |
| last_ack_frame_.largest_acked = end - 1; | |
| } | |
| // Drop ack ranges which ack packets below least_unacked. | |
| QuicPacketNumber least_unacked = unacked_packets_.GetLeastUnacked(); | |
| if (least_unacked.IsInitialized() && end <= least_unacked) { | |
| return; | |
| } | |
| start = std::max(start, least_unacked); | |
| do { | |
| QuicPacketNumber newly_acked_start = start; | |
| if (acked_packets_iter_ != last_ack_frame_.packets.rend()) { | |
| newly_acked_start = std::max(start, acked_packets_iter_->max()); | |
| } | |
| for (QuicPacketNumber acked = end - 1; acked >= newly_acked_start; | |
| --acked) { | |
| // Check if end is above the current range. If so add newly acked packets | |
| // in descending order. | |
| packets_acked_.push_back(AckedPacket(acked, 0, std::nullopt)); | |
| if (acked == FirstSendingPacketNumber()) { | |
| break; | |
| } | |
| } | |
| if (acked_packets_iter_ == last_ack_frame_.packets.rend() || | |
| start > acked_packets_iter_->min()) { | |
| // Finish adding all newly acked packets. | |
| return; | |
| } | |
| end = std::min(end, acked_packets_iter_->min()); | |
| ++acked_packets_iter_; | |
| } while (start < end); | |
| } | |
| void QuicSentPacketManager::OnAckTimestamp(QuicPacketNumber packet_number, | |
| QuicReceiveTime timestamp) { | |
| last_ack_frame_.received_packet_times.push_back({packet_number, timestamp}); | |
| for (AckedPacket& packet : packets_acked_) { | |
| if (packet.packet_number == packet_number) { | |
| packet.receive_timestamp = timestamp; | |
| return; | |
| } | |
| } | |
| } | |
| bool QuicSentPacketManager::IsEcnFeedbackValid( | |
| PacketNumberSpace space, const std::optional<QuicEcnCounts>& ecn_counts, | |
| QuicPacketCount newly_acked_ect0, QuicPacketCount newly_acked_ect1) { | |
| if (!ecn_counts.has_value()) { | |
| if (newly_acked_ect0 > 0 || newly_acked_ect1 > 0) { | |
| QUIC_DVLOG(1) << ENDPOINT | |
| << "ECN packets acknowledged, no counts reported."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| if (ecn_counts->ect0 < peer_ack_ecn_counts_[space].ect0 || | |
| ecn_counts->ect1 < peer_ack_ecn_counts_[space].ect1 || | |
| ecn_counts->ce < peer_ack_ecn_counts_[space].ce) { | |
| QUIC_DVLOG(1) << ENDPOINT << "Reported ECN count declined."; | |
| return false; | |
| } | |
| if (ecn_counts->ect0 > ect0_packets_sent_[space] || | |
| ecn_counts->ect1 > ect1_packets_sent_[space] || | |
| (ecn_counts->ect0 + ecn_counts->ect1 + ecn_counts->ce > | |
| ect0_packets_sent_[space] + ect1_packets_sent_[space])) { | |
| QUIC_DVLOG(1) << ENDPOINT << "Reported ECT + CE exceeds packets sent:" | |
| << " reported " << ecn_counts->ToString() << " , ECT(0) sent " | |
| << ect0_packets_sent_[space] << " , ECT(1) sent " | |
| << ect1_packets_sent_[space]; | |
| return false; | |
| } | |
| if ((newly_acked_ect0 > | |
| (ecn_counts->ect0 + ecn_counts->ce - peer_ack_ecn_counts_[space].ect0 + | |
| peer_ack_ecn_counts_[space].ce)) || | |
| (newly_acked_ect1 > | |
| (ecn_counts->ect1 + ecn_counts->ce - peer_ack_ecn_counts_[space].ect1 + | |
| peer_ack_ecn_counts_[space].ce))) { | |
| QUIC_DVLOG(1) << ENDPOINT | |
| << "Peer acked packet but did not report the ECN mark: " | |
| << " New ECN counts: " << ecn_counts->ToString() | |
| << " Old ECN counts: " | |
| << peer_ack_ecn_counts_[space].ToString() | |
| << " Newly acked ECT(0) : " << newly_acked_ect0 | |
| << " Newly acked ECT(1) : " << newly_acked_ect1; | |
| return false; | |
| } | |
| return true; | |
| } | |
| AckResult QuicSentPacketManager::OnAckFrameEnd( | |
| QuicTime ack_receive_time, QuicPacketNumber ack_packet_number, | |
| EncryptionLevel ack_decrypted_level, | |
| const std::optional<QuicEcnCounts>& ecn_counts) { | |
| QuicByteCount prior_bytes_in_flight = unacked_packets_.bytes_in_flight(); | |
| QuicPacketCount newly_acked_ect0 = 0; | |
| QuicPacketCount newly_acked_ect1 = 0; | |
| PacketNumberSpace acked_packet_number_space = | |
| QuicUtils::GetPacketNumberSpace(ack_decrypted_level); | |
| QuicPacketNumber old_largest_acked = | |
| unacked_packets_.GetLargestAckedOfPacketNumberSpace( | |
| acked_packet_number_space); | |
| // Reverse packets_acked_ so that it is in ascending order. | |
| std::reverse(packets_acked_.begin(), packets_acked_.end()); | |
| for (AckedPacket& acked_packet : packets_acked_) { | |
| QuicTransmissionInfo* info = | |
| unacked_packets_.GetMutableTransmissionInfo(acked_packet.packet_number); | |
| if (!QuicUtils::IsAckable(info->state)) { | |
| if (info->state == ACKED) { | |
| QUIC_BUG(quic_bug_10750_5) | |
| << "Trying to ack an already acked packet: " | |
| << acked_packet.packet_number | |
| << ", last_ack_frame_: " << last_ack_frame_ | |
| << ", least_unacked: " << unacked_packets_.GetLeastUnacked() | |
| << ", packets_acked_: " << quiche::PrintElements(packets_acked_); | |
| } else { | |
| QUIC_PEER_BUG(quic_peer_bug_10750_6) | |
| << "Received " << ack_decrypted_level | |
| << " ack for unackable packet: " << acked_packet.packet_number | |
| << " with state: " | |
| << QuicUtils::SentPacketStateToString(info->state); | |
| if (supports_multiple_packet_number_spaces()) { | |
| if (info->state == NEVER_SENT) { | |
| return UNSENT_PACKETS_ACKED; | |
| } | |
| return UNACKABLE_PACKETS_ACKED; | |
| } | |
| } | |
| continue; | |
| } | |
| QUIC_DVLOG(1) << ENDPOINT << "Got an " << ack_decrypted_level | |
| << " ack for packet " << acked_packet.packet_number | |
| << " , state: " | |
| << QuicUtils::SentPacketStateToString(info->state); | |
| const PacketNumberSpace packet_number_space = | |
| unacked_packets_.GetPacketNumberSpace(info->encryption_level); | |
| if (supports_multiple_packet_number_spaces() && | |
| QuicUtils::GetPacketNumberSpace(ack_decrypted_level) != | |
| packet_number_space) { | |
| return PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE; | |
| } | |
| last_ack_frame_.packets.Add(acked_packet.packet_number); | |
| if (info->encryption_level == ENCRYPTION_HANDSHAKE) { | |
| handshake_packet_acked_ = true; | |
| } else if (info->encryption_level == ENCRYPTION_ZERO_RTT) { | |
| zero_rtt_packet_acked_ = true; | |
| } else if (info->encryption_level == ENCRYPTION_FORWARD_SECURE) { | |
| one_rtt_packet_acked_ = true; | |
| } | |
| if (packet_number_space == APPLICATION_DATA) { | |
| ++stats_->application_data_packets_acked; | |
| if (acked_packet.receive_timestamp.has_value()) { | |
| ++stats_->application_data_packets_with_receive_timestamps_acked; | |
| } | |
| } | |
| largest_packet_peer_knows_is_acked_.UpdateMax(info->largest_acked); | |
| if (supports_multiple_packet_number_spaces()) { | |
| largest_packets_peer_knows_is_acked_[packet_number_space].UpdateMax( | |
| info->largest_acked); | |
| } | |
| // If data is associated with the most recent transmission of this | |
| // packet, then inform the caller. | |
| if (info->in_flight) { | |
| acked_packet.bytes_acked = info->bytes_sent; | |
| } else { | |
| acked_packet.spurious_loss = (info->state == LOST); | |
| // Unackable packets are skipped earlier. | |
| largest_newly_acked_ = acked_packet.packet_number; | |
| } | |
| switch (info->ecn_codepoint) { | |
| case ECN_NOT_ECT: | |
| break; | |
| case ECN_CE: | |
| // ECN_CE should only happen in tests. Feedback validation doesn't track | |
| // newly acked CEs, and if newly_acked_ect0 and newly_acked_ect1 are | |
| // lower than expected that won't fail validation. So when it's CE don't | |
| // increment anything. | |
| break; | |
| case ECN_ECT0: | |
| ++newly_acked_ect0; | |
| if (info->in_flight) { | |
| network_change_visitor_->OnInFlightEcnPacketAcked(); | |
| } | |
| break; | |
| case ECN_ECT1: | |
| ++newly_acked_ect1; | |
| if (info->in_flight) { | |
| network_change_visitor_->OnInFlightEcnPacketAcked(); | |
| } | |
| break; | |
| } | |
| unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace( | |
| packet_number_space, acked_packet.packet_number); | |
| MarkPacketHandled(acked_packet.packet_number, ack_receive_time, | |
| last_ack_frame_.ack_delay_time, info); | |
| } | |
| // Copy raw ECN counts to last_ack_frame_ so it is logged properly. Validated | |
| // ECN counts are stored in valid_ecn_counts, and the congestion controller | |
| // uses that for processing. | |
| last_ack_frame_.ecn_counters = ecn_counts; | |
| // Validate ECN feedback. | |
| std::optional<QuicEcnCounts> valid_ecn_counts; | |
| if (ecn_queried_) { | |
| if (IsEcnFeedbackValid(acked_packet_number_space, ecn_counts, | |
| newly_acked_ect0, newly_acked_ect1)) { | |
| valid_ecn_counts = ecn_counts; | |
| } else if (!old_largest_acked.IsInitialized() || | |
| old_largest_acked < | |
| unacked_packets_.GetLargestAckedOfPacketNumberSpace( | |
| acked_packet_number_space)) { | |
| // RFC 9000 S13.4.2.1: "An endpoint MUST NOT fail ECN validation as a | |
| // result of processing an ACK frame that does not increase the largest | |
| // acknowledged packet number." | |
| network_change_visitor_->OnInvalidEcnFeedback(); | |
| } | |
| } | |
| const bool acked_new_packet = !packets_acked_.empty(); | |
| PostProcessNewlyAckedPackets(ack_packet_number, ack_decrypted_level, | |
| last_ack_frame_, ack_receive_time, rtt_updated_, | |
| prior_bytes_in_flight, valid_ecn_counts); | |
| if (valid_ecn_counts.has_value()) { | |
| peer_ack_ecn_counts_[acked_packet_number_space] = *valid_ecn_counts; | |
| } | |
| return acked_new_packet ? PACKETS_NEWLY_ACKED : NO_PACKETS_NEWLY_ACKED; | |
| } | |
| void QuicSentPacketManager::SetDebugDelegate(DebugDelegate* debug_delegate) { | |
| debug_delegate_ = debug_delegate; | |
| } | |
| void QuicSentPacketManager::OnApplicationLimited() { | |
| if (using_pacing_) { | |
| pacing_sender_.OnApplicationLimited(); | |
| } | |
| send_algorithm_->OnApplicationLimited(unacked_packets_.bytes_in_flight()); | |
| if (debug_delegate_ != nullptr) { | |
| debug_delegate_->OnApplicationLimited(); | |
| } | |
| } | |
| NextReleaseTimeResult QuicSentPacketManager::GetNextReleaseTime() const { | |
| if (!using_pacing_) { | |
| return {QuicTime::Zero(), false}; | |
| } | |
| return pacing_sender_.GetNextReleaseTime(); | |
| } | |
| void QuicSentPacketManager::SetInitialRtt(QuicTime::Delta rtt, bool trusted) { | |
| const QuicTime::Delta min_rtt = QuicTime::Delta::FromMicroseconds( | |
| trusted ? kMinTrustedInitialRoundTripTimeUs | |
| : kMinUntrustedInitialRoundTripTimeUs); | |
| QuicTime::Delta max_rtt = | |
| QuicTime::Delta::FromMicroseconds(kMaxInitialRoundTripTimeUs); | |
| rtt_stats_.set_initial_rtt(std::max(min_rtt, std::min(max_rtt, rtt))); | |
| } | |
| void QuicSentPacketManager::EnableMultiplePacketNumberSpacesSupport() { | |
| EnableIetfPtoAndLossDetection(); | |
| unacked_packets_.EnableMultiplePacketNumberSpacesSupport(); | |
| } | |
| QuicPacketNumber QuicSentPacketManager::GetLargestAckedPacket( | |
| EncryptionLevel decrypted_packet_level) const { | |
| QUICHE_DCHECK(supports_multiple_packet_number_spaces()); | |
| return unacked_packets_.GetLargestAckedOfPacketNumberSpace( | |
| QuicUtils::GetPacketNumberSpace(decrypted_packet_level)); | |
| } | |
| QuicPacketNumber QuicSentPacketManager::GetLeastPacketAwaitedByPeer( | |
| EncryptionLevel encryption_level) const { | |
| QuicPacketNumber largest_acked; | |
| if (supports_multiple_packet_number_spaces()) { | |
| largest_acked = GetLargestAckedPacket(encryption_level); | |
| } else { | |
| largest_acked = GetLargestObserved(); | |
| } | |
| if (!largest_acked.IsInitialized()) { | |
| // If no packets have been acked, return the first sent packet to ensure | |
| // we use a large enough packet number length. | |
| return FirstSendingPacketNumber(); | |
| } | |
| QuicPacketNumber least_awaited = largest_acked + 1; | |
| QuicPacketNumber least_unacked = GetLeastUnacked(); | |
| if (least_unacked.IsInitialized() && least_unacked < least_awaited) { | |
| least_awaited = least_unacked; | |
| } | |
| return least_awaited; | |
| } | |
| QuicPacketNumber QuicSentPacketManager::GetLargestPacketPeerKnowsIsAcked( | |
| EncryptionLevel decrypted_packet_level) const { | |
| if (supports_multiple_packet_number_spaces()) { | |
| return largest_packets_peer_knows_is_acked_[QuicUtils::GetPacketNumberSpace( | |
| decrypted_packet_level)]; | |
| } | |
| return largest_packet_peer_knows_is_acked_; | |
| } | |
| QuicTime::Delta | |
| QuicSentPacketManager::GetNConsecutiveRetransmissionTimeoutDelay( | |
| int num_timeouts) const { | |
| QuicTime::Delta total_delay = QuicTime::Delta::Zero(); | |
| const QuicTime::Delta srtt = rtt_stats_.SmoothedOrInitialRtt(); | |
| int num_tlps = | |
| std::min(num_timeouts, static_cast<int>(kDefaultMaxTailLossProbes)); | |
| num_timeouts -= num_tlps; | |
| if (num_tlps > 0) { | |
| const QuicTime::Delta tlp_delay = std::max( | |
| 2 * srtt, | |
| unacked_packets_.HasMultipleInFlightPackets() | |
| ? QuicTime::Delta::FromMilliseconds(kMinTailLossProbeTimeoutMs) | |
| : (1.5 * srtt + | |
| (QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs) * | |
| 0.5))); | |
| total_delay = total_delay + num_tlps * tlp_delay; | |
| } | |
| if (num_timeouts == 0) { | |
| return total_delay; | |
| } | |
| const QuicTime::Delta retransmission_delay = | |
| rtt_stats_.smoothed_rtt().IsZero() | |
| ? QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs) | |
| : std::max( | |
| srtt + 4 * rtt_stats_.mean_deviation(), | |
| QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs)); | |
| total_delay = total_delay + ((1 << num_timeouts) - 1) * retransmission_delay; | |
| return total_delay; | |
| } | |
| bool QuicSentPacketManager::PeerCompletedAddressValidation() const { | |
| if (unacked_packets_.perspective() == Perspective::IS_SERVER || | |
| !handshake_mode_disabled_) { | |
| return true; | |
| } | |
| // To avoid handshake deadlock due to anti-amplification limit, client needs | |
| // to set PTO timer until server successfully processed any HANDSHAKE packet. | |
| return handshake_finished_ || handshake_packet_acked_; | |
| } | |
| bool QuicSentPacketManager::IsLessThanThreePTOs(QuicTime::Delta timeout) const { | |
| return timeout < 3 * GetPtoDelay(); | |
| } | |
| QuicTime::Delta QuicSentPacketManager::GetPtoDelay() const { | |
| return GetProbeTimeoutDelay(APPLICATION_DATA); | |
| } | |
| void QuicSentPacketManager::OnAckFrequencyFrameSent( | |
| const QuicAckFrequencyFrame& ack_frequency_frame) { | |
| in_use_sent_ack_delays_.emplace_back( | |
| ack_frequency_frame.requested_max_ack_delay, | |
| ack_frequency_frame.sequence_number); | |
| if (ack_frequency_frame.requested_max_ack_delay > peer_max_ack_delay_) { | |
| peer_max_ack_delay_ = ack_frequency_frame.requested_max_ack_delay; | |
| } | |
| } | |
| void QuicSentPacketManager::OnAckFrequencyFrameAcked( | |
| const QuicAckFrequencyFrame& ack_frequency_frame) { | |
| int stale_entry_count = 0; | |
| for (auto it = in_use_sent_ack_delays_.cbegin(); | |
| it != in_use_sent_ack_delays_.cend(); ++it) { | |
| if (it->second < ack_frequency_frame.sequence_number) { | |
| ++stale_entry_count; | |
| } else { | |
| break; | |
| } | |
| } | |
| if (stale_entry_count > 0) { | |
| in_use_sent_ack_delays_.pop_front_n(stale_entry_count); | |
| } | |
| if (in_use_sent_ack_delays_.empty()) { | |
| QUIC_BUG(quic_bug_10750_7) << "in_use_sent_ack_delays_ is empty."; | |
| return; | |
| } | |
| peer_max_ack_delay_ = std::max_element(in_use_sent_ack_delays_.cbegin(), | |
| in_use_sent_ack_delays_.cend()) | |
| ->first; | |
| } | |
| float QuicSentPacketManager::GetOverheadEstimate() const { | |
| // `overhead_total_bytes_` check is a defense-in-depth again divide-by-zero. | |
| if (overhead_total_bytes_ < kMinBytesForOverheadMeasurement || | |
| overhead_good_bytes_ < kMinBytesForOverheadMeasurement) { | |
| return kDefaultOverhead; | |
| } | |
| return 1.0f - static_cast<float>(overhead_good_bytes_) / | |
| static_cast<float>(overhead_total_bytes_); | |
| } | |
| void QuicSentPacketManager::UpdateOverheadMeasurements( | |
| const SerializedPacket& packet) { | |
| if (!measure_overhead_) { | |
| return; | |
| } | |
| // Ignore packets with the long header. | |
| if (packet.encryption_level != ENCRYPTION_FORWARD_SECURE) { | |
| return; | |
| } | |
| overhead_total_bytes_ += packet.encrypted_length; | |
| for (const QuicFrame& frame : packet.retransmittable_frames) { | |
| if (frame.type == QuicFrameType::STREAM_FRAME) { | |
| overhead_good_bytes_ += frame.stream_frame.data_length; | |
| } | |
| if (frame.type == QuicFrameType::DATAGRAM_FRAME) { | |
| overhead_good_bytes_ += frame.datagram_frame->datagram_length; | |
| } | |
| } | |
| } | |
| #undef ENDPOINT // undef for jumbo builds | |
| } // namespace quic |