| // Copyright (c) 2012 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_connection.h" |
| |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <iterator> |
| #include <limits> |
| #include <memory> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "quiche/quic/core/congestion_control/rtt_stats.h" |
| #include "quiche/quic/core/congestion_control/send_algorithm_interface.h" |
| #include "quiche/quic/core/crypto/crypto_protocol.h" |
| #include "quiche/quic/core/crypto/crypto_utils.h" |
| #include "quiche/quic/core/crypto/quic_decrypter.h" |
| #include "quiche/quic/core/crypto/quic_encrypter.h" |
| #include "quiche/quic/core/proto/cached_network_parameters_proto.h" |
| #include "quiche/quic/core/quic_bandwidth.h" |
| #include "quiche/quic/core/quic_config.h" |
| #include "quiche/quic/core/quic_connection_id.h" |
| #include "quiche/quic/core/quic_constants.h" |
| #include "quiche/quic/core/quic_error_codes.h" |
| #include "quiche/quic/core/quic_legacy_version_encapsulator.h" |
| #include "quiche/quic/core/quic_packet_creator.h" |
| #include "quiche/quic/core/quic_packet_writer.h" |
| #include "quiche/quic/core/quic_packets.h" |
| #include "quiche/quic/core/quic_path_validator.h" |
| #include "quiche/quic/core/quic_time.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_client_stats.h" |
| #include "quiche/quic/platform/api/quic_exported_stats.h" |
| #include "quiche/quic/platform/api/quic_flag_utils.h" |
| #include "quiche/quic/platform/api/quic_flags.h" |
| #include "quiche/quic/platform/api/quic_hostname_utils.h" |
| #include "quiche/quic/platform/api/quic_logging.h" |
| #include "quiche/quic/platform/api/quic_server_stats.h" |
| #include "quiche/quic/platform/api/quic_socket_address.h" |
| #include "quiche/common/platform/api/quiche_flag_utils.h" |
| #include "quiche/common/quiche_text_utils.h" |
| |
| namespace quic { |
| |
| class QuicDecrypter; |
| class QuicEncrypter; |
| |
| namespace { |
| |
| // Maximum number of consecutive sent nonretransmittable packets. |
| const QuicPacketCount kMaxConsecutiveNonRetransmittablePackets = 19; |
| |
| // The minimum release time into future in ms. |
| const int kMinReleaseTimeIntoFutureMs = 1; |
| |
| // Base class of all alarms owned by a QuicConnection. |
| class QuicConnectionAlarmDelegate : public QuicAlarm::Delegate { |
| public: |
| explicit QuicConnectionAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| QuicConnectionAlarmDelegate(const QuicConnectionAlarmDelegate&) = delete; |
| QuicConnectionAlarmDelegate& operator=(const QuicConnectionAlarmDelegate&) = |
| delete; |
| |
| QuicConnectionContext* GetConnectionContext() override { |
| return (connection_ == nullptr) ? nullptr : connection_->context(); |
| } |
| |
| protected: |
| QuicConnection* connection_; |
| }; |
| |
| // An alarm that is scheduled to send an ack if a timeout occurs. |
| class AckAlarmDelegate : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->ack_frame_updated()); |
| QUICHE_DCHECK(connection_->connected()); |
| QuicConnection::ScopedPacketFlusher flusher(connection_); |
| if (connection_->SupportsMultiplePacketNumberSpaces()) { |
| connection_->SendAllPendingAcks(); |
| } else { |
| connection_->SendAck(); |
| } |
| } |
| }; |
| |
| // This alarm will be scheduled any time a data-bearing packet is sent out. |
| // When the alarm goes off, the connection checks to see if the oldest packets |
| // have been acked, and retransmit them if they have not. |
| class RetransmissionAlarmDelegate : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->OnRetransmissionTimeout(); |
| } |
| }; |
| |
| // An alarm that is scheduled when the SentPacketManager requires a delay |
| // before sending packets and fires when the packet may be sent. |
| class SendAlarmDelegate : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->WriteIfNotBlocked(); |
| } |
| }; |
| |
| class PingAlarmDelegate : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| QUICHE_DCHECK(!GetQuicReloadableFlag(quic_use_ping_manager2)); |
| connection_->OnPingTimeout(); |
| } |
| }; |
| |
| class MtuDiscoveryAlarmDelegate : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->DiscoverMtu(); |
| } |
| }; |
| |
| class ProcessUndecryptablePacketsAlarmDelegate |
| : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| QuicConnection::ScopedPacketFlusher flusher(connection_); |
| connection_->MaybeProcessUndecryptablePackets(); |
| } |
| }; |
| |
| class DiscardPreviousOneRttKeysAlarmDelegate |
| : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->DiscardPreviousOneRttKeys(); |
| } |
| }; |
| |
| class DiscardZeroRttDecryptionKeysAlarmDelegate |
| : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| QUIC_DLOG(INFO) << "0-RTT discard alarm fired"; |
| connection_->RemoveDecrypter(ENCRYPTION_ZERO_RTT); |
| connection_->RetireOriginalDestinationConnectionId(); |
| } |
| }; |
| |
| class MultiPortProbingAlarmDelegate : public QuicConnectionAlarmDelegate { |
| public: |
| using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| QUIC_DLOG(INFO) << "Alternative path probing alarm fired"; |
| connection_->ProbeMultiPortPath(); |
| } |
| }; |
| |
| // When the clearer goes out of scope, the coalesced packet gets cleared. |
| class ScopedCoalescedPacketClearer { |
| public: |
| explicit ScopedCoalescedPacketClearer(QuicCoalescedPacket* coalesced) |
| : coalesced_(coalesced) {} |
| ~ScopedCoalescedPacketClearer() { coalesced_->Clear(); } |
| |
| private: |
| QuicCoalescedPacket* coalesced_; // Unowned. |
| }; |
| |
| // Whether this incoming packet is allowed to replace our connection ID. |
| bool PacketCanReplaceServerConnectionId(const QuicPacketHeader& header, |
| Perspective perspective) { |
| return perspective == Perspective::IS_CLIENT && |
| header.form == IETF_QUIC_LONG_HEADER_PACKET && |
| header.version.IsKnown() && |
| header.version.AllowsVariableLengthConnectionIds() && |
| (header.long_packet_type == INITIAL || |
| header.long_packet_type == RETRY); |
| } |
| |
| // Due to a lost Initial packet, a Handshake packet might use a new connection |
| // ID we haven't seen before. We shouldn't update the connection ID based on |
| // this, but should buffer the packet in case it works out. |
| bool NewServerConnectionIdMightBeValid(const QuicPacketHeader& header, |
| Perspective perspective, |
| bool connection_id_already_replaced) { |
| return perspective == Perspective::IS_CLIENT && |
| header.form == IETF_QUIC_LONG_HEADER_PACKET && |
| header.version.IsKnown() && |
| header.version.AllowsVariableLengthConnectionIds() && |
| header.long_packet_type == HANDSHAKE && |
| !connection_id_already_replaced; |
| } |
| |
| CongestionControlType GetDefaultCongestionControlType() { |
| if (GetQuicReloadableFlag(quic_default_to_bbr_v2)) { |
| return kBBRv2; |
| } |
| |
| if (GetQuicReloadableFlag(quic_default_to_bbr)) { |
| return kBBR; |
| } |
| |
| return kCubicBytes; |
| } |
| |
| bool ContainsNonProbingFrame(const SerializedPacket& packet) { |
| for (const QuicFrame& frame : packet.nonretransmittable_frames) { |
| if (!QuicUtils::IsProbingFrame(frame.type)) { |
| return true; |
| } |
| } |
| for (const QuicFrame& frame : packet.retransmittable_frames) { |
| if (!QuicUtils::IsProbingFrame(frame.type)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| #define ENDPOINT \ |
| (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ") |
| |
| QuicConnection::QuicConnection( |
| QuicConnectionId server_connection_id, |
| QuicSocketAddress initial_self_address, |
| QuicSocketAddress initial_peer_address, |
| QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory, |
| QuicPacketWriter* writer, bool owns_writer, Perspective perspective, |
| const ParsedQuicVersionVector& supported_versions, |
| ConnectionIdGeneratorInterface& generator) |
| : framer_(supported_versions, helper->GetClock()->ApproximateNow(), |
| perspective, server_connection_id.length()), |
| current_packet_content_(NO_FRAMES_RECEIVED), |
| is_current_packet_connectivity_probing_(false), |
| has_path_challenge_in_current_packet_(false), |
| current_effective_peer_migration_type_(NO_CHANGE), |
| helper_(helper), |
| alarm_factory_(alarm_factory), |
| per_packet_options_(nullptr), |
| writer_(writer), |
| owns_writer_(owns_writer), |
| encryption_level_(ENCRYPTION_INITIAL), |
| clock_(helper->GetClock()), |
| random_generator_(helper->GetRandomGenerator()), |
| client_connection_id_is_set_(false), |
| direct_peer_address_(initial_peer_address), |
| default_path_(initial_self_address, QuicSocketAddress(), |
| /*client_connection_id=*/EmptyQuicConnectionId(), |
| server_connection_id, |
| /*stateless_reset_token=*/absl::nullopt), |
| active_effective_peer_migration_type_(NO_CHANGE), |
| support_key_update_for_connection_(false), |
| current_packet_data_(nullptr), |
| should_last_packet_instigate_acks_(false), |
| max_undecryptable_packets_(0), |
| max_tracked_packets_(GetQuicFlag(quic_max_tracked_packet_count)), |
| idle_timeout_connection_close_behavior_( |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET), |
| num_rtos_for_blackhole_detection_(0), |
| uber_received_packet_manager_(&stats_), |
| stop_waiting_count_(0), |
| pending_retransmission_alarm_(false), |
| defer_send_in_response_to_packets_(false), |
| keep_alive_ping_timeout_(QuicTime::Delta::FromSeconds(kPingTimeoutSecs)), |
| initial_retransmittable_on_wire_timeout_(QuicTime::Delta::Infinite()), |
| consecutive_retransmittable_on_wire_ping_count_(0), |
| retransmittable_on_wire_ping_count_(0), |
| arena_(), |
| ack_alarm_(alarm_factory_->CreateAlarm(arena_.New<AckAlarmDelegate>(this), |
| &arena_)), |
| retransmission_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<RetransmissionAlarmDelegate>(this), &arena_)), |
| send_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<SendAlarmDelegate>(this), &arena_)), |
| ping_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<PingAlarmDelegate>(this), &arena_)), |
| mtu_discovery_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<MtuDiscoveryAlarmDelegate>(this), &arena_)), |
| process_undecryptable_packets_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<ProcessUndecryptablePacketsAlarmDelegate>(this), &arena_)), |
| discard_previous_one_rtt_keys_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<DiscardPreviousOneRttKeysAlarmDelegate>(this), &arena_)), |
| discard_zero_rtt_decryption_keys_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<DiscardZeroRttDecryptionKeysAlarmDelegate>(this), |
| &arena_)), |
| multi_port_probing_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<MultiPortProbingAlarmDelegate>(this), &arena_)), |
| visitor_(nullptr), |
| debug_visitor_(nullptr), |
| packet_creator_(server_connection_id, &framer_, random_generator_, this), |
| last_received_packet_info_(clock_->ApproximateNow()), |
| sent_packet_manager_(perspective, clock_, random_generator_, &stats_, |
| GetDefaultCongestionControlType()), |
| version_negotiated_(false), |
| perspective_(perspective), |
| connected_(true), |
| can_truncate_connection_ids_(perspective == Perspective::IS_SERVER), |
| mtu_probe_count_(0), |
| previous_validated_mtu_(0), |
| peer_max_packet_size_(kDefaultMaxPacketSizeTransportParam), |
| largest_received_packet_size_(0), |
| write_error_occurred_(false), |
| no_stop_waiting_frames_(version().HasIetfInvariantHeader()), |
| consecutive_num_packets_with_no_retransmittable_frames_(0), |
| max_consecutive_num_packets_with_no_retransmittable_frames_( |
| kMaxConsecutiveNonRetransmittablePackets), |
| bundle_retransmittable_with_pto_ack_(false), |
| last_control_frame_id_(kInvalidControlFrameId), |
| is_path_degrading_(false), |
| processing_ack_frame_(false), |
| supports_release_time_(false), |
| release_time_into_future_(QuicTime::Delta::Zero()), |
| blackhole_detector_(this, &arena_, alarm_factory_, &context_), |
| idle_network_detector_(this, clock_->ApproximateNow(), &arena_, |
| alarm_factory_, &context_), |
| path_validator_(alarm_factory_, &arena_, this, random_generator_, clock_, |
| &context_), |
| ping_manager_(perspective, this, &arena_, alarm_factory_, &context_), |
| multi_port_probing_interval_(kDefaultMultiPortProbingInterval), |
| connection_id_generator_(generator) { |
| QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT || |
| default_path_.self_address.IsInitialized()); |
| |
| QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " |
| << server_connection_id |
| << " and version: " << ParsedQuicVersionToString(version()); |
| |
| QUIC_BUG_IF(quic_bug_12714_2, !QuicUtils::IsConnectionIdValidForVersion( |
| server_connection_id, transport_version())) |
| << "QuicConnection: attempted to use server connection ID " |
| << server_connection_id << " which is invalid with version " << version(); |
| framer_.set_visitor(this); |
| stats_.connection_creation_time = clock_->ApproximateNow(); |
| // TODO(ianswett): Supply the NetworkChangeVisitor as a constructor argument |
| // and make it required non-null, because it's always used. |
| sent_packet_manager_.SetNetworkChangeVisitor(this); |
| if (GetQuicRestartFlag(quic_offload_pacing_to_usps2)) { |
| sent_packet_manager_.SetPacingAlarmGranularity(QuicTime::Delta::Zero()); |
| release_time_into_future_ = |
| QuicTime::Delta::FromMilliseconds(kMinReleaseTimeIntoFutureMs); |
| } |
| // Allow the packet writer to potentially reduce the packet size to a value |
| // even smaller than kDefaultMaxPacketSize. |
| SetMaxPacketLength(perspective_ == Perspective::IS_SERVER |
| ? kDefaultServerMaxPacketSize |
| : kDefaultMaxPacketSize); |
| uber_received_packet_manager_.set_max_ack_ranges(255); |
| MaybeEnableMultiplePacketNumberSpacesSupport(); |
| QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT || |
| supported_versions.size() == 1); |
| InstallInitialCrypters(default_path_.server_connection_id); |
| |
| // On the server side, version negotiation has been done by the dispatcher, |
| // and the server connection is created with the right version. |
| if (perspective_ == Perspective::IS_SERVER) { |
| SetVersionNegotiated(); |
| } |
| if (default_enable_5rto_blackhole_detection_) { |
| num_rtos_for_blackhole_detection_ = 5; |
| if (GetQuicReloadableFlag(quic_disable_server_blackhole_detection) && |
| perspective_ == Perspective::IS_SERVER) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_disable_server_blackhole_detection); |
| blackhole_detection_disabled_ = true; |
| } |
| } |
| packet_creator_.SetDefaultPeerAddress(initial_peer_address); |
| } |
| |
| void QuicConnection::InstallInitialCrypters(QuicConnectionId connection_id) { |
| CrypterPair crypters; |
| CryptoUtils::CreateInitialObfuscators(perspective_, version(), connection_id, |
| &crypters); |
| SetEncrypter(ENCRYPTION_INITIAL, std::move(crypters.encrypter)); |
| if (version().KnowsWhichDecrypterToUse()) { |
| InstallDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter)); |
| } else { |
| SetDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter)); |
| } |
| } |
| |
| QuicConnection::~QuicConnection() { |
| QUICHE_DCHECK_GE(stats_.max_egress_mtu, long_term_mtu_); |
| if (owns_writer_) { |
| delete writer_; |
| } |
| ClearQueuedPackets(); |
| if (stats_ |
| .num_tls_server_zero_rtt_packets_received_after_discarding_decrypter > |
| 0) { |
| QUIC_CODE_COUNT_N( |
| quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 2, |
| 3); |
| } else { |
| QUIC_CODE_COUNT_N( |
| quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 3, |
| 3); |
| } |
| } |
| |
| void QuicConnection::ClearQueuedPackets() { buffered_packets_.clear(); } |
| |
| bool QuicConnection::ValidateConfigConnectionIds(const QuicConfig& config) { |
| QUICHE_DCHECK(config.negotiated()); |
| if (!version().UsesTls()) { |
| // QUIC+TLS is required to transmit connection ID transport parameters. |
| return true; |
| } |
| // This function validates connection IDs as defined in IETF draft-28 and |
| // later. |
| |
| // Validate initial_source_connection_id. |
| QuicConnectionId expected_initial_source_connection_id; |
| if (perspective_ == Perspective::IS_CLIENT) { |
| expected_initial_source_connection_id = default_path_.server_connection_id; |
| } else { |
| expected_initial_source_connection_id = default_path_.client_connection_id; |
| } |
| if (!config.HasReceivedInitialSourceConnectionId() || |
| config.ReceivedInitialSourceConnectionId() != |
| expected_initial_source_connection_id) { |
| std::string received_value; |
| if (config.HasReceivedInitialSourceConnectionId()) { |
| received_value = config.ReceivedInitialSourceConnectionId().ToString(); |
| } else { |
| received_value = "none"; |
| } |
| std::string error_details = |
| absl::StrCat("Bad initial_source_connection_id: expected ", |
| expected_initial_source_connection_id.ToString(), |
| ", received ", received_value); |
| CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| if (perspective_ == Perspective::IS_CLIENT) { |
| // Validate original_destination_connection_id. |
| if (!config.HasReceivedOriginalConnectionId() || |
| config.ReceivedOriginalConnectionId() != |
| GetOriginalDestinationConnectionId()) { |
| std::string received_value; |
| if (config.HasReceivedOriginalConnectionId()) { |
| received_value = config.ReceivedOriginalConnectionId().ToString(); |
| } else { |
| received_value = "none"; |
| } |
| std::string error_details = |
| absl::StrCat("Bad original_destination_connection_id: expected ", |
| GetOriginalDestinationConnectionId().ToString(), |
| ", received ", received_value); |
| CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| // Validate retry_source_connection_id. |
| if (retry_source_connection_id_.has_value()) { |
| // We received a RETRY packet, validate that the retry source |
| // connection ID from the config matches the one from the RETRY. |
| if (!config.HasReceivedRetrySourceConnectionId() || |
| config.ReceivedRetrySourceConnectionId() != |
| retry_source_connection_id_.value()) { |
| std::string received_value; |
| if (config.HasReceivedRetrySourceConnectionId()) { |
| received_value = config.ReceivedRetrySourceConnectionId().ToString(); |
| } else { |
| received_value = "none"; |
| } |
| std::string error_details = |
| absl::StrCat("Bad retry_source_connection_id: expected ", |
| retry_source_connection_id_.value().ToString(), |
| ", received ", received_value); |
| CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| } else { |
| // We did not receive a RETRY packet, make sure we did not receive the |
| // retry_source_connection_id transport parameter. |
| if (config.HasReceivedRetrySourceConnectionId()) { |
| std::string error_details = absl::StrCat( |
| "Bad retry_source_connection_id: did not receive RETRY but " |
| "received ", |
| config.ReceivedRetrySourceConnectionId().ToString()); |
| CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| void QuicConnection::SetFromConfig(const QuicConfig& config) { |
| if (config.negotiated()) { |
| // Handshake complete, set handshake timeout to Infinite. |
| SetNetworkTimeouts(QuicTime::Delta::Infinite(), |
| config.IdleNetworkTimeout()); |
| idle_timeout_connection_close_behavior_ = |
| ConnectionCloseBehavior::SILENT_CLOSE; |
| if (perspective_ == Perspective::IS_SERVER) { |
| idle_timeout_connection_close_behavior_ = ConnectionCloseBehavior:: |
| SILENT_CLOSE_WITH_CONNECTION_CLOSE_PACKET_SERIALIZED; |
| } |
| if (config.HasClientRequestedIndependentOption(kNSLC, perspective_)) { |
| idle_timeout_connection_close_behavior_ = |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET; |
| } |
| if (!ValidateConfigConnectionIds(config)) { |
| return; |
| } |
| support_key_update_for_connection_ = version().UsesTls(); |
| framer_.SetKeyUpdateSupportForConnection( |
| support_key_update_for_connection_); |
| } else { |
| SetNetworkTimeouts(config.max_time_before_crypto_handshake(), |
| config.max_idle_time_before_crypto_handshake()); |
| } |
| |
| if (version().HasIetfQuicFrames() && |
| config.HasReceivedPreferredAddressConnectionIdAndToken()) { |
| QuicNewConnectionIdFrame frame; |
| std::tie(frame.connection_id, frame.stateless_reset_token) = |
| config.ReceivedPreferredAddressConnectionIdAndToken(); |
| frame.sequence_number = 1u; |
| frame.retire_prior_to = 0u; |
| OnNewConnectionIdFrameInner(frame); |
| } |
| |
| sent_packet_manager_.SetFromConfig(config); |
| if (perspective_ == Perspective::IS_SERVER && |
| config.HasClientSentConnectionOption(kAFF2, perspective_)) { |
| send_ack_frequency_on_handshake_completion_ = true; |
| } |
| if (config.HasReceivedBytesForConnectionId() && |
| can_truncate_connection_ids_) { |
| packet_creator_.SetServerConnectionIdLength( |
| config.ReceivedBytesForConnectionId()); |
| } |
| max_undecryptable_packets_ = config.max_undecryptable_packets(); |
| |
| if (!GetQuicReloadableFlag(quic_enable_mtu_discovery_at_server)) { |
| if (config.HasClientRequestedIndependentOption(kMTUH, perspective_)) { |
| SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh); |
| } |
| } |
| if (config.HasClientRequestedIndependentOption(kMTUL, perspective_)) { |
| SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeLow); |
| } |
| if (default_enable_5rto_blackhole_detection_) { |
| if (config.HasClientRequestedIndependentOption(kCBHD, perspective_)) { |
| QUIC_CODE_COUNT(quic_client_only_blackhole_detection); |
| blackhole_detection_disabled_ = true; |
| } |
| if (config.HasClientSentConnectionOption(kNBHD, perspective_)) { |
| blackhole_detection_disabled_ = true; |
| } |
| } |
| |
| if (config.HasClientRequestedIndependentOption(kFIDT, perspective_)) { |
| idle_network_detector_.enable_shorter_idle_timeout_on_sent_packet(); |
| } |
| if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames()) { |
| // Only conduct those experiments in IETF QUIC because random packets may |
| // elicit reset and gQUIC PUBLIC_RESET will cause connection close. |
| if (config.HasClientRequestedIndependentOption(kROWF, perspective_)) { |
| retransmittable_on_wire_behavior_ = SEND_FIRST_FORWARD_SECURE_PACKET; |
| } |
| if (config.HasClientRequestedIndependentOption(kROWR, perspective_)) { |
| retransmittable_on_wire_behavior_ = SEND_RANDOM_BYTES; |
| } |
| } |
| if (config.HasClientRequestedIndependentOption(k3AFF, perspective_)) { |
| anti_amplification_factor_ = 3; |
| } |
| if (config.HasClientRequestedIndependentOption(k10AF, perspective_)) { |
| anti_amplification_factor_ = 10; |
| } |
| |
| if (GetQuicReloadableFlag(quic_enable_server_on_wire_ping) && |
| perspective_ == Perspective::IS_SERVER && |
| config.HasClientSentConnectionOption(kSRWP, perspective_)) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_enable_server_on_wire_ping); |
| set_initial_retransmittable_on_wire_timeout( |
| QuicTime::Delta::FromMilliseconds(200)); |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnSetFromConfig(config); |
| } |
| uber_received_packet_manager_.SetFromConfig(config, perspective_); |
| if (config.HasClientSentConnectionOption(k5RTO, perspective_)) { |
| num_rtos_for_blackhole_detection_ = 5; |
| } |
| if (config.HasClientSentConnectionOption(k6PTO, perspective_) || |
| config.HasClientSentConnectionOption(k7PTO, perspective_) || |
| config.HasClientSentConnectionOption(k8PTO, perspective_)) { |
| num_rtos_for_blackhole_detection_ = 5; |
| } |
| if (config.HasClientSentConnectionOption(kNSTP, perspective_)) { |
| no_stop_waiting_frames_ = true; |
| } |
| if (config.HasReceivedStatelessResetToken()) { |
| default_path_.stateless_reset_token = config.ReceivedStatelessResetToken(); |
| } |
| if (config.HasReceivedAckDelayExponent()) { |
| framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent()); |
| } |
| if (config.HasClientSentConnectionOption(kEACK, perspective_)) { |
| bundle_retransmittable_with_pto_ack_ = true; |
| } |
| if (config.HasClientSentConnectionOption(kDFER, perspective_)) { |
| defer_send_in_response_to_packets_ = false; |
| } |
| |
| if (config.HasClientRequestedIndependentOption(kINVC, perspective_)) { |
| send_connection_close_for_invalid_version_ = true; |
| } |
| const bool remove_connection_migration_connection_option = |
| GetQuicReloadableFlag( |
| quic_remove_connection_migration_connection_option_v2); |
| if (remove_connection_migration_connection_option) { |
| QUIC_RELOADABLE_FLAG_COUNT( |
| quic_remove_connection_migration_connection_option_v2); |
| } |
| if (framer_.version().HasIetfQuicFrames() && |
| (remove_connection_migration_connection_option || |
| config.HasClientSentConnectionOption(kRVCM, perspective_))) { |
| validate_client_addresses_ = true; |
| } |
| // Having connection_migration_use_new_cid_ depends on the same set of flags |
| // and connection option on both client and server sides has the advantage of: |
| // 1) Less chance of skew in using new connection ID or not between client |
| // and server in unit tests with random flag combinations. |
| // 2) Client side's rollout can be protected by the same connection option. |
| connection_migration_use_new_cid_ = |
| validate_client_addresses_ && |
| GetQuicReloadableFlag(quic_connection_migration_use_new_cid_v2); |
| if (config.HasReceivedMaxPacketSize()) { |
| peer_max_packet_size_ = config.ReceivedMaxPacketSize(); |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| } |
| if (config.HasReceivedMaxDatagramFrameSize()) { |
| packet_creator_.SetMaxDatagramFrameSize( |
| config.ReceivedMaxDatagramFrameSize()); |
| } |
| |
| supports_release_time_ = |
| writer_ != nullptr && writer_->SupportsReleaseTime() && |
| !config.HasClientSentConnectionOption(kNPCO, perspective_); |
| |
| if (supports_release_time_) { |
| UpdateReleaseTimeIntoFuture(); |
| } |
| |
| multi_port_enabled_ = |
| connection_migration_use_new_cid_ && |
| config.HasClientSentConnectionOption(kMPQC, perspective_); |
| if (multi_port_enabled_) { |
| multi_port_stats_ = std::make_unique<MultiPortStats>(); |
| } |
| } |
| |
| void QuicConnection::EnableLegacyVersionEncapsulation( |
| const std::string& server_name) { |
| if (perspective_ != Perspective::IS_CLIENT) { |
| QUIC_BUG(quic_bug_10511_1) |
| << "Cannot enable Legacy Version Encapsulation on the server"; |
| return; |
| } |
| if (legacy_version_encapsulation_enabled_) { |
| QUIC_BUG(quic_bug_10511_2) |
| << "Do not call EnableLegacyVersionEncapsulation twice"; |
| return; |
| } |
| if (!QuicHostnameUtils::IsValidSNI(server_name)) { |
| // Legacy Version Encapsulation is only used when SNI is transmitted. |
| QUIC_DLOG(INFO) |
| << "Refusing to use Legacy Version Encapsulation with invalid SNI \"" |
| << server_name << "\""; |
| return; |
| } |
| QUIC_DLOG(INFO) << "Enabling Legacy Version Encapsulation with SNI \"" |
| << server_name << "\""; |
| legacy_version_encapsulation_enabled_ = true; |
| legacy_version_encapsulation_sni_ = server_name; |
| } |
| |
| bool QuicConnection::MaybeTestLiveness() { |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| if (encryption_level_ != ENCRYPTION_FORWARD_SECURE) { |
| return false; |
| } |
| const QuicTime idle_network_deadline = |
| idle_network_detector_.GetIdleNetworkDeadline(); |
| if (!idle_network_deadline.IsInitialized()) { |
| return false; |
| } |
| const QuicTime now = clock_->ApproximateNow(); |
| if (now > idle_network_deadline) { |
| QUIC_DLOG(WARNING) << "Idle network deadline has passed"; |
| return false; |
| } |
| const QuicTime::Delta timeout = idle_network_deadline - now; |
| if (2 * timeout > idle_network_detector_.idle_network_timeout()) { |
| // Do not test liveness if timeout is > half timeout. This is used to |
| // prevent an infinite loop for short idle timeout. |
| return false; |
| } |
| if (!sent_packet_manager_.IsLessThanThreePTOs(timeout)) { |
| return false; |
| } |
| SendConnectivityProbingPacket(writer_, peer_address()); |
| return true; |
| } |
| |
| void QuicConnection::ApplyConnectionOptions( |
| const QuicTagVector& connection_options) { |
| sent_packet_manager_.ApplyConnectionOptions(connection_options); |
| } |
| |
| void QuicConnection::OnSendConnectionState( |
| const CachedNetworkParameters& cached_network_params) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnSendConnectionState(cached_network_params); |
| } |
| } |
| |
| void QuicConnection::OnReceiveConnectionState( |
| const CachedNetworkParameters& cached_network_params) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnReceiveConnectionState(cached_network_params); |
| } |
| } |
| |
| void QuicConnection::ResumeConnectionState( |
| const CachedNetworkParameters& cached_network_params, |
| bool max_bandwidth_resumption) { |
| sent_packet_manager_.ResumeConnectionState(cached_network_params, |
| max_bandwidth_resumption); |
| } |
| |
| void QuicConnection::SetMaxPacingRate(QuicBandwidth max_pacing_rate) { |
| sent_packet_manager_.SetMaxPacingRate(max_pacing_rate); |
| } |
| |
| void QuicConnection::AdjustNetworkParameters( |
| const SendAlgorithmInterface::NetworkParams& params) { |
| sent_packet_manager_.AdjustNetworkParameters(params); |
| } |
| |
| void QuicConnection::SetLossDetectionTuner( |
| std::unique_ptr<LossDetectionTunerInterface> tuner) { |
| sent_packet_manager_.SetLossDetectionTuner(std::move(tuner)); |
| } |
| |
| void QuicConnection::OnConfigNegotiated() { |
| sent_packet_manager_.OnConfigNegotiated(); |
| |
| if (GetQuicReloadableFlag(quic_enable_mtu_discovery_at_server) && |
| perspective_ == Perspective::IS_SERVER) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_enable_mtu_discovery_at_server); |
| SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh); |
| } |
| } |
| |
| QuicBandwidth QuicConnection::MaxPacingRate() const { |
| return sent_packet_manager_.MaxPacingRate(); |
| } |
| |
| bool QuicConnection::SelectMutualVersion( |
| const ParsedQuicVersionVector& available_versions) { |
| // Try to find the highest mutual version by iterating over supported |
| // versions, starting with the highest, and breaking out of the loop once we |
| // find a matching version in the provided available_versions vector. |
| const ParsedQuicVersionVector& supported_versions = |
| framer_.supported_versions(); |
| for (size_t i = 0; i < supported_versions.size(); ++i) { |
| const ParsedQuicVersion& version = supported_versions[i]; |
| if (std::find(available_versions.begin(), available_versions.end(), |
| version) != available_versions.end()) { |
| framer_.set_version(version); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void QuicConnection::OnError(QuicFramer* framer) { |
| // Packets that we can not or have not decrypted are dropped. |
| // TODO(rch): add stats to measure this. |
| if (!connected_ || !last_received_packet_info_.decrypted) { |
| return; |
| } |
| CloseConnection(framer->error(), framer->detailed_error(), |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| } |
| |
| void QuicConnection::OnPacket() { |
| last_received_packet_info_.decrypted = false; |
| } |
| |
| void QuicConnection::OnPublicResetPacket(const QuicPublicResetPacket& packet) { |
| // Check that any public reset packet with a different connection ID that was |
| // routed to this QuicConnection has been redirected before control reaches |
| // here. (Check for a bug regression.) |
| QUICHE_DCHECK_EQ(default_path_.server_connection_id, packet.connection_id); |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| QUICHE_DCHECK(!version().HasIetfInvariantHeader()); |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPublicResetPacket(packet); |
| } |
| std::string error_details = "Received public reset."; |
| if (perspective_ == Perspective::IS_CLIENT && !packet.endpoint_id.empty()) { |
| absl::StrAppend(&error_details, " From ", packet.endpoint_id, "."); |
| } |
| QUIC_DLOG(INFO) << ENDPOINT << error_details; |
| QUIC_CODE_COUNT(quic_tear_down_local_connection_on_public_reset); |
| TearDownLocalConnectionState(QUIC_PUBLIC_RESET, NO_IETF_QUIC_ERROR, |
| error_details, ConnectionCloseSource::FROM_PEER); |
| } |
| |
| bool QuicConnection::OnProtocolVersionMismatch( |
| ParsedQuicVersion received_version) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Received packet with mismatched version " |
| << ParsedQuicVersionToString(received_version); |
| if (perspective_ == Perspective::IS_CLIENT) { |
| const std::string error_details = "Protocol version mismatch."; |
| QUIC_BUG(quic_bug_10511_3) << ENDPOINT << error_details; |
| CloseConnection(QUIC_INTERNAL_ERROR, error_details, |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| } |
| |
| // Server drops old packets that were sent by the client before the version |
| // was negotiated. |
| return false; |
| } |
| |
| // Handles version negotiation for client connection. |
| void QuicConnection::OnVersionNegotiationPacket( |
| const QuicVersionNegotiationPacket& packet) { |
| // Check that any public reset packet with a different connection ID that was |
| // routed to this QuicConnection has been redirected before control reaches |
| // here. (Check for a bug regression.) |
| QUICHE_DCHECK_EQ(default_path_.server_connection_id, packet.connection_id); |
| if (perspective_ == Perspective::IS_SERVER) { |
| const std::string error_details = |
| "Server received version negotiation packet."; |
| QUIC_BUG(quic_bug_10511_4) << error_details; |
| QUIC_CODE_COUNT(quic_tear_down_local_connection_on_version_negotiation); |
| CloseConnection(QUIC_INTERNAL_ERROR, error_details, |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| return; |
| } |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnVersionNegotiationPacket(packet); |
| } |
| |
| if (version_negotiated_) { |
| // Possibly a duplicate version negotiation packet. |
| return; |
| } |
| |
| if (std::find(packet.versions.begin(), packet.versions.end(), version()) != |
| packet.versions.end()) { |
| const std::string error_details = absl::StrCat( |
| "Server already supports client's version ", |
| ParsedQuicVersionToString(version()), |
| " and should have accepted the connection instead of sending {", |
| ParsedQuicVersionVectorToString(packet.versions), "}."); |
| QUIC_DLOG(WARNING) << error_details; |
| CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, error_details, |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| return; |
| } |
| |
| server_supported_versions_ = packet.versions; |
| CloseConnection( |
| QUIC_INVALID_VERSION, |
| absl::StrCat( |
| "Client may support one of the versions in the server's list, but " |
| "it's going to close the connection anyway. Supported versions: {", |
| ParsedQuicVersionVectorToString(framer_.supported_versions()), |
| "}, peer supported versions: {", |
| ParsedQuicVersionVectorToString(packet.versions), "}"), |
| send_connection_close_for_invalid_version_ |
| ? ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET |
| : ConnectionCloseBehavior::SILENT_CLOSE); |
| } |
| |
| // Handles retry for client connection. |
| void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id, |
| QuicConnectionId new_connection_id, |
| absl::string_view retry_token, |
| absl::string_view retry_integrity_tag, |
| absl::string_view retry_without_tag) { |
| QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_); |
| if (version().UsesTls()) { |
| if (!CryptoUtils::ValidateRetryIntegrityTag( |
| version(), default_path_.server_connection_id, retry_without_tag, |
| retry_integrity_tag)) { |
| QUIC_DLOG(ERROR) << "Ignoring RETRY with invalid integrity tag"; |
| return; |
| } |
| } else { |
| if (original_connection_id != default_path_.server_connection_id) { |
| QUIC_DLOG(ERROR) << "Ignoring RETRY with original connection ID " |
| << original_connection_id << " not matching expected " |
| << default_path_.server_connection_id << " token " |
| << absl::BytesToHexString(retry_token); |
| return; |
| } |
| } |
| framer_.set_drop_incoming_retry_packets(true); |
| stats_.retry_packet_processed = true; |
| QUIC_DLOG(INFO) << "Received RETRY, replacing connection ID " |
| << default_path_.server_connection_id << " with " |
| << new_connection_id << ", received token " |
| << absl::BytesToHexString(retry_token); |
| if (!original_destination_connection_id_.has_value()) { |
| original_destination_connection_id_ = default_path_.server_connection_id; |
| } |
| QUICHE_DCHECK(!retry_source_connection_id_.has_value()) |
| << retry_source_connection_id_.value(); |
| retry_source_connection_id_ = new_connection_id; |
| ReplaceInitialServerConnectionId(new_connection_id); |
| packet_creator_.SetRetryToken(retry_token); |
| |
| // Reinstall initial crypters because the connection ID changed. |
| InstallInitialCrypters(default_path_.server_connection_id); |
| |
| sent_packet_manager_.MarkInitialPacketsForRetransmission(); |
| } |
| |
| void QuicConnection::SetOriginalDestinationConnectionId( |
| const QuicConnectionId& original_destination_connection_id) { |
| QUIC_DLOG(INFO) << "Setting original_destination_connection_id to " |
| << original_destination_connection_id |
| << " on connection with server_connection_id " |
| << default_path_.server_connection_id; |
| QUICHE_DCHECK_NE(original_destination_connection_id, |
| default_path_.server_connection_id); |
| InstallInitialCrypters(original_destination_connection_id); |
| QUICHE_DCHECK(!original_destination_connection_id_.has_value()) |
| << original_destination_connection_id_.value(); |
| original_destination_connection_id_ = original_destination_connection_id; |
| original_destination_connection_id_replacement_ = |
| default_path_.server_connection_id; |
| } |
| |
| QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() const { |
| if (original_destination_connection_id_.has_value()) { |
| return original_destination_connection_id_.value(); |
| } |
| return default_path_.server_connection_id; |
| } |
| |
| void QuicConnection::RetireOriginalDestinationConnectionId() { |
| if (original_destination_connection_id_.has_value()) { |
| visitor_->OnServerConnectionIdRetired(*original_destination_connection_id_); |
| original_destination_connection_id_.reset(); |
| } |
| } |
| |
| bool QuicConnection::ValidateServerConnectionId( |
| const QuicPacketHeader& header) const { |
| if (perspective_ == Perspective::IS_CLIENT && |
| header.form == IETF_QUIC_SHORT_HEADER_PACKET) { |
| return true; |
| } |
| |
| QuicConnectionId server_connection_id = |
| GetServerConnectionIdAsRecipient(header, perspective_); |
| |
| if (server_connection_id == default_path_.server_connection_id || |
| server_connection_id == original_destination_connection_id_) { |
| return true; |
| } |
| |
| if (PacketCanReplaceServerConnectionId(header, perspective_)) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID " |
| << server_connection_id << " instead of " |
| << default_path_.server_connection_id; |
| return true; |
| } |
| |
| if (connection_migration_use_new_cid_ && |
| perspective_ == Perspective::IS_SERVER && |
| self_issued_cid_manager_ != nullptr && |
| self_issued_cid_manager_->IsConnectionIdInUse(server_connection_id)) { |
| return true; |
| } |
| |
| if (NewServerConnectionIdMightBeValid( |
| header, perspective_, server_connection_id_replaced_by_initial_)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool QuicConnection::OnUnauthenticatedPublicHeader( |
| const QuicPacketHeader& header) { |
| last_received_packet_info_.destination_connection_id = |
| header.destination_connection_id; |
| // If last packet destination connection ID is the original server |
| // connection ID chosen by client, replaces it with the connection ID chosen |
| // by server. |
| if (perspective_ == Perspective::IS_SERVER && |
| original_destination_connection_id_.has_value() && |
| last_received_packet_info_.destination_connection_id == |
| *original_destination_connection_id_) { |
| last_received_packet_info_.destination_connection_id = |
| original_destination_connection_id_replacement_; |
| } |
| |
| // As soon as we receive an initial we start ignoring subsequent retries. |
| if (header.version_flag && header.long_packet_type == INITIAL) { |
| framer_.set_drop_incoming_retry_packets(true); |
| } |
| |
| if (!ValidateServerConnectionId(header)) { |
| ++stats_.packets_dropped; |
| QuicConnectionId server_connection_id = |
| GetServerConnectionIdAsRecipient(header, perspective_); |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Ignoring packet from unexpected server connection ID " |
| << server_connection_id << " instead of " |
| << default_path_.server_connection_id; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnIncorrectConnectionId(server_connection_id); |
| } |
| QUICHE_DCHECK_NE(Perspective::IS_SERVER, perspective_); |
| return false; |
| } |
| |
| if (!version().SupportsClientConnectionIds()) { |
| return true; |
| } |
| |
| if (perspective_ == Perspective::IS_SERVER && |
| header.form == IETF_QUIC_SHORT_HEADER_PACKET) { |
| return true; |
| } |
| |
| QuicConnectionId client_connection_id = |
| GetClientConnectionIdAsRecipient(header, perspective_); |
| |
| if (client_connection_id == default_path_.client_connection_id) { |
| return true; |
| } |
| |
| if (!client_connection_id_is_set_ && perspective_ == Perspective::IS_SERVER) { |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Setting client connection ID from first packet to " |
| << client_connection_id; |
| set_client_connection_id(client_connection_id); |
| return true; |
| } |
| |
| if (connection_migration_use_new_cid_ && |
| perspective_ == Perspective::IS_CLIENT && |
| self_issued_cid_manager_ != nullptr && |
| self_issued_cid_manager_->IsConnectionIdInUse(client_connection_id)) { |
| return true; |
| } |
| |
| ++stats_.packets_dropped; |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Ignoring packet from unexpected client connection ID " |
| << client_connection_id << " instead of " |
| << default_path_.client_connection_id; |
| return false; |
| } |
| |
| bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnUnauthenticatedHeader(header); |
| } |
| |
| // Sanity check on the server connection ID in header. |
| QUICHE_DCHECK(ValidateServerConnectionId(header)); |
| |
| if (packet_creator_.HasPendingFrames()) { |
| // Incoming packets may change a queued ACK frame. |
| const std::string error_details = |
| "Pending frames must be serialized before incoming packets are " |
| "processed."; |
| QUIC_BUG(quic_pending_frames_not_serialized) |
| << error_details << ", received header: " << header; |
| CloseConnection(QUIC_INTERNAL_ERROR, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void QuicConnection::OnSuccessfulVersionNegotiation() { |
| visitor_->OnSuccessfulVersionNegotiation(version()); |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnSuccessfulVersionNegotiation(version()); |
| } |
| } |
| |
| void QuicConnection::OnSuccessfulMigration(bool is_port_change) { |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| if (IsPathDegrading()) { |
| // If path was previously degrading, and migration is successful after |
| // probing, restart the path degrading and blackhole detection. |
| OnForwardProgressMade(); |
| } |
| if (IsAlternativePath(default_path_.self_address, |
| default_path_.peer_address)) { |
| // Reset alternative path state even if it is still under validation. |
| alternative_path_.Clear(); |
| } |
| // TODO(b/159074035): notify SentPacketManger with RTT sample from probing. |
| if (version().HasIetfQuicFrames() && !is_port_change) { |
| sent_packet_manager_.OnConnectionMigration(/*reset_send_algorithm=*/true); |
| } |
| } |
| |
| void QuicConnection::OnTransportParametersSent( |
| const TransportParameters& transport_parameters) const { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnTransportParametersSent(transport_parameters); |
| } |
| } |
| |
| void QuicConnection::OnTransportParametersReceived( |
| const TransportParameters& transport_parameters) const { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnTransportParametersReceived(transport_parameters); |
| } |
| } |
| |
| void QuicConnection::OnTransportParametersResumed( |
| const TransportParameters& transport_parameters) const { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnTransportParametersResumed(transport_parameters); |
| } |
| } |
| |
| bool QuicConnection::HasPendingAcks() const { return ack_alarm_->IsSet(); } |
| |
| void QuicConnection::OnUserAgentIdKnown(const std::string& /*user_agent_id*/) { |
| sent_packet_manager_.OnUserAgentIdKnown(); |
| } |
| |
| void QuicConnection::OnDecryptedPacket(size_t /*length*/, |
| EncryptionLevel level) { |
| last_received_packet_info_.decrypted_level = level; |
| last_received_packet_info_.decrypted = true; |
| if (level == ENCRYPTION_FORWARD_SECURE && |
| !have_decrypted_first_one_rtt_packet_) { |
| have_decrypted_first_one_rtt_packet_ = true; |
| if (version().UsesTls() && perspective_ == Perspective::IS_SERVER) { |
| // Servers MAY temporarily retain 0-RTT keys to allow decrypting reordered |
| // packets without requiring their contents to be retransmitted with 1-RTT |
| // keys. After receiving a 1-RTT packet, servers MUST discard 0-RTT keys |
| // within a short time; the RECOMMENDED time period is three times the |
| // Probe Timeout. |
| // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-discarding-0-rtt-keys |
| discard_zero_rtt_decryption_keys_alarm_->Set( |
| clock_->ApproximateNow() + sent_packet_manager_.GetPtoDelay() * 3); |
| } |
| } |
| if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() && |
| (level == ENCRYPTION_HANDSHAKE || level == ENCRYPTION_FORWARD_SECURE)) { |
| // Address is validated by successfully processing a HANDSHAKE or 1-RTT |
| // packet. |
| default_path_.validated = true; |
| stats_.address_validated_via_decrypting_packet = true; |
| } |
| idle_network_detector_.OnPacketReceived( |
| last_received_packet_info_.receipt_time); |
| |
| visitor_->OnPacketDecrypted(level); |
| } |
| |
| QuicSocketAddress QuicConnection::GetEffectivePeerAddressFromCurrentPacket() |
| const { |
| // By default, the connection is not proxied, and the effective peer address |
| // is the packet's source address, i.e. the direct peer address. |
| return last_received_packet_info_.source_address; |
| } |
| |
| bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPacketHeader(header, clock_->ApproximateNow(), |
| last_received_packet_info_.decrypted_level); |
| } |
| |
| // Will be decremented below if we fall through to return true. |
| ++stats_.packets_dropped; |
| |
| if (!ProcessValidatedPacket(header)) { |
| return false; |
| } |
| |
| // Initialize the current packet content state. |
| current_packet_content_ = NO_FRAMES_RECEIVED; |
| is_current_packet_connectivity_probing_ = false; |
| has_path_challenge_in_current_packet_ = false; |
| current_effective_peer_migration_type_ = NO_CHANGE; |
| |
| if (perspective_ == Perspective::IS_CLIENT) { |
| if (!GetLargestReceivedPacket().IsInitialized() || |
| header.packet_number > GetLargestReceivedPacket()) { |
| // Update direct_peer_address_ and default path peer_address immediately |
| // for client connections. |
| // TODO(fayang): only change peer addresses in application data packet |
| // number space. |
| UpdatePeerAddress(last_received_packet_info_.source_address); |
| default_path_.peer_address = GetEffectivePeerAddressFromCurrentPacket(); |
| } |
| } else { |
| // At server, remember the address change type of effective_peer_address |
| // in current_effective_peer_migration_type_. But this variable alone |
| // doesn't necessarily starts a migration. A migration will be started |
| // later, once the current packet is confirmed to meet the following |
| // conditions: |
| // 1) current_effective_peer_migration_type_ is not NO_CHANGE. |
| // 2) The current packet is not a connectivity probing. |
| // 3) The current packet is not reordered, i.e. its packet number is the |
| // largest of this connection so far. |
| // Once the above conditions are confirmed, a new migration will start |
| // even if there is an active migration underway. |
| current_effective_peer_migration_type_ = |
| QuicUtils::DetermineAddressChangeType( |
| default_path_.peer_address, |
| GetEffectivePeerAddressFromCurrentPacket()); |
| |
| if (connection_migration_use_new_cid_) { |
| auto effective_peer_address = GetEffectivePeerAddressFromCurrentPacket(); |
| // Since server does not send new connection ID to client before handshake |
| // completion and source connection ID is omitted in short header packet, |
| // the server_connection_id on PathState on the server side does not |
| // affect the packets server writes after handshake completion. On the |
| // other hand, it is still desirable to have the "correct" server |
| // connection ID set on path. |
| // 1) If client uses 1 unique server connection ID per path and the packet |
| // is received from an existing path, then |
| // last_received_packet_info_.destination_connection_id will always be the |
| // same as the server connection ID on path. Server side will maintain the |
| // 1-to-1 mapping from server connection ID to path. 2) If client uses |
| // multiple server connection IDs on the same path, compared to the |
| // server_connection_id on path, |
| // last_received_packet_info_.destination_connection_id has the advantage |
| // that it is still present in the session map since the packet can be |
| // routed here regardless of packet reordering. |
| if (IsDefaultPath(last_received_packet_info_.destination_address, |
| effective_peer_address)) { |
| default_path_.server_connection_id = |
| last_received_packet_info_.destination_connection_id; |
| } else if (IsAlternativePath( |
| last_received_packet_info_.destination_address, |
| effective_peer_address)) { |
| alternative_path_.server_connection_id = |
| last_received_packet_info_.destination_connection_id; |
| } |
| } |
| |
| if (last_received_packet_info_.destination_connection_id != |
| default_path_.server_connection_id && |
| (!original_destination_connection_id_.has_value() || |
| last_received_packet_info_.destination_connection_id != |
| *original_destination_connection_id_)) { |
| QUIC_CODE_COUNT(quic_connection_id_change); |
| } |
| |
| QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE) |
| << ENDPOINT << "Effective peer's ip:port changed from " |
| << default_path_.peer_address.ToString() << " to " |
| << GetEffectivePeerAddressFromCurrentPacket().ToString() |
| << ", active_effective_peer_migration_type is " |
| << active_effective_peer_migration_type_; |
| } |
| |
| --stats_.packets_dropped; |
| QUIC_DVLOG(1) << ENDPOINT << "Received packet header: " << header; |
| last_received_packet_info_.header = header; |
| if (!stats_.first_decrypted_packet.IsInitialized()) { |
| stats_.first_decrypted_packet = |
| last_received_packet_info_.header.packet_number; |
| } |
| |
| // Record packet receipt to populate ack info before processing stream |
| // frames, since the processing may result in sending a bundled ack. |
| QuicTime receipt_time = idle_network_detector_.time_of_last_received_packet(); |
| if (SupportsMultiplePacketNumberSpaces()) { |
| receipt_time = last_received_packet_info_.receipt_time; |
| } |
| uber_received_packet_manager_.RecordPacketReceived( |
| last_received_packet_info_.decrypted_level, |
| last_received_packet_info_.header, receipt_time); |
| if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() && |
| !header.retry_token.empty() && |
| visitor_->ValidateToken(header.retry_token)) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Address validated via token."; |
| QUIC_CODE_COUNT(quic_address_validated_via_token); |
| default_path_.validated = true; |
| stats_.address_validated_via_token = true; |
| } |
| QUICHE_DCHECK(connected_); |
| return true; |
| } |
| |
| bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_3, !connected_) |
| << "Processing STREAM frame when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| |
| // Since a stream frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(STREAM_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnStreamFrame(frame); |
| } |
| if (!QuicUtils::IsCryptoStreamId(transport_version(), frame.stream_id) && |
| last_received_packet_info_.decrypted_level == ENCRYPTION_INITIAL) { |
| if (MaybeConsiderAsMemoryCorruption(frame)) { |
| CloseConnection(QUIC_MAYBE_CORRUPTED_MEMORY, |
| "Received crypto frame on non crypto stream.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| |
| QUIC_PEER_BUG(quic_peer_bug_10511_6) |
| << ENDPOINT << "Received an unencrypted data frame: closing connection" |
| << " packet_number:" << last_received_packet_info_.header.packet_number |
| << " stream_id:" << frame.stream_id |
| << " received_packets:" << ack_frame(); |
| CloseConnection(QUIC_UNENCRYPTED_STREAM_DATA, |
| "Unencrypted stream data seen.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| // TODO(fayang): Consider moving UpdatePacketContent and |
| // MaybeUpdateAckTimeout to a stand-alone function instead of calling them for |
| // all frames. |
| MaybeUpdateAckTimeout(); |
| visitor_->OnStreamFrame(frame); |
| stats_.stream_bytes_received += frame.data_length; |
| if (use_ping_manager_) { |
| ping_manager_.reset_consecutive_retransmittable_on_wire_count(); |
| } else { |
| consecutive_retransmittable_on_wire_ping_count_ = 0; |
| } |
| return connected_; |
| } |
| |
| bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_4, !connected_) |
| << "Processing CRYPTO frame when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| |
| // Since a CRYPTO frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(CRYPTO_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnCryptoFrame(frame); |
| } |
| MaybeUpdateAckTimeout(); |
| visitor_->OnCryptoFrame(frame); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked, |
| QuicTime::Delta ack_delay_time) { |
| QUIC_BUG_IF(quic_bug_12714_5, !connected_) |
| << "Processing ACK frame start when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| |
| if (processing_ack_frame_) { |
| CloseConnection(QUIC_INVALID_ACK_DATA, |
| "Received a new ack while processing an ack frame.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| |
| // Since an ack frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(ACK_FRAME)) { |
| return false; |
| } |
| |
| QUIC_DVLOG(1) << ENDPOINT |
| << "OnAckFrameStart, largest_acked: " << largest_acked; |
| |
| if (GetLargestReceivedPacketWithAck().IsInitialized() && |
| last_received_packet_info_.header.packet_number <= |
| GetLargestReceivedPacketWithAck()) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; |
| return true; |
| } |
| |
| if (!sent_packet_manager_.GetLargestSentPacket().IsInitialized() || |
| largest_acked > sent_packet_manager_.GetLargestSentPacket()) { |
| QUIC_DLOG(WARNING) << ENDPOINT |
| << "Peer's observed unsent packet:" << largest_acked |
| << " vs " << sent_packet_manager_.GetLargestSentPacket() |
| << ". SupportsMultiplePacketNumberSpaces():" |
| << SupportsMultiplePacketNumberSpaces() |
| << ", last_received_packet_info_.decrypted_level:" |
| << last_received_packet_info_.decrypted_level; |
| // We got an ack for data we have not sent. |
| CloseConnection(QUIC_INVALID_ACK_DATA, "Largest observed too high.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| processing_ack_frame_ = true; |
| sent_packet_manager_.OnAckFrameStart( |
| largest_acked, ack_delay_time, |
| idle_network_detector_.time_of_last_received_packet()); |
| return true; |
| } |
| |
| bool QuicConnection::OnAckRange(QuicPacketNumber start, QuicPacketNumber end) { |
| QUIC_BUG_IF(quic_bug_12714_6, !connected_) |
| << "Processing ACK frame range when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")"; |
| |
| if (GetLargestReceivedPacketWithAck().IsInitialized() && |
| last_received_packet_info_.header.packet_number <= |
| GetLargestReceivedPacketWithAck()) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; |
| return true; |
| } |
| |
| sent_packet_manager_.OnAckRange(start, end); |
| return true; |
| } |
| |
| bool QuicConnection::OnAckTimestamp(QuicPacketNumber packet_number, |
| QuicTime timestamp) { |
| QUIC_BUG_IF(quic_bug_10511_7, !connected_) |
| << "Processing ACK frame time stamp when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| QUIC_DVLOG(1) << ENDPOINT << "OnAckTimestamp: [" << packet_number << ", " |
| << timestamp.ToDebuggingValue() << ")"; |
| |
| if (GetLargestReceivedPacketWithAck().IsInitialized() && |
| last_received_packet_info_.header.packet_number <= |
| GetLargestReceivedPacketWithAck()) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; |
| return true; |
| } |
| |
| sent_packet_manager_.OnAckTimestamp(packet_number, timestamp); |
| return true; |
| } |
| |
| bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) { |
| QUIC_BUG_IF(quic_bug_12714_7, !connected_) |
| << "Processing ACK frame end when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start; |
| |
| if (GetLargestReceivedPacketWithAck().IsInitialized() && |
| last_received_packet_info_.header.packet_number <= |
| GetLargestReceivedPacketWithAck()) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; |
| return true; |
| } |
| const bool one_rtt_packet_was_acked = |
| sent_packet_manager_.one_rtt_packet_acked(); |
| const bool zero_rtt_packet_was_acked = |
| sent_packet_manager_.zero_rtt_packet_acked(); |
| const AckResult ack_result = sent_packet_manager_.OnAckFrameEnd( |
| idle_network_detector_.time_of_last_received_packet(), |
| last_received_packet_info_.header.packet_number, |
| last_received_packet_info_.decrypted_level); |
| if (ack_result != PACKETS_NEWLY_ACKED && |
| ack_result != NO_PACKETS_NEWLY_ACKED) { |
| // Error occurred (e.g., this ACK tries to ack packets in wrong packet |
| // number space), and this would cause the connection to be closed. |
| QUIC_DLOG(ERROR) << ENDPOINT |
| << "Error occurred when processing an ACK frame: " |
| << QuicUtils::AckResultToString(ack_result); |
| return false; |
| } |
| if (SupportsMultiplePacketNumberSpaces() && !one_rtt_packet_was_acked && |
| sent_packet_manager_.one_rtt_packet_acked()) { |
| visitor_->OnOneRttPacketAcknowledged(); |
| } |
| if (debug_visitor_ != nullptr && version().UsesTls() && |
| !zero_rtt_packet_was_acked && |
| sent_packet_manager_.zero_rtt_packet_acked()) { |
| debug_visitor_->OnZeroRttPacketAcked(); |
| } |
| // Cancel the send alarm because new packets likely have been acked, which |
| // may change the congestion window and/or pacing rate. Canceling the alarm |
| // causes CanWrite to recalculate the next send time. |
| if (send_alarm_->IsSet()) { |
| send_alarm_->Cancel(); |
| } |
| if (supports_release_time_) { |
| // Update pace time into future because smoothed RTT is likely updated. |
| UpdateReleaseTimeIntoFuture(); |
| } |
| SetLargestReceivedPacketWithAck( |
| last_received_packet_info_.header.packet_number); |
| // If the incoming ack's packets set expresses missing packets: peer is still |
| // waiting for a packet lower than a packet that we are no longer planning to |
| // send. |
| // If the incoming ack's packets set expresses received packets: peer is still |
| // acking packets which we never care about. |
| // Send an ack to raise the high water mark. |
| const bool send_stop_waiting = |
| no_stop_waiting_frames_ ? false : GetLeastUnacked() > start; |
| PostProcessAfterAckFrame(send_stop_waiting, |
| ack_result == PACKETS_NEWLY_ACKED); |
| processing_ack_frame_ = false; |
| return connected_; |
| } |
| |
| bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_8, !connected_) |
| << "Processing STOP_WAITING frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| |
| // Since a stop waiting frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(STOP_WAITING_FRAME)) { |
| return false; |
| } |
| |
| if (no_stop_waiting_frames_) { |
| return true; |
| } |
| if (largest_seen_packet_with_stop_waiting_.IsInitialized() && |
| last_received_packet_info_.header.packet_number <= |
| largest_seen_packet_with_stop_waiting_) { |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Received an old stop waiting frame: ignoring"; |
| return true; |
| } |
| |
| const char* error = ValidateStopWaitingFrame(frame); |
| if (error != nullptr) { |
| CloseConnection(QUIC_INVALID_STOP_WAITING_DATA, error, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnStopWaitingFrame(frame); |
| } |
| |
| largest_seen_packet_with_stop_waiting_ = |
| last_received_packet_info_.header.packet_number; |
| uber_received_packet_manager_.DontWaitForPacketsBefore( |
| last_received_packet_info_.decrypted_level, frame.least_unacked); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_9, !connected_) |
| << "Processing PADDING frame when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| if (!UpdatePacketContent(PADDING_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPaddingFrame(frame); |
| } |
| return true; |
| } |
| |
| bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_10, !connected_) |
| << "Processing PING frame when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| if (!UpdatePacketContent(PING_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| QuicTime::Delta ping_received_delay = QuicTime::Delta::Zero(); |
| const QuicTime now = clock_->ApproximateNow(); |
| if (now > stats_.connection_creation_time) { |
| ping_received_delay = now - stats_.connection_creation_time; |
| } |
| debug_visitor_->OnPingFrame(frame, ping_received_delay); |
| } |
| MaybeUpdateAckTimeout(); |
| return true; |
| } |
| |
| const char* QuicConnection::ValidateStopWaitingFrame( |
| const QuicStopWaitingFrame& stop_waiting) { |
| const QuicPacketNumber peer_least_packet_awaiting_ack = |
| uber_received_packet_manager_.peer_least_packet_awaiting_ack(); |
| if (peer_least_packet_awaiting_ack.IsInitialized() && |
| stop_waiting.least_unacked < peer_least_packet_awaiting_ack) { |
| QUIC_DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " |
| << stop_waiting.least_unacked << " vs " |
| << peer_least_packet_awaiting_ack; |
| // We never process old ack frames, so this number should only increase. |
| return "Least unacked too small."; |
| } |
| |
| if (stop_waiting.least_unacked > |
| last_received_packet_info_.header.packet_number) { |
| QUIC_DLOG(ERROR) << ENDPOINT |
| << "Peer sent least_unacked:" << stop_waiting.least_unacked |
| << " greater than the enclosing packet number:" |
| << last_received_packet_info_.header.packet_number; |
| return "Least unacked too large."; |
| } |
| |
| return nullptr; |
| } |
| |
| bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_11, !connected_) |
| << "Processing RST_STREAM frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| |
| // Since a reset stream frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(RST_STREAM_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnRstStreamFrame(frame); |
| } |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "RST_STREAM_FRAME received for stream: " << frame.stream_id |
| << " with error: " |
| << QuicRstStreamErrorCodeToString(frame.error_code); |
| MaybeUpdateAckTimeout(); |
| visitor_->OnRstStream(frame); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnStopSendingFrame(const QuicStopSendingFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_12, !connected_) |
| << "Processing STOP_SENDING frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| |
| // Since a reset stream frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(STOP_SENDING_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnStopSendingFrame(frame); |
| } |
| |
| QUIC_DLOG(INFO) << ENDPOINT << "STOP_SENDING frame received for stream: " |
| << frame.stream_id |
| << " with error: " << frame.ietf_error_code; |
| MaybeUpdateAckTimeout(); |
| visitor_->OnStopSendingFrame(frame); |
| return connected_; |
| } |
| |
| class ReversePathValidationContext : public QuicPathValidationContext { |
| public: |
| ReversePathValidationContext(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const QuicSocketAddress& effective_peer_address, |
| QuicConnection* connection) |
| : QuicPathValidationContext(self_address, peer_address, |
| effective_peer_address), |
| connection_(connection) {} |
| |
| QuicPacketWriter* WriterToUse() override { return connection_->writer(); } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) { |
| QUIC_BUG_IF(quic_bug_10511_8, !connected_) |
| << "Processing PATH_CHALLENGE frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| if (has_path_challenge_in_current_packet_) { |
| // Only respond to the 1st PATH_CHALLENGE in the packet. |
| return true; |
| } |
| if (!validate_client_addresses_) { |
| return OnPathChallengeFrameInternal(frame); |
| } |
| { |
| // TODO(danzh) inline OnPathChallengeFrameInternal() once |
| // validate_client_addresses_ is deprecated. |
| if (!OnPathChallengeFrameInternal(frame)) { |
| return false; |
| } |
| } |
| return connected_; |
| } |
| |
| bool QuicConnection::OnPathChallengeFrameInternal( |
| const QuicPathChallengeFrame& frame) { |
| should_proactively_validate_peer_address_on_path_challenge_ = false; |
| // UpdatePacketContent() may start reverse path validation. |
| if (!UpdatePacketContent(PATH_CHALLENGE_FRAME)) { |
| return false; |
| } |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPathChallengeFrame(frame); |
| } |
| |
| const QuicSocketAddress current_effective_peer_address = |
| GetEffectivePeerAddressFromCurrentPacket(); |
| QuicConnectionId client_cid, server_cid; |
| FindOnPathConnectionIds(last_received_packet_info_.destination_address, |
| current_effective_peer_address, &client_cid, |
| &server_cid); |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, last_received_packet_info_.source_address, client_cid, |
| server_cid, connection_migration_use_new_cid_); |
| if (should_proactively_validate_peer_address_on_path_challenge_) { |
| // Conditions to proactively validate peer address: |
| // The perspective is server |
| // The PATH_CHALLENGE is received on an unvalidated alternative path. |
| // The connection isn't validating migrated peer address, which is of |
| // higher prority. |
| QUIC_DVLOG(1) << "Proactively validate the effective peer address " |
| << current_effective_peer_address; |
| QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 2, 6); |
| ValidatePath(std::make_unique<ReversePathValidationContext>( |
| default_path_.self_address, |
| last_received_packet_info_.source_address, |
| current_effective_peer_address, this), |
| std::make_unique<ReversePathValidationResultDelegate>( |
| this, peer_address())); |
| } |
| has_path_challenge_in_current_packet_ = true; |
| MaybeUpdateAckTimeout(); |
| // Queue or send PATH_RESPONSE. Send PATH_RESPONSE to the source address of |
| // the current incoming packet, even if it's not the default path or the |
| // alternative path. |
| if (!SendPathResponse(frame.data_buffer, |
| last_received_packet_info_.source_address, |
| current_effective_peer_address)) { |
| QUIC_CODE_COUNT(quic_failed_to_send_path_response); |
| } |
| // TODO(b/150095588): change the stats to |
| // num_valid_path_challenge_received. |
| ++stats_.num_connectivity_probing_received; |
| |
| // SendPathResponse() might cause connection to be closed. |
| return connected_; |
| } |
| |
| bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) { |
| QUIC_BUG_IF(quic_bug_10511_9, !connected_) |
| << "Processing PATH_RESPONSE frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| ++stats_.num_path_response_received; |
| if (!UpdatePacketContent(PATH_RESPONSE_FRAME)) { |
| return false; |
| } |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPathResponseFrame(frame); |
| } |
| MaybeUpdateAckTimeout(); |
| path_validator_.OnPathResponse( |
| frame.data_buffer, last_received_packet_info_.destination_address); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnConnectionCloseFrame( |
| const QuicConnectionCloseFrame& frame) { |
| QUIC_BUG_IF(quic_bug_10511_10, !connected_) |
| << "Processing CONNECTION_CLOSE frame when connection is closed. " |
| "Received packet info: " |
| << last_received_packet_info_; |
| |
| // Since a connection close frame was received, this is not a connectivity |
| // probe. A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(CONNECTION_CLOSE_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnConnectionCloseFrame(frame); |
| } |
| switch (frame.close_type) { |
| case GOOGLE_QUIC_CONNECTION_CLOSE: |
| QUIC_DLOG(INFO) << ENDPOINT << "Received ConnectionClose for connection: " |
| << connection_id() << ", with error: " |
| << QuicErrorCodeToString(frame.quic_error_code) << " (" |
| << frame.error_details << ")"; |
| break; |
| case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE: |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Received Transport ConnectionClose for connection: " |
| << connection_id() << ", with error: " |
| << QuicErrorCodeToString(frame.quic_error_code) << " (" |
| << frame.error_details << ")" |
| << ", transport error code: " |
| << QuicIetfTransportErrorCodeString( |
| static_cast<QuicIetfTransportErrorCodes>( |
| frame.wire_error_code)) |
| << ", error frame type: " |
| << frame.transport_close_frame_type; |
| break; |
| case IETF_QUIC_APPLICATION_CONNECTION_CLOSE: |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Received Application ConnectionClose for connection: " |
| << connection_id() << ", with error: " |
| << QuicErrorCodeToString(frame.quic_error_code) << " (" |
| << frame.error_details << ")" |
| << ", application error code: " << frame.wire_error_code; |
| break; |
| } |
| |
| if (frame.quic_error_code == QUIC_BAD_MULTIPATH_FLAG) { |
| QUIC_LOG_FIRST_N(ERROR, 10) |
| << "Unexpected QUIC_BAD_MULTIPATH_FLAG error." |
| << " last_received_header: " << last_received_packet_info_.header |
| << " encryption_level: " << encryption_level_; |
| } |
| TearDownLocalConnectionState(frame, ConnectionCloseSource::FROM_PEER); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_13, !connected_) |
| << "Processing MAX_STREAMS frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| if (!UpdatePacketContent(MAX_STREAMS_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnMaxStreamsFrame(frame); |
| } |
| MaybeUpdateAckTimeout(); |
| return visitor_->OnMaxStreamsFrame(frame) && connected_; |
| } |
| |
| bool QuicConnection::OnStreamsBlockedFrame( |
| const QuicStreamsBlockedFrame& frame) { |
| QUIC_BUG_IF(quic_bug_10511_11, !connected_) |
| << "Processing STREAMS_BLOCKED frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| if (!UpdatePacketContent(STREAMS_BLOCKED_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnStreamsBlockedFrame(frame); |
| } |
| MaybeUpdateAckTimeout(); |
| return visitor_->OnStreamsBlockedFrame(frame) && connected_; |
| } |
| |
| bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_14, !connected_) |
| << "Processing GOAWAY frame when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| |
| // Since a go away frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(GOAWAY_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnGoAwayFrame(frame); |
| } |
| QUIC_DLOG(INFO) << ENDPOINT << "GOAWAY_FRAME received with last good stream: " |
| << frame.last_good_stream_id |
| << " and error: " << QuicErrorCodeToString(frame.error_code) |
| << " and reason: " << frame.reason_phrase; |
| MaybeUpdateAckTimeout(); |
| visitor_->OnGoAway(frame); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { |
| QUIC_BUG_IF(quic_bug_10511_12, !connected_) |
| << "Processing WINDOW_UPDATE frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| |
| // Since a window update frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(WINDOW_UPDATE_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnWindowUpdateFrame( |
| frame, idle_network_detector_.time_of_last_received_packet()); |
| } |
| QUIC_DVLOG(1) << ENDPOINT << "WINDOW_UPDATE_FRAME received " << frame; |
| MaybeUpdateAckTimeout(); |
| visitor_->OnWindowUpdateFrame(frame); |
| return connected_; |
| } |
| |
| void QuicConnection::OnClientConnectionIdAvailable() { |
| QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); |
| if (!peer_issued_cid_manager_->HasUnusedConnectionId()) { |
| return; |
| } |
| if (default_path_.client_connection_id.IsEmpty()) { |
| // Count client connection ID patched onto the default path. |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 3, |
| 6); |
| const QuicConnectionIdData* unused_cid_data = |
| peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); |
| QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID " |
| << unused_cid_data->connection_id << " to default path"; |
| default_path_.client_connection_id = unused_cid_data->connection_id; |
| default_path_.stateless_reset_token = |
| unused_cid_data->stateless_reset_token; |
| QUICHE_DCHECK(!packet_creator_.HasPendingFrames()); |
| QUICHE_DCHECK(packet_creator_.GetDestinationConnectionId().IsEmpty()); |
| packet_creator_.SetClientConnectionId(default_path_.client_connection_id); |
| return; |
| } |
| if (alternative_path_.peer_address.IsInitialized() && |
| alternative_path_.client_connection_id.IsEmpty()) { |
| // Count client connection ID patched onto the alternative path. |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 4, |
| 6); |
| const QuicConnectionIdData* unused_cid_data = |
| peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); |
| QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID " |
| << unused_cid_data->connection_id << " to alternative path"; |
| alternative_path_.client_connection_id = unused_cid_data->connection_id; |
| alternative_path_.stateless_reset_token = |
| unused_cid_data->stateless_reset_token; |
| } |
| } |
| |
| bool QuicConnection::OnNewConnectionIdFrameInner( |
| const QuicNewConnectionIdFrame& frame) { |
| if (peer_issued_cid_manager_ == nullptr) { |
| CloseConnection( |
| IETF_QUIC_PROTOCOL_VIOLATION, |
| "Receives NEW_CONNECTION_ID while peer uses zero length connection ID", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| std::string error_detail; |
| QuicErrorCode error = |
| peer_issued_cid_manager_->OnNewConnectionIdFrame(frame, &error_detail); |
| if (error != QUIC_NO_ERROR) { |
| CloseConnection(error, error_detail, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| if (perspective_ == Perspective::IS_SERVER) { |
| OnClientConnectionIdAvailable(); |
| } |
| MaybeUpdateAckTimeout(); |
| return true; |
| } |
| |
| bool QuicConnection::OnNewConnectionIdFrame( |
| const QuicNewConnectionIdFrame& frame) { |
| QUICHE_DCHECK(version().HasIetfQuicFrames()); |
| QUIC_BUG_IF(quic_bug_10511_13, !connected_) |
| << "Processing NEW_CONNECTION_ID frame when connection is closed. " |
| "Received packet info: " |
| << last_received_packet_info_; |
| if (!UpdatePacketContent(NEW_CONNECTION_ID_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnNewConnectionIdFrame(frame); |
| } |
| |
| if (!OnNewConnectionIdFrameInner(frame)) { |
| return false; |
| } |
| if (perspective_ == Perspective::IS_CLIENT && multi_port_enabled_) { |
| MaybeCreateMultiPortPath(); |
| } |
| return true; |
| } |
| |
| bool QuicConnection::OnRetireConnectionIdFrame( |
| const QuicRetireConnectionIdFrame& frame) { |
| QUICHE_DCHECK(version().HasIetfQuicFrames()); |
| QUIC_BUG_IF(quic_bug_10511_14, !connected_) |
| << "Processing RETIRE_CONNECTION_ID frame when connection is closed. " |
| "Received packet info: " |
| << last_received_packet_info_; |
| if (!UpdatePacketContent(RETIRE_CONNECTION_ID_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnRetireConnectionIdFrame(frame); |
| } |
| if (!connection_migration_use_new_cid_) { |
| // Do not respond to RetireConnectionId frame. |
| return true; |
| } |
| if (self_issued_cid_manager_ == nullptr) { |
| CloseConnection( |
| IETF_QUIC_PROTOCOL_VIOLATION, |
| "Receives RETIRE_CONNECTION_ID while new connection ID is never issued", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| std::string error_detail; |
| QuicErrorCode error = self_issued_cid_manager_->OnRetireConnectionIdFrame( |
| frame, sent_packet_manager_.GetPtoDelay(), &error_detail); |
| if (error != QUIC_NO_ERROR) { |
| CloseConnection(error, error_detail, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| // Count successfully received RETIRE_CONNECTION_ID frames. |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 5, 6); |
| MaybeUpdateAckTimeout(); |
| return true; |
| } |
| |
| bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_15, !connected_) |
| << "Processing NEW_TOKEN frame when connection is closed. Received " |
| "packet info: " |
| << last_received_packet_info_; |
| if (!UpdatePacketContent(NEW_TOKEN_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnNewTokenFrame(frame); |
| } |
| if (perspective_ == Perspective::IS_SERVER) { |
| CloseConnection(QUIC_INVALID_NEW_TOKEN, "Server received new token frame.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| // NEW_TOKEN frame should insitgate ACKs. |
| MaybeUpdateAckTimeout(); |
| visitor_->OnNewTokenReceived(frame.token); |
| return true; |
| } |
| |
| bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_16, !connected_) |
| << "Processing MESSAGE frame when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| |
| // Since a message frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(MESSAGE_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnMessageFrame(frame); |
| } |
| MaybeUpdateAckTimeout(); |
| visitor_->OnMessageReceived( |
| absl::string_view(frame.data, frame.message_length)); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) { |
| QUIC_BUG_IF(quic_bug_10511_15, !connected_) |
| << "Processing HANDSHAKE_DONE frame when connection " |
| "is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| if (!version().UsesTls()) { |
| CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, |
| "Handshake done frame is unsupported", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| |
| if (perspective_ == Perspective::IS_SERVER) { |
| CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, |
| "Server received handshake done frame.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| |
| // Since a handshake done frame was received, this is not a connectivity |
| // probe. A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(HANDSHAKE_DONE_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnHandshakeDoneFrame(frame); |
| } |
| MaybeUpdateAckTimeout(); |
| visitor_->OnHandshakeDoneReceived(); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) { |
| QUIC_BUG_IF(quic_bug_10511_16, !connected_) |
| << "Processing ACK_FREQUENCY frame when connection " |
| "is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnAckFrequencyFrame(frame); |
| } |
| if (!UpdatePacketContent(ACK_FREQUENCY_FRAME)) { |
| return false; |
| } |
| |
| if (!can_receive_ack_frequency_frame_) { |
| QUIC_LOG_EVERY_N_SEC(ERROR, 120) << "Get unexpected AckFrequencyFrame."; |
| return false; |
| } |
| if (auto packet_number_space = |
| QuicUtils::GetPacketNumberSpace( |
| last_received_packet_info_.decrypted_level) == APPLICATION_DATA) { |
| uber_received_packet_manager_.OnAckFrequencyFrame(frame); |
| } else { |
| QUIC_LOG_EVERY_N_SEC(ERROR, 120) |
| << "Get AckFrequencyFrame in packet number space " |
| << packet_number_space; |
| } |
| MaybeUpdateAckTimeout(); |
| return true; |
| } |
| |
| bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { |
| QUIC_BUG_IF(quic_bug_12714_17, !connected_) |
| << "Processing BLOCKED frame when connection is closed. Received packet " |
| "info: " |
| << last_received_packet_info_; |
| |
| // Since a blocked frame was received, this is not a connectivity probe. |
| // A probe only contains a PING and full padding. |
| if (!UpdatePacketContent(BLOCKED_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnBlockedFrame(frame); |
| } |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "BLOCKED_FRAME received for stream: " << frame.stream_id; |
| MaybeUpdateAckTimeout(); |
| visitor_->OnBlockedFrame(frame); |
| stats_.blocked_frames_received++; |
| return connected_; |
| } |
| |
| void QuicConnection::OnPacketComplete() { |
| // Don't do anything if this packet closed the connection. |
| if (!connected_) { |
| ClearLastFrames(); |
| return; |
| } |
| |
| if (IsCurrentPacketConnectivityProbing()) { |
| QUICHE_DCHECK(!version().HasIetfQuicFrames()); |
| ++stats_.num_connectivity_probing_received; |
| } |
| |
| QUIC_DVLOG(1) << ENDPOINT << "Got" |
| << (SupportsMultiplePacketNumberSpaces() |
| ? (" " + |
| EncryptionLevelToString( |
| last_received_packet_info_.decrypted_level)) |
| : "") |
| << " packet " << last_received_packet_info_.header.packet_number |
| << " for " |
| << GetServerConnectionIdAsRecipient( |
| last_received_packet_info_.header, perspective_); |
| |
| QUIC_DLOG_IF(INFO, current_packet_content_ == SECOND_FRAME_IS_PADDING) |
| << ENDPOINT << "Received a padded PING packet. is_probing: " |
| << IsCurrentPacketConnectivityProbing(); |
| |
| if (!version().HasIetfQuicFrames()) { |
| MaybeRespondToConnectivityProbingOrMigration(); |
| } |
| |
| current_effective_peer_migration_type_ = NO_CHANGE; |
| |
| // For IETF QUIC, it is guaranteed that TLS will give connection the |
| // corresponding write key before read key. In other words, connection should |
| // never process a packet while an ACK for it cannot be encrypted. |
| if (!should_last_packet_instigate_acks_) { |
| uber_received_packet_manager_.MaybeUpdateAckTimeout( |
| should_last_packet_instigate_acks_, |
| last_received_packet_info_.decrypted_level, |
| last_received_packet_info_.header.packet_number, |
| last_received_packet_info_.receipt_time, clock_->ApproximateNow(), |
| sent_packet_manager_.GetRttStats()); |
| } |
| |
| ClearLastFrames(); |
| CloseIfTooManyOutstandingSentPackets(); |
| } |
| |
| void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() { |
| QUICHE_DCHECK(!version().HasIetfQuicFrames()); |
| if (IsCurrentPacketConnectivityProbing()) { |
| visitor_->OnPacketReceived(last_received_packet_info_.destination_address, |
| last_received_packet_info_.source_address, |
| /*is_connectivity_probe=*/true); |
| return; |
| } |
| if (perspective_ == Perspective::IS_CLIENT) { |
| // This node is a client, notify that a speculative connectivity probing |
| // packet has been received anyway. |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Received a speculative connectivity probing packet for " |
| << GetServerConnectionIdAsRecipient( |
| last_received_packet_info_.header, perspective_) |
| << " from ip:port: " |
| << last_received_packet_info_.source_address.ToString() |
| << " to ip:port: " |
| << last_received_packet_info_.destination_address.ToString(); |
| visitor_->OnPacketReceived(last_received_packet_info_.destination_address, |
| last_received_packet_info_.source_address, |
| /*is_connectivity_probe=*/false); |
| return; |
| } |
| } |
| |
| bool QuicConnection::IsValidStatelessResetToken( |
| const StatelessResetToken& token) const { |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| return default_path_.stateless_reset_token.has_value() && |
| QuicUtils::AreStatelessResetTokensEqual( |
| token, *default_path_.stateless_reset_token); |
| } |
| |
| void QuicConnection::OnAuthenticatedIetfStatelessResetPacket( |
| const QuicIetfStatelessResetPacket& /*packet*/) { |
| // TODO(fayang): Add OnAuthenticatedIetfStatelessResetPacket to |
| // debug_visitor_. |
| QUICHE_DCHECK(version().HasIetfInvariantHeader()); |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| |
| if (!IsDefaultPath(last_received_packet_info_.destination_address, |
| last_received_packet_info_.source_address)) { |
| // This packet is received on a probing path. Do not close connection. |
| if (IsAlternativePath(last_received_packet_info_.destination_address, |
| GetEffectivePeerAddressFromCurrentPacket())) { |
| QUIC_BUG_IF(quic_bug_12714_18, alternative_path_.validated) |
| << "STATELESS_RESET received on alternate path after it's " |
| "validated."; |
| path_validator_.CancelPathValidation(); |
| } else { |
| QUIC_BUG(quic_bug_10511_17) |
| << "Received Stateless Reset on unknown socket."; |
| } |
| return; |
| } |
| |
| const std::string error_details = "Received stateless reset."; |
| QUIC_CODE_COUNT(quic_tear_down_local_connection_on_stateless_reset); |
| TearDownLocalConnectionState(QUIC_PUBLIC_RESET, NO_IETF_QUIC_ERROR, |
| error_details, ConnectionCloseSource::FROM_PEER); |
| } |
| |
| void QuicConnection::OnKeyUpdate(KeyUpdateReason reason) { |
| QUICHE_DCHECK(support_key_update_for_connection_); |
| QUIC_DLOG(INFO) << ENDPOINT << "Key phase updated for " << reason; |
| |
| lowest_packet_sent_in_current_key_phase_.Clear(); |
| stats_.key_update_count++; |
| |
| // If another key update triggers while the previous |
| // discard_previous_one_rtt_keys_alarm_ hasn't fired yet, cancel it since the |
| // old keys would already be discarded. |
| discard_previous_one_rtt_keys_alarm_->Cancel(); |
| |
| visitor_->OnKeyUpdate(reason); |
| } |
| |
| void QuicConnection::OnDecryptedFirstPacketInKeyPhase() { |
| QUIC_DLOG(INFO) << ENDPOINT << "OnDecryptedFirstPacketInKeyPhase"; |
| // An endpoint SHOULD retain old read keys for no more than three times the |
| // PTO after having received a packet protected using the new keys. After this |
| // period, old read keys and their corresponding secrets SHOULD be discarded. |
| // |
| // Note that this will cause an unnecessary |
| // discard_previous_one_rtt_keys_alarm_ on the first packet in the 1RTT |
| // encryption level, but this is harmless. |
| discard_previous_one_rtt_keys_alarm_->Set( |
| clock_->ApproximateNow() + sent_packet_manager_.GetPtoDelay() * 3); |
| } |
| |
| std::unique_ptr<QuicDecrypter> |
| QuicConnection::AdvanceKeysAndCreateCurrentOneRttDecrypter() { |
| QUIC_DLOG(INFO) << ENDPOINT << "AdvanceKeysAndCreateCurrentOneRttDecrypter"; |
| return visitor_->AdvanceKeysAndCreateCurrentOneRttDecrypter(); |
| } |
| |
| std::unique_ptr<QuicEncrypter> QuicConnection::CreateCurrentOneRttEncrypter() { |
| QUIC_DLOG(INFO) << ENDPOINT << "CreateCurrentOneRttEncrypter"; |
| return visitor_->CreateCurrentOneRttEncrypter(); |
| } |
| |
| void QuicConnection::ClearLastFrames() { |
| should_last_packet_instigate_acks_ = false; |
| } |
| |
| void QuicConnection::CloseIfTooManyOutstandingSentPackets() { |
| // This occurs if we don't discard old packets we've seen fast enough. It's |
| // possible largest observed is less than leaset unacked. |
| const bool should_close = |
| sent_packet_manager_.GetLargestSentPacket().IsInitialized() && |
| sent_packet_manager_.GetLargestSentPacket() > |
| sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_; |
| |
| if (should_close) { |
| CloseConnection( |
| QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, |
| absl::StrCat("More than ", max_tracked_packets_, |
| " outstanding, least_unacked: ", |
| sent_packet_manager_.GetLeastUnacked().ToUint64(), |
| ", packets_processed: ", stats_.packets_processed, |
| ", last_decrypted_packet_level: ", |
| EncryptionLevelToString( |
| last_received_packet_info_.decrypted_level)), |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| } |
| } |
| |
| const QuicFrame QuicConnection::GetUpdatedAckFrame() { |
| QUICHE_DCHECK(!uber_received_packet_manager_.IsAckFrameEmpty( |
| QuicUtils::GetPacketNumberSpace(encryption_level_))) |
| << "Try to retrieve an empty ACK frame"; |
| return uber_received_packet_manager_.GetUpdatedAckFrame( |
| QuicUtils::GetPacketNumberSpace(encryption_level_), |
| clock_->ApproximateNow()); |
| } |
| |
| void QuicConnection::PopulateStopWaitingFrame( |
| QuicStopWaitingFrame* stop_waiting) { |
| stop_waiting->least_unacked = GetLeastUnacked(); |
| } |
| |
| QuicPacketNumber QuicConnection::GetLeastUnacked() const { |
| return sent_packet_manager_.GetLeastUnacked(); |
| } |
| |
| bool QuicConnection::HandleWriteBlocked() { |
| if (!writer_->IsWriteBlocked()) { |
| return false; |
| } |
| |
| visitor_->OnWriteBlocked(); |
| return true; |
| } |
| |
| void QuicConnection::MaybeSendInResponseToPacket() { |
| if (!connected_) { |
| return; |
| } |
| |
| // If the writer is blocked, don't attempt to send packets now or in the send |
| // alarm. When the writer unblocks, OnCanWrite() will be called for this |
| // connection to send. |
| if (HandleWriteBlocked()) { |
| return; |
| } |
| |
| // Now that we have received an ack, we might be able to send packets which |
| // are queued locally, or drain streams which are blocked. |
| if (defer_send_in_response_to_packets_) { |
| send_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero()); |
| } else { |
| WriteIfNotBlocked(); |
| } |
| } |
| |
| void QuicConnection::MaybeActivateLegacyVersionEncapsulation() { |
| if (!legacy_version_encapsulation_enabled_) { |
| return; |
| } |
| QUICHE_DCHECK(!legacy_version_encapsulation_in_progress_); |
| QUIC_BUG_IF(quic_bug_12714_19, !packet_creator_.CanSetMaxPacketLength()) |
| << "Cannot activate Legacy Version Encapsulation mid-packet"; |
| QUIC_BUG_IF(quic_bug_12714_20, coalesced_packet_.length() != 0u) |
| << "Cannot activate Legacy Version Encapsulation mid-coalesced-packet"; |
| legacy_version_encapsulation_in_progress_ = true; |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| } |
| void QuicConnection::MaybeDisactivateLegacyVersionEncapsulation() { |
| if (!legacy_version_encapsulation_in_progress_) { |
| return; |
| } |
| // Flush any remaining packet before disactivating encapsulation. |
| packet_creator_.FlushCurrentPacket(); |
| QUICHE_DCHECK(legacy_version_encapsulation_enabled_); |
| legacy_version_encapsulation_in_progress_ = false; |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| } |
| |
| size_t QuicConnection::SendCryptoData(EncryptionLevel level, |
| size_t write_length, |
| QuicStreamOffset offset) { |
| if (write_length == 0) { |
| QUIC_BUG(quic_bug_10511_18) << "Attempt to send empty crypto frame"; |
| return 0; |
| } |
| if (level == ENCRYPTION_INITIAL) { |
| MaybeActivateLegacyVersionEncapsulation(); |
| } |
| size_t consumed_length; |
| { |
| ScopedPacketFlusher flusher(this); |
| consumed_length = |
| packet_creator_.ConsumeCryptoData(level, write_length, offset); |
| } // Added scope ensures packets are flushed before continuing. |
| MaybeDisactivateLegacyVersionEncapsulation(); |
| return consumed_length; |
| } |
| |
| QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, |
| size_t write_length, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| if (state == NO_FIN && write_length == 0) { |
| QUIC_BUG(quic_bug_10511_19) << "Attempt to send empty stream frame"; |
| return QuicConsumedData(0, false); |
| } |
| |
| if (packet_creator_.encryption_level() == ENCRYPTION_INITIAL && |
| QuicUtils::IsCryptoStreamId(transport_version(), id)) { |
| MaybeActivateLegacyVersionEncapsulation(); |
| } |
| if (version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) { |
| if (in_on_retransmission_time_out_ && |
| coalesced_packet_.NumberOfPackets() == 0u) { |
| // PTO fires while handshake is not confirmed. Do not preempt handshake |
| // data with stream data. |
| QUIC_CODE_COUNT(quic_try_to_send_half_rtt_data_when_pto_fires); |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Not PTOing stream data before handshake gets confirmed"; |
| return QuicConsumedData(0, false); |
| } |
| if (perspective_ == Perspective::IS_SERVER && |
| coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) && |
| coalesced_packet_.NumberOfPackets() == 1u) { |
| // Handshake is not confirmed yet, if there is only an initial packet in |
| // the coalescer, try to bundle an ENCRYPTION_HANDSHAKE packet before |
| // sending stream data. |
| sent_packet_manager_.RetransmitDataOfSpaceIfAny(HANDSHAKE_DATA); |
| } |
| } |
| QuicConsumedData consumed_data(0, false); |
| { |
| // Opportunistically bundle an ack with every outgoing packet. |
| // Particularly, we want to bundle with handshake packets since we don't |
| // know which decrypter will be used on an ack packet following a handshake |
| // packet (a handshake packet from client to server could result in a REJ or |
| // a SHLO from the server, leading to two different decrypters at the |
| // server.) |
| ScopedPacketFlusher flusher(this); |
| consumed_data = |
| packet_creator_.ConsumeData(id, write_length, offset, state); |
| } // Added scope ensures packets are flushed before continuing. |
| MaybeDisactivateLegacyVersionEncapsulation(); |
| return consumed_data; |
| } |
| |
| bool QuicConnection::SendControlFrame(const QuicFrame& frame) { |
| if (SupportsMultiplePacketNumberSpaces() && |
| (encryption_level_ == ENCRYPTION_INITIAL || |
| encryption_level_ == ENCRYPTION_HANDSHAKE) && |
| frame.type != PING_FRAME) { |
| // Allow PING frame to be sent without APPLICATION key. For example, when |
| // anti-amplification limit is used, client needs to send something to avoid |
| // handshake deadlock. |
| QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame |
| << " at encryption level: " << encryption_level_; |
| return false; |
| } |
| ScopedPacketFlusher flusher(this); |
| const bool consumed = |
| packet_creator_.ConsumeRetransmittableControlFrame(frame); |
| if (!consumed) { |
| QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame; |
| return false; |
| } |
| if (frame.type == PING_FRAME) { |
| // Flush PING frame immediately. |
| packet_creator_.FlushCurrentPacket(); |
| stats_.ping_frames_sent++; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPingSent(); |
| } |
| } |
| if (frame.type == BLOCKED_FRAME) { |
| stats_.blocked_frames_sent++; |
| } |
| return true; |
| } |
| |
| void QuicConnection::OnStreamReset(QuicStreamId id, |
| QuicRstStreamErrorCode error) { |
| if (error == QUIC_STREAM_NO_ERROR) { |
| // All data for streams which are reset with QUIC_STREAM_NO_ERROR must |
| // be received by the peer. |
| return; |
| } |
| // Flush stream frames of reset stream. |
| if (packet_creator_.HasPendingStreamFramesOfStream(id)) { |
| ScopedPacketFlusher flusher(this); |
| packet_creator_.FlushCurrentPacket(); |
| } |
| // TODO(ianswett): Consider checking for 3 RTOs when the last stream is |
| // cancelled as well. |
| } |
| |
| const QuicConnectionStats& QuicConnection::GetStats() { |
| const RttStats* rtt_stats = sent_packet_manager_.GetRttStats(); |
| |
| // Update rtt and estimated bandwidth. |
| QuicTime::Delta min_rtt = rtt_stats->min_rtt(); |
| if (min_rtt.IsZero()) { |
| // If min RTT has not been set, use initial RTT instead. |
| min_rtt = rtt_stats->initial_rtt(); |
| } |
| stats_.min_rtt_us = min_rtt.ToMicroseconds(); |
| |
| QuicTime::Delta srtt = rtt_stats->SmoothedOrInitialRtt(); |
| stats_.srtt_us = srtt.ToMicroseconds(); |
| |
| stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate(); |
| sent_packet_manager_.GetSendAlgorithm()->PopulateConnectionStats(&stats_); |
| stats_.egress_mtu = long_term_mtu_; |
| stats_.ingress_mtu = largest_received_packet_size_; |
| return stats_; |
| } |
| |
| void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) { |
| QueueCoalescedPacket(packet); |
| } |
| |
| void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet, |
| EncryptionLevel decryption_level, |
| bool has_decryption_key) { |
| QUIC_DVLOG(1) << ENDPOINT << "Received undecryptable packet of length " |
| << packet.length() << " with" |
| << (has_decryption_key ? "" : "out") << " key at level " |
| << decryption_level |
| << " while connection is at encryption level " |
| << encryption_level_; |
| QUICHE_DCHECK(EncryptionLevelIsValid(decryption_level)); |
| if (encryption_level_ != ENCRYPTION_FORWARD_SECURE) { |
| ++stats_.undecryptable_packets_received_before_handshake_complete; |
| } |
| |
| const bool should_enqueue = |
| ShouldEnqueueUnDecryptablePacket(decryption_level, has_decryption_key); |
| if (should_enqueue) { |
| QueueUndecryptablePacket(packet, decryption_level); |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnUndecryptablePacket(decryption_level, |
| /*dropped=*/!should_enqueue); |
| } |
| |
| if (has_decryption_key) { |
| stats_.num_failed_authentication_packets_received++; |
| if (version().UsesTls()) { |
| // Should always be non-null if has_decryption_key is true. |
| QUICHE_DCHECK(framer_.GetDecrypter(decryption_level)); |
| const QuicPacketCount integrity_limit = |
| framer_.GetDecrypter(decryption_level)->GetIntegrityLimit(); |
| QUIC_DVLOG(2) << ENDPOINT << "Checking AEAD integrity limits:" |
| << " num_failed_authentication_packets_received=" |
| << stats_.num_failed_authentication_packets_received |
| << " integrity_limit=" << integrity_limit; |
| if (stats_.num_failed_authentication_packets_received >= |
| integrity_limit) { |
| const std::string error_details = absl::StrCat( |
| "decrypter integrity limit reached:" |
| " num_failed_authentication_packets_received=", |
| stats_.num_failed_authentication_packets_received, |
| " integrity_limit=", integrity_limit); |
| CloseConnection(QUIC_AEAD_LIMIT_REACHED, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| } |
| } |
| } |
| |
| if (version().UsesTls() && perspective_ == Perspective::IS_SERVER && |
| decryption_level == ENCRYPTION_ZERO_RTT && !has_decryption_key && |
| had_zero_rtt_decrypter_) { |
| QUIC_CODE_COUNT_N( |
| quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 1, |
| 3); |
| stats_ |
| .num_tls_server_zero_rtt_packets_received_after_discarding_decrypter++; |
| } |
| } |
| |
| bool QuicConnection::ShouldEnqueueUnDecryptablePacket( |
| EncryptionLevel decryption_level, bool has_decryption_key) const { |
| if (has_decryption_key) { |
| // We already have the key for this decryption level, therefore no |
| // future keys will allow it be decrypted. |
| return false; |
| } |
| if (IsHandshakeComplete()) { |
| // We do not expect to install any further keys. |
| return false; |
| } |
| if (undecryptable_packets_.size() >= max_undecryptable_packets_) { |
| // We do not queue more than max_undecryptable_packets_ packets. |
| return false; |
| } |
| if (version().KnowsWhichDecrypterToUse() && |
| decryption_level == ENCRYPTION_INITIAL) { |
| // When the corresponding decryption key is not available, all |
| // non-Initial packets should be buffered until the handshake is complete. |
| return false; |
| } |
| if (perspective_ == Perspective::IS_CLIENT && version().UsesTls() && |
| decryption_level == ENCRYPTION_ZERO_RTT) { |
| // Only clients send Zero RTT packets in IETF QUIC. |
| QUIC_PEER_BUG(quic_peer_bug_client_received_zero_rtt) |
| << "Client received a Zero RTT packet, not buffering."; |
| return false; |
| } |
| return true; |
| } |
| |
| std::string QuicConnection::UndecryptablePacketsInfo() const { |
| std::string info = absl::StrCat( |
| "num_undecryptable_packets: ", undecryptable_packets_.size(), " {"); |
| for (const auto& packet : undecryptable_packets_) { |
| absl::StrAppend(&info, "[", |
| EncryptionLevelToString(packet.encryption_level), ", ", |
| packet.packet->length(), "]"); |
| } |
| absl::StrAppend(&info, "}"); |
| return info; |
| } |
| |
| void QuicConnection::MaybeUpdatePacketCreatorMaxPacketLengthAndPadding() { |
| QuicByteCount max_packet_length = GetLimitedMaxPacketSize(long_term_mtu_); |
| if (legacy_version_encapsulation_in_progress_) { |
| QUICHE_DCHECK(legacy_version_encapsulation_enabled_); |
| const QuicByteCount minimum_overhead = |
| QuicLegacyVersionEncapsulator::GetMinimumOverhead( |
| legacy_version_encapsulation_sni_); |
| if (max_packet_length < minimum_overhead) { |
| QUIC_BUG(quic_bug_10511_20) |
| << "Cannot apply Legacy Version Encapsulation overhead because " |
| << "max_packet_length " << max_packet_length << " < minimum_overhead " |
| << minimum_overhead; |
| legacy_version_encapsulation_in_progress_ = false; |
| legacy_version_encapsulation_enabled_ = false; |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| return; |
| } |
| max_packet_length -= minimum_overhead; |
| } |
| packet_creator_.SetMaxPacketLength(max_packet_length); |
| } |
| |
| void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const QuicReceivedPacket& packet) { |
| if (!connected_) { |
| return; |
| } |
| QUIC_DVLOG(2) << ENDPOINT << "Received encrypted " << packet.length() |
| << " bytes:" << std::endl |
| << quiche::QuicheTextUtils::HexDump( |
| absl::string_view(packet.data(), packet.length())); |
| QUIC_BUG_IF(quic_bug_12714_21, current_packet_data_ != nullptr) |
| << "ProcessUdpPacket must not be called while processing a packet."; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPacketReceived(self_address, peer_address, packet); |
| } |
| last_received_packet_info_ = ReceivedPacketInfo( |
| self_address, peer_address, packet.receipt_time(), packet.length()); |
| current_packet_data_ = packet.data(); |
| |
| if (!default_path_.self_address.IsInitialized()) { |
| default_path_.self_address = last_received_packet_info_.destination_address; |
| } |
| |
| if (!direct_peer_address_.IsInitialized()) { |
| UpdatePeerAddress(last_received_packet_info_.source_address); |
| } |
| |
| if (!default_path_.peer_address.IsInitialized()) { |
| const QuicSocketAddress effective_peer_addr = |
| GetEffectivePeerAddressFromCurrentPacket(); |
| |
| // The default path peer_address must be initialized at the beginning of the |
| // first packet processed(here). If effective_peer_addr is uninitialized, |
| // just set effective_peer_address_ to the direct peer address. |
| default_path_.peer_address = effective_peer_addr.IsInitialized() |
| ? effective_peer_addr |
| : direct_peer_address_; |
| } |
| |
| stats_.bytes_received += packet.length(); |
| ++stats_.packets_received; |
| if (IsDefaultPath(last_received_packet_info_.destination_address, |
| last_received_packet_info_.source_address) && |
| EnforceAntiAmplificationLimit()) { |
| last_received_packet_info_.received_bytes_counted = true; |
| default_path_.bytes_received_before_address_validation += |
| last_received_packet_info_.length; |
| } |
| |
| // Ensure the time coming from the packet reader is within 2 minutes of now. |
| if (std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) > |
| 2 * 60) { |
| QUIC_BUG(quic_bug_10511_21) |
| << "Packet receipt time:" << packet.receipt_time().ToDebuggingValue() |
| << " too far from current time:" |
| << clock_->ApproximateNow().ToDebuggingValue(); |
| } |
| QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: " |
| << packet.receipt_time().ToDebuggingValue() << " from peer " |
| << last_received_packet_info_.source_address; |
| |
| ScopedPacketFlusher flusher(this); |
| if (!framer_.ProcessPacket(packet)) { |
| // If we are unable to decrypt this packet, it might be |
| // because the CHLO or SHLO packet was lost. |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Unable to process packet. Last packet processed: " |
| << last_received_packet_info_.header.packet_number; |
| current_packet_data_ = nullptr; |
| is_current_packet_connectivity_probing_ = false; |
| |
| MaybeProcessCoalescedPackets(); |
| return; |
| } |
| |
| ++stats_.packets_processed; |
| |
| QUIC_DLOG_IF(INFO, active_effective_peer_migration_type_ != NO_CHANGE) |
| << "sent_packet_manager_.GetLargestObserved() = " |
| << sent_packet_manager_.GetLargestObserved() |
| << ", highest_packet_sent_before_effective_peer_migration_ = " |
| << highest_packet_sent_before_effective_peer_migration_; |
| if (!validate_client_addresses_ && |
| active_effective_peer_migration_type_ != NO_CHANGE && |
| sent_packet_manager_.GetLargestObserved().IsInitialized() && |
| (!highest_packet_sent_before_effective_peer_migration_.IsInitialized() || |
| sent_packet_manager_.GetLargestObserved() > |
| highest_packet_sent_before_effective_peer_migration_)) { |
| if (perspective_ == Perspective::IS_SERVER) { |
| OnEffectivePeerMigrationValidated(); |
| } |
| } |
| |
| if (!MaybeProcessCoalescedPackets()) { |
| MaybeProcessUndecryptablePackets(); |
| MaybeSendInResponseToPacket(); |
| } |
| SetPingAlarm(); |
| RetirePeerIssuedConnectionIdsNoLongerOnPath(); |
| current_packet_data_ = nullptr; |
| is_current_packet_connectivity_probing_ = false; |
| } |
| |
| void QuicConnection::OnBlockedWriterCanWrite() { |
| writer_->SetWritable(); |
| OnCanWrite(); |
| } |
| |
| void QuicConnection::OnCanWrite() { |
| if (!connected_) { |
| return; |
| } |
| if (writer_->IsWriteBlocked()) { |
| const std::string error_details = |
| "Writer is blocked while calling OnCanWrite."; |
| QUIC_BUG(quic_bug_10511_22) << ENDPOINT << error_details; |
| CloseConnection(QUIC_INTERNAL_ERROR, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return; |
| } |
| |
| ScopedPacketFlusher flusher(this); |
| |
| WriteQueuedPackets(); |
| const QuicTime ack_timeout = |
| uber_received_packet_manager_.GetEarliestAckTimeout(); |
| if (ack_timeout.IsInitialized() && ack_timeout <= clock_->ApproximateNow()) { |
| // Send an ACK now because either 1) we were write blocked when we last |
| // tried to send an ACK, or 2) both ack alarm and send alarm were set to |
| // go off together. |
| if (SupportsMultiplePacketNumberSpaces()) { |
| SendAllPendingAcks(); |
| } else { |
| SendAck(); |
| } |
| } |
| |
| // Sending queued packets may have caused the socket to become write blocked, |
| // or the congestion manager to prohibit sending. |
| if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { |
| return; |
| } |
| |
| // Tell the session it can write. |
| visitor_->OnCanWrite(); |
| |
| // After the visitor writes, it may have caused the socket to become write |
| // blocked or the congestion manager to prohibit sending, so check again. |
| if (visitor_->WillingAndAbleToWrite() && !send_alarm_->IsSet() && |
| CanWrite(HAS_RETRANSMITTABLE_DATA)) { |
| // We're not write blocked, but some data wasn't written. Register for |
| // 'immediate' resumption so we'll keep writing after other connections. |
| send_alarm_->Set(clock_->ApproximateNow()); |
| } |
| } |
| |
| void QuicConnection::WriteIfNotBlocked() { |
| if (framer().is_processing_packet()) { |
| QUIC_BUG(connection_write_mid_packet_processing) |
| << ENDPOINT << "Tried to write in mid of packet processing"; |
| return; |
| } |
| if (!HandleWriteBlocked()) { |
| OnCanWrite(); |
| } |
| } |
| |
| void QuicConnection::MaybeClearQueuedPacketsOnPathChange() { |
| if (connection_migration_use_new_cid_ && |
| peer_issued_cid_manager_ != nullptr && HasQueuedPackets()) { |
| // Discard packets serialized with the connection ID on the old code path. |
| // It is possible to clear queued packets only if connection ID changes. |
| // However, the case where connection ID is unchanged and queued packets are |
| // non-empty is quite rare. |
| ClearQueuedPackets(); |
| } |
| } |
| |
| void QuicConnection::ReplaceInitialServerConnectionId( |
| const QuicConnectionId& new_server_connection_id) { |
| QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); |
| if (version().HasIetfQuicFrames()) { |
| if (new_server_connection_id.IsEmpty()) { |
| peer_issued_cid_manager_ = nullptr; |
| } else { |
| if (peer_issued_cid_manager_ != nullptr) { |
| QUIC_BUG_IF(quic_bug_12714_22, |
| !peer_issued_cid_manager_->IsConnectionIdActive( |
| default_path_.server_connection_id)) |
| << "Connection ID replaced header is no longer active. old id: " |
| << default_path_.server_connection_id |
| << " new_id: " << new_server_connection_id; |
| peer_issued_cid_manager_->ReplaceConnectionId( |
| default_path_.server_connection_id, new_server_connection_id); |
| } else { |
| peer_issued_cid_manager_ = |
| std::make_unique<QuicPeerIssuedConnectionIdManager>( |
| kMinNumOfActiveConnectionIds, new_server_connection_id, clock_, |
| alarm_factory_, this, context()); |
| } |
| } |
| } |
| default_path_.server_connection_id = new_server_connection_id; |
| packet_creator_.SetServerConnectionId(default_path_.server_connection_id); |
| } |
| |
| void QuicConnection::FindMatchingOrNewClientConnectionIdOrToken( |
| const PathState& default_path, const PathState& alternative_path, |
| const QuicConnectionId& server_connection_id, |
| QuicConnectionId* client_connection_id, |
| absl::optional<StatelessResetToken>* stateless_reset_token) { |
| QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER); |
| if (peer_issued_cid_manager_ == nullptr || |
| server_connection_id == default_path.server_connection_id) { |
| *client_connection_id = default_path.client_connection_id; |
| *stateless_reset_token = default_path.stateless_reset_token; |
| return; |
| } |
| if (server_connection_id == alternative_path_.server_connection_id) { |
| *client_connection_id = alternative_path.client_connection_id; |
| *stateless_reset_token = alternative_path.stateless_reset_token; |
| return; |
| } |
| if (!connection_migration_use_new_cid_) { |
| QUIC_BUG(quic_bug_46004) << "Cannot find matching connection ID."; |
| return; |
| } |
| auto* connection_id_data = |
| peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); |
| if (connection_id_data == nullptr) { |
| return; |
| } |
| *client_connection_id = connection_id_data->connection_id; |
| *stateless_reset_token = connection_id_data->stateless_reset_token; |
| } |
| |
| bool QuicConnection::FindOnPathConnectionIds( |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| QuicConnectionId* client_connection_id, |
| QuicConnectionId* server_connection_id) const { |
| if (IsDefaultPath(self_address, peer_address)) { |
| *client_connection_id = default_path_.client_connection_id, |
| *server_connection_id = default_path_.server_connection_id; |
| return true; |
| } |
| if (IsAlternativePath(self_address, peer_address)) { |
| *client_connection_id = alternative_path_.client_connection_id, |
| *server_connection_id = alternative_path_.server_connection_id; |
| return true; |
| } |
| return false; |
| } |
| |
| void QuicConnection::SetDefaultPathState(PathState new_path_state) { |
| default_path_ = std::move(new_path_state); |
| if (connection_migration_use_new_cid_) { |
| packet_creator_.SetClientConnectionId(default_path_.client_connection_id); |
| packet_creator_.SetServerConnectionId(default_path_.server_connection_id); |
| } |
| } |
| |
| bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { |
| if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames() && |
| direct_peer_address_.IsInitialized() && |
| last_received_packet_info_.source_address.IsInitialized() && |
| direct_peer_address_ != last_received_packet_info_.source_address && |
| !visitor_->IsKnownServerAddress( |
| last_received_packet_info_.source_address)) { |
| // TODO(haoyuewang) Revisit this when preferred_address transport parameter |
| // is used on the client side. |
| // Discard packets received from unseen server addresses. |
| return false; |
| } |
| |
| if (perspective_ == Perspective::IS_SERVER && |
| default_path_.self_address.IsInitialized() && |
| last_received_packet_info_.destination_address.IsInitialized() && |
| default_path_.self_address != |
| last_received_packet_info_.destination_address) { |
| // Allow change between pure IPv4 and equivalent mapped IPv4 address. |
| if (default_path_.self_address.port() != |
| last_received_packet_info_.destination_address.port() || |
| default_path_.self_address.host().Normalized() != |
| last_received_packet_info_.destination_address.host() |
| .Normalized()) { |
| if (!visitor_->AllowSelfAddressChange()) { |
| const std::string error_details = absl::StrCat( |
| "Self address migration is not supported at the server, current " |
| "address: ", |
| default_path_.self_address.ToString(), |
| ", received packet address: ", |
| last_received_packet_info_.destination_address.ToString(), |
| ", size: ", last_received_packet_info_.length, |
| ", packet number: ", header.packet_number.ToString(), |
| ", encryption level: ", |
| EncryptionLevelToString( |
| last_received_packet_info_.decrypted_level)); |
| QUIC_LOG_EVERY_N_SEC(INFO, 100) << error_details; |
| QUIC_CODE_COUNT(quic_dropped_packets_with_changed_server_address); |
| return false; |
| } |
| } |
| default_path_.self_address = last_received_packet_info_.destination_address; |
| } |
| |
| if (PacketCanReplaceServerConnectionId(header, perspective_) && |
| default_path_.server_connection_id != header.source_connection_id) { |
| QUICHE_DCHECK_EQ(header.long_packet_type, INITIAL); |
| if (server_connection_id_replaced_by_initial_) { |
| QUIC_DLOG(ERROR) << ENDPOINT << "Refusing to replace connection ID " |
| << default_path_.server_connection_id << " with " |
| << header.source_connection_id; |
| return false; |
| } |
| server_connection_id_replaced_by_initial_ = true; |
| QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID " |
| << default_path_.server_connection_id << " with " |
| << header.source_connection_id; |
| if (!original_destination_connection_id_.has_value()) { |
| original_destination_connection_id_ = default_path_.server_connection_id; |
| } |
| ReplaceInitialServerConnectionId(header.source_connection_id); |
| } |
| |
| if (!ValidateReceivedPacketNumber(header.packet_number)) { |
| return false; |
| } |
| |
| if (!version_negotiated_) { |
| if (perspective_ == Perspective::IS_CLIENT) { |
| QUICHE_DCHECK(!header.version_flag || header.form != GOOGLE_QUIC_PACKET); |
| if (!version().HasIetfInvariantHeader()) { |
| // If the client gets a packet without the version flag from the server |
| // it should stop sending version since the version negotiation is done. |
| // IETF QUIC stops sending version once encryption level switches to |
| // forward secure. |
| packet_creator_.StopSendingVersion(); |
| } |
| version_negotiated_ = true; |
| OnSuccessfulVersionNegotiation(); |
| } |
| } |
| |
| if (last_received_packet_info_.length > largest_received_packet_size_) { |
| largest_received_packet_size_ = last_received_packet_info_.length; |
| } |
| |
| if (perspective_ == Perspective::IS_SERVER && |
| encryption_level_ == ENCRYPTION_INITIAL && |
| last_received_packet_info_.length > packet_creator_.max_packet_length()) { |
| if (GetQuicFlag(quic_use_lower_server_response_mtu_for_test)) { |
| SetMaxPacketLength( |
| std::min(last_received_packet_info_.length, QuicByteCount(1250))); |
| } else { |
| SetMaxPacketLength(last_received_packet_info_.length); |
| } |
| } |
| return true; |
| } |
| |
| bool QuicConnection::ValidateReceivedPacketNumber( |
| QuicPacketNumber packet_number) { |
| // If this packet has already been seen, or the sender has told us that it |
| // will not be retransmitted, then stop processing the packet. |
| if (!uber_received_packet_manager_.IsAwaitingPacket( |
| last_received_packet_info_.decrypted_level, packet_number)) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Packet " << packet_number |
| << " no longer being waited for at level " |
| << static_cast<int>( |
| last_received_packet_info_.decrypted_level) |
| << ". Discarding."; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnDuplicatePacket(packet_number); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void QuicConnection::WriteQueuedPackets() { |
| QUICHE_DCHECK(!writer_->IsWriteBlocked()); |
| |
| QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsBeforeWrite", |
| buffered_packets_.size(), 1, 1000, 50, ""); |
| |
| while (!buffered_packets_.empty()) { |
| if (HandleWriteBlocked()) { |
| break; |
| } |
| const BufferedPacket& packet = buffered_packets_.front(); |
| WriteResult result = writer_->WritePacket( |
| packet.data.get(), packet.length, packet.self_address.host(), |
| packet.peer_address, per_packet_options_); |
| QUIC_DVLOG(1) << ENDPOINT << "Sending buffered packet, result: " << result; |
| if (IsMsgTooBig(writer_, result) && packet.length > long_term_mtu_) { |
| // When MSG_TOO_BIG is returned, the system typically knows what the |
| // actual MTU is, so there is no need to probe further. |
| // TODO(wub): Reduce max packet size to a safe default, or the actual MTU. |
| mtu_discoverer_.Disable(); |
| mtu_discovery_alarm_->Cancel(); |
| buffered_packets_.pop_front(); |
| continue; |
| } |
| if (IsWriteError(result.status)) { |
| OnWriteError(result.error_code); |
| break; |
| } |
| if (result.status == WRITE_STATUS_OK || |
| result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) { |
| buffered_packets_.pop_front(); |
| } |
| if (IsWriteBlockedStatus(result.status)) { |
| visitor_->OnWriteBlocked(); |
| break; |
| } |
| } |
| } |
| |
| void QuicConnection::MarkZeroRttPacketsForRetransmission(int reject_reason) { |
| sent_packet_manager_.MarkZeroRttPacketsForRetransmission(); |
| if (debug_visitor_ != nullptr && version().UsesTls()) { |
| debug_visitor_->OnZeroRttRejected(reject_reason); |
| } |
| } |
| |
| void QuicConnection::NeuterUnencryptedPackets() { |
| sent_packet_manager_.NeuterUnencryptedPackets(); |
| // This may have changed the retransmission timer, so re-arm it. |
| SetRetransmissionAlarm(); |
| if (default_enable_5rto_blackhole_detection_) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_enable_5rto_blackhole_detection2, |
| 1, 3); |
| // Consider this as forward progress since this is called when initial key |
| // gets discarded (or previous unencrypted data is not needed anymore). |
| OnForwardProgressMade(); |
| } |
| if (SupportsMultiplePacketNumberSpaces()) { |
| // Stop sending ack of initial packet number space. |
| uber_received_packet_manager_.ResetAckStates(ENCRYPTION_INITIAL); |
| // Re-arm ack alarm. |
| ack_alarm_->Update(uber_received_packet_manager_.GetEarliestAckTimeout(), |
| kAlarmGranularity); |
| } |
| } |
| |
| bool QuicConnection::ShouldGeneratePacket( |
| HasRetransmittableData retransmittable, IsHandshake handshake) { |
| QUICHE_DCHECK(handshake != IS_HANDSHAKE || |
| QuicVersionUsesCryptoFrames(transport_version())) |
| << ENDPOINT |
| << "Handshake in STREAM frames should not check ShouldGeneratePacket"; |
| if (peer_issued_cid_manager_ != nullptr && |
| packet_creator_.GetDestinationConnectionId().IsEmpty()) { |
| QUICHE_DCHECK(version().HasIetfQuicFrames()); |
| QUIC_CODE_COUNT(quic_generate_packet_blocked_by_no_connection_id); |
| QUIC_BUG_IF(quic_bug_90265_1, perspective_ == Perspective::IS_CLIENT); |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "There is no destination connection ID available to " |
| "generate packet."; |
| return false; |
| } |
| if (IsDefaultPath(default_path_.self_address, |
| packet_creator_.peer_address())) { |
| return CanWrite(retransmittable); |
| } |
| // This is checking on the alternative path with a different peer address. The |
| // self address and the writer used are the same as the default path. In the |
| // case of different self address and writer, writing packet would use a |
| // differnt code path without checking the states of the default writer. |
| return connected_ && !HandleWriteBlocked(); |
| } |
| |
| const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() { |
| if (!ack_frequency_sent_ && sent_packet_manager_.CanSendAckFrequency()) { |
| if (packet_creator_.NextSendingPacketNumber() >= |
| FirstSendingPacketNumber() + kMinReceivedBeforeAckDecimation) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_can_send_ack_frequency, 3, 3); |
| ack_frequency_sent_ = true; |
| auto frame = sent_packet_manager_.GetUpdatedAckFrequencyFrame(); |
| visitor_->SendAckFrequency(frame); |
| } |
| } |
| |
| QuicFrames frames; |
| const bool has_pending_ack = |
| uber_received_packet_manager_ |
| .GetAckTimeout(QuicUtils::GetPacketNumberSpace(encryption_level_)) |
| .IsInitialized(); |
| if (!has_pending_ack && stop_waiting_count_ <= 1) { |
| // No need to send an ACK. |
| return frames; |
| } |
| ResetAckStates(); |
| |
| QUIC_DVLOG(1) << ENDPOINT << "Bundle an ACK opportunistically"; |
| QuicFrame updated_ack_frame = GetUpdatedAckFrame(); |
| QUIC_BUG_IF(quic_bug_12714_23, updated_ack_frame.ack_frame->packets.Empty()) |
| << ENDPOINT << "Attempted to opportunistically bundle an empty " |
| << encryption_level_ << " ACK, " << (has_pending_ack ? "" : "!") |
| << "has_pending_ack, stop_waiting_count_ " << stop_waiting_count_; |
| frames.push_back(updated_ack_frame); |
| |
| if (!no_stop_waiting_frames_) { |
| QuicStopWaitingFrame stop_waiting; |
| PopulateStopWaitingFrame(&stop_waiting); |
| frames.push_back(QuicFrame(stop_waiting)); |
| } |
| return frames; |
| } |
| |
| bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) { |
| if (!connected_) { |
| return false; |
| } |
| |
| if (version().CanSendCoalescedPackets() && |
| framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL) && |
| framer_.is_processing_packet()) { |
| // While we still have initial keys, suppress sending in mid of packet |
| // processing. |
| // TODO(fayang): always suppress sending while in the mid of packet |
| // processing. |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Suppress sending in the mid of packet processing"; |
| return false; |
| } |
| |
| if (fill_coalesced_packet_) { |
| // Try to coalesce packet, only allow to write when creator is on soft max |
| // packet length. Given the next created packet is going to fill current |
| // coalesced packet, do not check amplification factor. |
| return packet_creator_.HasSoftMaxPacketLength(); |
| } |
| |
| if (sent_packet_manager_.pending_timer_transmission_count() > 0) { |
| // Allow sending if there are pending tokens, which occurs when: |
| // 1) firing PTO, |
| // 2) bundling CRYPTO data with ACKs, |
| // 3) coalescing CRYPTO data of higher space. |
| return true; |
| } |
| |
| if (LimitedByAmplificationFactor(packet_creator_.max_packet_length())) { |
| // Server is constrained by the amplification restriction. |
| QUIC_CODE_COUNT(quic_throttled_by_amplification_limit); |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Constrained by amplification restriction to peer address " |
| << default_path_.peer_address << " bytes received " |
| << default_path_.bytes_received_before_address_validation |
| << ", bytes sent" |
| << default_path_.bytes_sent_before_address_validation; |
| ++stats_.num_amplification_throttling; |
| return false; |
| } |
| |
| if (HandleWriteBlocked()) { |
| return false; |
| } |
| |
| // Allow acks and probing frames to be sent immediately. |
| if (retransmittable == NO_RETRANSMITTABLE_DATA) { |
| return true; |
| } |
| // If the send alarm is set, wait for it to fire. |
| if (send_alarm_->IsSet()) { |
| return false; |
| } |
| |
| QuicTime now = clock_->Now(); |
| QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(now); |
| if (delay.IsInfinite()) { |
| send_alarm_->Cancel(); |
| return false; |
| } |
| |
| // Scheduler requires a delay. |
| if (!delay.IsZero()) { |
| if (delay <= release_time_into_future_) { |
| // Required delay is within pace time into future, send now. |
| return true; |
| } |
| // Cannot send packet now because delay is too far in the future. |
| send_alarm_->Update(now + delay, kAlarmGranularity); |
| QUIC_DVLOG(1) << ENDPOINT << "Delaying sending " << delay.ToMilliseconds() |
| << "ms"; |
| return false; |
| } |
| return true; |
| } |
| |
| QuicTime QuicConnection::CalculatePacketSentTime() { |
| const QuicTime now = clock_->Now(); |
| if (!supports_release_time_ || per_packet_options_ == nullptr) { |
| // Don't change the release delay. |
| return now; |
| } |
| |
| auto next_release_time_result = sent_packet_manager_.GetNextReleaseTime(); |
| |
| // Release before |now| is impossible. |
| QuicTime next_release_time = |
| std::max(now, next_release_time_result.release_time); |
| per_packet_options_->release_time_delay = next_release_time - now; |
| per_packet_options_->allow_burst = next_release_time_result.allow_burst; |
| return next_release_time; |
| } |
| |
| bool QuicConnection::WritePacket(SerializedPacket* packet) { |
| if (sent_packet_manager_.GetLargestSentPacket().IsInitialized() && |
| packet->packet_number < sent_packet_manager_.GetLargestSentPacket()) { |
| QUIC_BUG(quic_bug_10511_23) |
| << "Attempt to write packet:" << packet->packet_number |
| << " after:" << sent_packet_manager_.GetLargestSentPacket(); |
| CloseConnection(QUIC_INTERNAL_ERROR, "Packet written out of order.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return true; |
| } |
| const bool is_mtu_discovery = QuicUtils::ContainsFrameType( |
| packet->nonretransmittable_frames, MTU_DISCOVERY_FRAME); |
| const SerializedPacketFate fate = packet->fate; |
| // Termination packets are encrypted and saved, so don't exit early. |
| QuicErrorCode error_code = QUIC_NO_ERROR; |
| const bool is_termination_packet = IsTerminationPacket(*packet, &error_code); |
| QuicPacketNumber packet_number = packet->packet_number; |
| QuicPacketLength encrypted_length = packet->encrypted_length; |
| // Termination packets are eventually owned by TimeWaitListManager. |
| // Others are deleted at the end of this call. |
| if (is_termination_packet) { |
| if (termination_packets_ == nullptr) { |
| termination_packets_.reset( |
| new std::vector<std::unique_ptr<QuicEncryptedPacket>>); |
| } |
| // Copy the buffer so it's owned in the future. |
| char* buffer_copy = CopyBuffer(*packet); |
| termination_packets_->emplace_back( |
| new QuicEncryptedPacket(buffer_copy, encrypted_length, true)); |
| if (error_code == QUIC_SILENT_IDLE_TIMEOUT) { |
| QUICHE_DCHECK_EQ(Perspective::IS_SERVER, perspective_); |
| // TODO(fayang): populate histogram indicating the time elapsed from this |
| // connection gets closed to following client packets get received. |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Added silent connection close to termination packets, " |
| "num of termination packets: " |
| << termination_packets_->size(); |
| return true; |
| } |
| } |
| |
| QUICHE_DCHECK_LE(encrypted_length, kMaxOutgoingPacketSize); |
| QUICHE_DCHECK(is_mtu_discovery || |
| encrypted_length <= packet_creator_.max_packet_length()) |
| << " encrypted_length=" << encrypted_length |
| << " > packet_creator max_packet_length=" |
| << packet_creator_.max_packet_length(); |
| QUIC_DVLOG(1) << ENDPOINT << "Sending packet " << packet_number << " : " |
| << (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA |
| ? "data bearing " |
| : " ack or probing only ") |
| << ", encryption level: " << packet->encryption_level |
| << ", encrypted length:" << encrypted_length |
| << ", fate: " << fate << " to peer " << packet->peer_address; |
| QUIC_DVLOG(2) << ENDPOINT << packet->encryption_level << " packet number " |
| << packet_number << " of length " << encrypted_length << ": " |
| << std::endl |
| << quiche::QuicheTextUtils::HexDump(absl::string_view( |
| packet->encrypted_buffer, encrypted_length)); |
| |
| // Measure the RTT from before the write begins to avoid underestimating the |
| // min_rtt_, especially in cases where the thread blocks or gets swapped out |
| // during the WritePacket below. |
| QuicTime packet_send_time = CalculatePacketSentTime(); |
| WriteResult result(WRITE_STATUS_OK, encrypted_length); |
| QuicSocketAddress send_to_address = packet->peer_address; |
| // Self address is always the default self address on this code path. |
| const bool send_on_current_path = send_to_address == peer_address(); |
| if (!send_on_current_path) { |
| QUIC_BUG_IF(quic_send_non_probing_frames_on_alternative_path, |
| ContainsNonProbingFrame(*packet)) |
| << "Packet " << packet->packet_number |
| << " with non-probing frames was sent on alternative path: " |
| "nonretransmittable_frames: " |
| << QuicFramesToString(packet->nonretransmittable_frames) |
| << " retransmittable_frames: " |
| << QuicFramesToString(packet->retransmittable_frames); |
| } |
| switch (fate) { |
| case DISCARD: |
| ++stats_.packets_discarded; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPacketDiscarded(*packet); |
| } |
| return true; |
| case COALESCE: |
| QUIC_BUG_IF(quic_bug_12714_24, |
| !version().CanSendCoalescedPackets() || coalescing_done_); |
| if (!coalesced_packet_.MaybeCoalescePacket( |
| *packet, self_address(), send_to_address, |
| helper_->GetStreamSendBufferAllocator(), |
| packet_creator_.max_packet_length())) { |
| // Failed to coalesce packet, flush current coalesced packet. |
| if (!FlushCoalescedPacket()) { |
| QUIC_BUG_IF(quic_connection_connected_after_flush_coalesced_failure, |
| connected_) |
| << "QUIC connection is still connected after failing to flush " |
| "coalesced packet."; |
| // Failed to flush coalesced packet, write error has been handled. |
| return false; |
| } |
| if (!coalesced_packet_.MaybeCoalescePacket( |
| *packet, self_address(), send_to_address, |
| helper_->GetStreamSendBufferAllocator(), |
| packet_creator_.max_packet_length())) { |
| // Failed to coalesce packet even it is the only packet, raise a write |
| // error. |
| QUIC_DLOG(ERROR) << ENDPOINT << "Failed to coalesce packet"; |
| result.error_code = WRITE_STATUS_FAILED_TO_COALESCE_PACKET; |
| break; |
| } |
| } |
| if (coalesced_packet_.length() < coalesced_packet_.max_packet_length()) { |
| QUIC_DVLOG(1) << ENDPOINT << "Trying to set soft max packet length to " |
| << coalesced_packet_.max_packet_length() - |
| coalesced_packet_.length(); |
| packet_creator_.SetSoftMaxPacketLength( |
| coalesced_packet_.max_packet_length() - coalesced_packet_.length()); |
| } |
| break; |
| case BUFFER: |
| QUIC_DVLOG(1) << ENDPOINT << "Adding packet: " << packet->packet_number |
| << " to buffered packets"; |
| buffered_packets_.emplace_back(*packet, self_address(), send_to_address); |
| break; |
| case SEND_TO_WRITER: |
| // Stop using coalescer from now on. |
| coalescing_done_ = true; |
| // At this point, packet->release_encrypted_buffer is either nullptr, |
| // meaning |packet->encrypted_buffer| is a stack buffer, or not-nullptr, |
| /// meaning it's a writer-allocated buffer. Note that connectivity probing |
| // packets do not use this function, so setting release_encrypted_buffer |
| // to nullptr will not cause probing packets to be leaked. |
| // |
| // writer_->WritePacket transfers buffer ownership back to the writer. |
| packet->release_encrypted_buffer = nullptr; |
| result = writer_->WritePacket(packet->encrypted_buffer, encrypted_length, |
| self_address().host(), send_to_address, |
| per_packet_options_); |
| // This is a work around for an issue with linux UDP GSO batch writers. |
| // When sending a GSO packet with 2 segments, if the first segment is |
| // larger than the path MTU, instead of EMSGSIZE, the linux kernel returns |
| // EINVAL, which translates to WRITE_STATUS_ERROR and causes conneciton to |
| // be closed. By manually flush the writer here, the MTU probe is sent in |
| // a normal(non-GSO) packet, so the kernel can return EMSGSIZE and we will |
| // not close the connection. |
| if (is_mtu_discovery && writer_->IsBatchMode()) { |
| result = writer_->Flush(); |
| } |
| break; |
| case LEGACY_VERSION_ENCAPSULATE: { |
| QUICHE_DCHECK(!is_mtu_discovery); |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| QUICHE_DCHECK_EQ(packet->encryption_level, ENCRYPTION_INITIAL); |
| QUICHE_DCHECK(legacy_version_encapsulation_enabled_); |
| QUICHE_DCHECK(legacy_version_encapsulation_in_progress_); |
| QuicPacketLength encapsulated_length = |
| QuicLegacyVersionEncapsulator::Encapsulate( |
| legacy_version_encapsulation_sni_, |
| absl::string_view(packet->encrypted_buffer, |
| packet->encrypted_length), |
| default_path_.server_connection_id, framer_.creation_time(), |
| GetLimitedMaxPacketSize(long_term_mtu_), |
| const_cast<char*>(packet->encrypted_buffer)); |
| if (encapsulated_length != 0) { |
| stats_.sent_legacy_version_encapsulated_packets++; |
| packet->encrypted_length = encapsulated_length; |
| encrypted_length = encapsulated_length; |
| QUIC_DVLOG(2) |
| << ENDPOINT |
| << "Successfully performed Legacy Version Encapsulation on " |
| << packet->encryption_level << " packet number " << packet_number |
| << " of length " << encrypted_length << ": " << std::endl |
| << quiche::QuicheTextUtils::HexDump(absl::string_view( |
| packet->encrypted_buffer, encrypted_length)); |
| } else { |
| QUIC_BUG(quic_bug_10511_24) |
| << ENDPOINT << "Failed to perform Legacy Version Encapsulation on " |
| << packet->encryption_level << " packet number " << packet_number |
| << " of length " << encrypted_length; |
| } |
| if (!buffered_packets_.empty() || HandleWriteBlocked()) { |
| // Buffer the packet. |
| buffered_packets_.emplace_back(*packet, self_address(), |
| send_to_address); |
| } else { // Send the packet to the writer. |
| // writer_->WritePacket transfers buffer ownership back to the writer. |
| packet->release_encrypted_buffer = nullptr; |
| result = writer_->WritePacket(packet->encrypted_buffer, |
| encrypted_length, self_address().host(), |
| send_to_address, per_packet_options_); |
| } |
| } break; |
| default: |
| QUICHE_DCHECK(false); |
| break; |
| } |
| |
| QUIC_HISTOGRAM_ENUM( |
| "QuicConnection.WritePacketStatus", result.status, |
| WRITE_STATUS_NUM_VALUES, |
| "Status code returned by writer_->WritePacket() in QuicConnection."); |
| |
| if (IsWriteBlockedStatus(result.status)) { |
| // Ensure the writer is still write blocked, otherwise QUIC may continue |
| // trying to write when it will not be able to. |
| QUICHE_DCHECK(writer_->IsWriteBlocked()); |
| visitor_->OnWriteBlocked(); |
| // If the socket buffers the data, then the packet should not |
| // be queued and sent again, which would result in an unnecessary |
| // duplicate packet being sent. The helper must call OnCanWrite |
| // when the write completes, and OnWriteError if an error occurs. |
| if (result.status != WRITE_STATUS_BLOCKED_DATA_BUFFERED) { |
| QUIC_DVLOG(1) << ENDPOINT << "Adding packet: " << packet->packet_number |
| << " to buffered packets"; |
| buffered_packets_.emplace_back(*packet, self_address(), send_to_address); |
| } |
| } |
| |
| // In some cases, an MTU probe can cause EMSGSIZE. This indicates that the |
| // MTU discovery is permanently unsuccessful. |
| if (IsMsgTooBig(writer_, result)) { |
| if (is_mtu_discovery) { |
| // When MSG_TOO_BIG is returned, the system typically knows what the |
| // actual MTU is, so there is no need to probe further. |
| // TODO(wub): Reduce max packet size to a safe default, or the actual MTU. |
| QUIC_DVLOG(1) << ENDPOINT |
| << " MTU probe packet too big, size:" << encrypted_length |
| << ", long_term_mtu_:" << long_term_mtu_; |
| mtu_discoverer_.Disable(); |
| mtu_discovery_alarm_->Cancel(); |
| // The write failed, but the writer is not blocked, so return true. |
| return true; |
| } |
| if (!send_on_current_path) { |
| // Only handle MSG_TOO_BIG as error on current path. |
| return true; |
| } |
| } |
| |
| if (IsWriteError(result.status)) { |
| QUIC_LOG_FIRST_N(ERROR, 10) |
| << ENDPOINT << "Failed writing packet " << packet_number << " of " |
| << encrypted_length << " bytes from " << self_address().host() << " to " |
| << send_to_address << ", with error code " << result.error_code |
| << ". long_term_mtu_:" << long_term_mtu_ |
| << ", previous_validated_mtu_:" << previous_validated_mtu_ |
| << ", max_packet_length():" << max_packet_length() |
| << ", is_mtu_discovery:" << is_mtu_discovery; |
| if (MaybeRevertToPreviousMtu()) { |
| return true; |
| } |
| |
| OnWriteError(result.error_code); |
| return false; |
| } |
| |
| if (result.status == WRITE_STATUS_OK) { |
| // packet_send_time is the ideal send time, if allow_burst is true, writer |
| // may have sent it earlier than that. |
| packet_send_time = packet_send_time + result.send_time_offset; |
| } |
| |
| if (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA && |
| !is_termination_packet) { |
| // Start blackhole/path degrading detections if the sent packet is not |
| // termination packet and contains retransmittable data. |
| // Do not restart detection if detection is in progress indicating no |
| // forward progress has been made since last event (i.e., packet was sent |
| // or new packets were acknowledged). |
| if (!blackhole_detector_.IsDetectionInProgress()) { |
| // Try to start detections if no detection in progress. This could |
| // because either both detections are inactive when sending last packet |
| // or this connection just gets out of quiescence. |
| blackhole_detector_.RestartDetection(GetPathDegradingDeadline(), |
| GetNetworkBlackholeDeadline(), |
| GetPathMtuReductionDeadline()); |
| } |
| idle_network_detector_.OnPacketSent(packet_send_time, |
| sent_packet_manager_.GetPtoDelay()); |
| } |
| |
| MaybeSetMtuAlarm(packet_number); |
| QUIC_DVLOG(1) << ENDPOINT << "time we began writing last sent packet: " |
| << packet_send_time.ToDebuggingValue(); |
| |
| if (IsDefaultPath(default_path_.self_address, send_to_address)) { |
| if (EnforceAntiAmplificationLimit()) { |
| // Include bytes sent even if they are not in flight. |
| default_path_.bytes_sent_before_address_validation += encrypted_length; |
| } |
| } else { |
| MaybeUpdateBytesSentToAlternativeAddress(send_to_address, encrypted_length); |
| } |
| |
| // Do not measure rtt of this packet if it's not sent on current path. |
| QUIC_DLOG_IF(INFO, !send_on_current_path) |
| << ENDPOINT << " Sent packet " << packet->packet_number |
| << " on a different path with remote address " << send_to_address |
| << " while current path has peer address " << peer_address(); |
| const bool in_flight = sent_packet_manager_.OnPacketSent( |
| packet, packet_send_time, packet->transmission_type, |
| IsRetransmittable(*packet), /*measure_rtt=*/send_on_current_path); |
| QUIC_BUG_IF(quic_bug_12714_25, |
| perspective_ == Perspective::IS_SERVER && |
| default_enable_5rto_blackhole_detection_ && |
| blackhole_detector_.IsDetectionInProgress() && |
| !sent_packet_manager_.HasInFlightPackets()) |
| << ENDPOINT |
| << "Trying to start blackhole detection without no bytes in flight"; |
| |
| if (debug_visitor_ != nullptr) { |
| if (sent_packet_manager_.unacked_packets().empty()) { |
| QUIC_BUG(quic_bug_10511_25) |
| << "Unacked map is empty right after packet is sent"; |
| } else { |
| debug_visitor_->OnPacketSent( |
| packet->packet_number, packet->encrypted_length, |
| packet->has_crypto_handshake, packet->transmission_type, |
| packet->encryption_level, |
| sent_packet_manager_.unacked_packets() |
| .rbegin() |
| ->retransmittable_frames, |
| packet->nonretransmittable_frames, packet_send_time); |
| } |
| } |
| if (packet->encryption_level == ENCRYPTION_HANDSHAKE) { |
| handshake_packet_sent_ = true; |
| } |
| |
| if (packet->encryption_level == ENCRYPTION_FORWARD_SECURE) { |
| if (!lowest_packet_sent_in_current_key_phase_.IsInitialized()) { |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "lowest_packet_sent_in_current_key_phase_ = " |
| << packet_number; |
| lowest_packet_sent_in_current_key_phase_ = packet_number; |
| } |
| if (!is_termination_packet && |
| MaybeHandleAeadConfidentialityLimits(*packet)) { |
| return true; |
| } |
| } |
| if (in_flight || !retransmission_alarm_->IsSet()) { |
| SetRetransmissionAlarm(); |
| } |
| SetPingAlarm(); |
| RetirePeerIssuedConnectionIdsNoLongerOnPath(); |
| |
| // The packet number length must be updated after OnPacketSent, because it |
| // may change the packet number length in packet. |
| packet_creator_.UpdatePacketNumberLength( |
| sent_packet_manager_.GetLeastPacketAwaitedByPeer(encryption_level_), |
| sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length())); |
| |
| stats_.bytes_sent += encrypted_length; |
| ++stats_.packets_sent; |
| |
| QuicByteCount bytes_not_retransmitted = |
| packet->bytes_not_retransmitted.value_or(0); |
| if (packet->transmission_type != NOT_RETRANSMISSION) { |
| if (static_cast<uint64_t>(encrypted_length) < bytes_not_retransmitted) { |
| QUIC_BUG(quic_packet_bytes_written_lt_bytes_not_retransmitted) |
| << "Total bytes written to the packet should be larger than the " |
| "bytes in not-retransmitted frames. Bytes written: " |
| << encrypted_length |
| << ", bytes not retransmitted: " << bytes_not_retransmitted; |
| } else { |
| // bytes_retransmitted includes packet's headers and encryption |
| // overhead. |
| stats_.bytes_retransmitted += |
| (encrypted_length - bytes_not_retransmitted); |
| } |
| ++stats_.packets_retransmitted; |
| } |
| |
| return true; |
| } |
| |
| bool QuicConnection::MaybeHandleAeadConfidentialityLimits( |
| const SerializedPacket& packet) { |
| if (!version().UsesTls()) { |
| return false; |
| } |
| |
| if (packet.encryption_level != ENCRYPTION_FORWARD_SECURE) { |
| QUIC_BUG(quic_bug_12714_26) |
| << "MaybeHandleAeadConfidentialityLimits called on non 1-RTT packet"; |
| return false; |
| } |
| if (!lowest_packet_sent_in_current_key_phase_.IsInitialized()) { |
| QUIC_BUG(quic_bug_10511_26) |
| << "lowest_packet_sent_in_current_key_phase_ must be initialized " |
| "before calling MaybeHandleAeadConfidentialityLimits"; |
| return false; |
| } |
| |
| // Calculate the number of packets encrypted from the packet number, which is |
| // simpler than keeping another counter. The packet number space may be |
| // sparse, so this might overcount, but doing a key update earlier than |
| // necessary would only improve security and has negligible cost. |
| if (packet.packet_number < lowest_packet_sent_in_current_key_phase_) { |
| const std::string error_details = |
| absl::StrCat("packet_number(", packet.packet_number.ToString(), |
| ") < lowest_packet_sent_in_current_key_phase_ (", |
| lowest_packet_sent_in_current_key_phase_.ToString(), ")"); |
| QUIC_BUG(quic_bug_10511_27) << error_details; |
| CloseConnection(QUIC_INTERNAL_ERROR, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return true; |
| } |
| const QuicPacketCount num_packets_encrypted_in_current_key_phase = |
| packet.packet_number - lowest_packet_sent_in_current_key_phase_ + 1; |
| |
| const QuicPacketCount confidentiality_limit = |
| framer_.GetOneRttEncrypterConfidentialityLimit(); |
| |
| // Attempt to initiate a key update before reaching the AEAD |
| // confidentiality limit when the number of packets sent in the current |
| // key phase gets within |kKeyUpdateConfidentialityLimitOffset| packets of |
| // the limit, unless overridden by |
| // FLAGS_quic_key_update_confidentiality_limit. |
| constexpr QuicPacketCount kKeyUpdateConfidentialityLimitOffset = 1000; |
| QuicPacketCount key_update_limit = 0; |
| if (confidentiality_limit > kKeyUpdateConfidentialityLimitOffset) { |
| key_update_limit = |
| confidentiality_limit - kKeyUpdateConfidentialityLimitOffset; |
| } |
| const QuicPacketCount key_update_limit_override = |
| GetQuicFlag(quic_key_update_confidentiality_limit); |
| if (key_update_limit_override) { |
| key_update_limit = key_update_limit_override; |
| } |
| |
| QUIC_DVLOG(2) << ENDPOINT << "Checking AEAD confidentiality limits: " |
| << "num_packets_encrypted_in_current_key_phase=" |
| << num_packets_encrypted_in_current_key_phase |
| << " key_update_limit=" << key_update_limit |
| << " confidentiality_limit=" << confidentiality_limit |
| << " IsKeyUpdateAllowed()=" << IsKeyUpdateAllowed(); |
| |
| if (num_packets_encrypted_in_current_key_phase >= confidentiality_limit) { |
| // Reached the confidentiality limit without initiating a key update, |
| // must close the connection. |
| const std::string error_details = absl::StrCat( |
| "encrypter confidentiality limit reached: " |
| "num_packets_encrypted_in_current_key_phase=", |
| num_packets_encrypted_in_current_key_phase, |
| " key_update_limit=", key_update_limit, |
| " confidentiality_limit=", confidentiality_limit, |
| " IsKeyUpdateAllowed()=", IsKeyUpdateAllowed()); |
| CloseConnection(QUIC_AEAD_LIMIT_REACHED, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return true; |
| } |
| |
| if (IsKeyUpdateAllowed() && |
| num_packets_encrypted_in_current_key_phase >= key_update_limit) { |
| // Approaching the confidentiality limit, initiate key update so that |
| // the next set of keys will be ready for the next packet before the |
| // limit is reached. |
| KeyUpdateReason reason = KeyUpdateReason::kLocalAeadConfidentialityLimit; |
| if (key_update_limit_override) { |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "reached FLAGS_quic_key_update_confidentiality_limit, " |
| "initiating key update: " |
| << "num_packets_encrypted_in_current_key_phase=" |
| << num_packets_encrypted_in_current_key_phase |
| << " key_update_limit=" << key_update_limit |
| << " confidentiality_limit=" << confidentiality_limit; |
| reason = KeyUpdateReason::kLocalKeyUpdateLimitOverride; |
| } else { |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "approaching AEAD confidentiality limit, " |
| "initiating key update: " |
| << "num_packets_encrypted_in_current_key_phase=" |
| << num_packets_encrypted_in_current_key_phase |
| << " key_update_limit=" << key_update_limit |
| << " confidentiality_limit=" << confidentiality_limit; |
| } |
| InitiateKeyUpdate(reason); |
| } |
| |
| return false; |
| } |
| |
| void QuicConnection::FlushPackets() { |
| if (!connected_) { |
| return; |
| } |
| |
| if (!writer_->IsBatchMode()) { |
| return; |
| } |
| |
| if (HandleWriteBlocked()) { |
| QUIC_DLOG(INFO) << ENDPOINT << "FlushPackets called while blocked."; |
| return; |
| } |
| |
| WriteResult result = writer_->Flush(); |
| |
| QUIC_HISTOGRAM_ENUM("QuicConnection.FlushPacketStatus", result.status, |
| WRITE_STATUS_NUM_VALUES, |
| "Status code returned by writer_->Flush() in " |
| "QuicConnection::FlushPackets."); |
| |
| if (HandleWriteBlocked()) { |
| QUICHE_DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status) |
| << "Unexpected flush result:" << result; |
| QUIC_DLOG(INFO) << ENDPOINT << "Write blocked in FlushPackets."; |
| return; |
| } |
| |
| if (IsWriteError(result.status) && !MaybeRevertToPreviousMtu()) { |
| OnWriteError(result.error_code); |
| } |
| } |
| |
| bool QuicConnection::IsMsgTooBig(const QuicPacketWriter* writer, |
| const WriteResult& result) { |
| absl::optional<int> writer_error_code = writer->MessageTooBigErrorCode(); |
| return (result.status == WRITE_STATUS_MSG_TOO_BIG) || |
| (writer_error_code.has_value() && IsWriteError(result.status) && |
| result.error_code == *writer_error_code); |
| } |
| |
| bool QuicConnection::ShouldDiscardPacket(EncryptionLevel encryption_level) { |
| if (!connected_) { |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Not sending packet as connection is disconnected."; |
| return true; |
| } |
| |
| if (encryption_level_ == ENCRYPTION_FORWARD_SECURE && |
| encryption_level == ENCRYPTION_INITIAL) { |
| // Drop packets that are NULL encrypted since the peer won't accept them |
| // anymore. |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Dropping NULL encrypted packet since the connection is " |
| "forward secure."; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| QuicTime QuicConnection::GetPathMtuReductionDeadline() const { |
| if (previous_validated_mtu_ == 0) { |
| return QuicTime::Zero(); |
| } |
| QuicTime::Delta delay = sent_packet_manager_.GetMtuReductionDelay( |
| num_rtos_for_blackhole_detection_); |
| if (delay.IsZero()) { |
| return QuicTime::Zero(); |
| } |
| return clock_->ApproximateNow() + delay; |
| } |
| |
| bool QuicConnection::MaybeRevertToPreviousMtu() { |
| if (previous_validated_mtu_ == 0) { |
| return false; |
| } |
| |
| SetMaxPacketLength(previous_validated_mtu_); |
| mtu_discoverer_.Disable(); |
| mtu_discovery_alarm_->Cancel(); |
| previous_validated_mtu_ = 0; |
| return true; |
| } |
| |
| void QuicConnection::OnWriteError(int error_code) { |
| if (write_error_occurred_) { |
| // A write error already occurred. The connection is being closed. |
| return; |
| } |
| write_error_occurred_ = true; |
| |
| const std::string error_details = absl::StrCat( |
| "Write failed with error: ", error_code, " (", strerror(error_code), ")"); |
| QUIC_LOG_FIRST_N(ERROR, 2) << ENDPOINT << error_details; |
| absl::optional<int> writer_error_code = writer_->MessageTooBigErrorCode(); |
| if (writer_error_code.has_value() && error_code == *writer_error_code) { |
| CloseConnection(QUIC_PACKET_WRITE_ERROR, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return; |
| } |
| // We can't send an error as the socket is presumably borked. |
| if (version().HasIetfInvariantHeader()) { |
| QUIC_CODE_COUNT(quic_tear_down_local_connection_on_write_error_ietf); |
| } else { |
| QUIC_CODE_COUNT(quic_tear_down_local_connection_on_write_error_non_ietf); |
| } |
| CloseConnection(QUIC_PACKET_WRITE_ERROR, error_details, |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| } |
| |
| QuicPacketBuffer QuicConnection::GetPacketBuffer() { |
| if (version().CanSendCoalescedPackets() && !coalescing_done_) { |
| // Do not use writer's packet buffer for coalesced packets which may |
| // contain multiple QUIC packets. |
| return {nullptr, nullptr}; |
| } |
| return writer_->GetNextWriteLocation(self_address().host(), peer_address()); |
| } |
| |
| void QuicConnection::OnSerializedPacket(SerializedPacket serialized_packet) { |
| if (serialized_packet.encrypted_buffer == nullptr) { |
| // We failed to serialize the packet, so close the connection. |
| // Specify that the close is silent, that no packet be sent, so no infinite |
| // loop here. |
| // TODO(ianswett): This is actually an internal error, not an |
| // encryption failure. |
| if (version().HasIetfInvariantHeader()) { |
| QUIC_CODE_COUNT( |
| quic_tear_down_local_connection_on_serialized_packet_ietf); |
| } else { |
| QUIC_CODE_COUNT( |
| quic_tear_down_local_connection_on_serialized_packet_non_ietf); |
| } |
| CloseConnection(QUIC_ENCRYPTION_FAILURE, |
| "Serialized packet does not have an encrypted buffer.", |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| return; |
| } |
| |
| if (serialized_packet.retransmittable_frames.empty()) { |
| // Increment consecutive_num_packets_with_no_retransmittable_frames_ if |
| // this packet is a new transmission with no retransmittable frames. |
| ++consecutive_num_packets_with_no_retransmittable_frames_; |
| } else { |
| consecutive_num_packets_with_no_retransmittable_frames_ = 0; |
| } |
| if (retransmittable_on_wire_behavior_ == SEND_FIRST_FORWARD_SECURE_PACKET && |
| first_serialized_one_rtt_packet_ == nullptr && |
| serialized_packet.encryption_level == ENCRYPTION_FORWARD_SECURE) { |
| first_serialized_one_rtt_packet_ = std::make_unique<BufferedPacket>( |
| serialized_packet, self_address(), peer_address()); |
| } |
| SendOrQueuePacket(std::move(serialized_packet)); |
| } |
| |
| void QuicConnection::OnUnrecoverableError(QuicErrorCode error, |
| const std::string& error_details) { |
| // The packet creator or generator encountered an unrecoverable error: tear |
| // down local connection state immediately. |
| if (version().HasIetfInvariantHeader()) { |
| QUIC_CODE_COUNT( |
| quic_tear_down_local_connection_on_unrecoverable_error_ietf); |
| } else { |
| QUIC_CODE_COUNT( |
| quic_tear_down_local_connection_on_unrecoverable_error_non_ietf); |
| } |
| CloseConnection(error, error_details, ConnectionCloseBehavior::SILENT_CLOSE); |
| } |
| |
| void QuicConnection::OnCongestionChange() { |
| visitor_->OnCongestionWindowChange(clock_->ApproximateNow()); |
| |
| // Uses the connection's smoothed RTT. If zero, uses initial_rtt. |
| QuicTime::Delta rtt = sent_packet_manager_.GetRttStats()->smoothed_rtt(); |
| if (rtt.IsZero()) { |
| rtt = sent_packet_manager_.GetRttStats()->initial_rtt(); |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnRttChanged(rtt); |
| } |
| } |
| |
| void QuicConnection::OnPathMtuIncreased(QuicPacketLength packet_size) { |
| if (packet_size > max_packet_length()) { |
| previous_validated_mtu_ = max_packet_length(); |
| SetMaxPacketLength(packet_size); |
| mtu_discoverer_.OnMaxPacketLengthUpdated(previous_validated_mtu_, |
| max_packet_length()); |
| } |
| } |
| |
| std::unique_ptr<QuicSelfIssuedConnectionIdManager> |
| QuicConnection::MakeSelfIssuedConnectionIdManager() { |
| QUICHE_DCHECK((perspective_ == Perspective::IS_CLIENT && |
| !default_path_.client_connection_id.IsEmpty()) || |
| (perspective_ == Perspective::IS_SERVER && |
| !default_path_.server_connection_id.IsEmpty())); |
| return std::make_unique<QuicSelfIssuedConnectionIdManager>( |
| kMinNumOfActiveConnectionIds, |
| perspective_ == Perspective::IS_CLIENT |
| ? default_path_.client_connection_id |
| : default_path_.server_connection_id, |
| clock_, alarm_factory_, this, context(), connection_id_generator_); |
| } |
| |
| void QuicConnection::MaybeSendConnectionIdToClient() { |
| if (perspective_ == Perspective::IS_CLIENT) { |
| return; |
| } |
| QUICHE_DCHECK(self_issued_cid_manager_ != nullptr); |
| self_issued_cid_manager_->MaybeSendNewConnectionIds(); |
| } |
| |
| void QuicConnection::OnHandshakeComplete() { |
| sent_packet_manager_.SetHandshakeConfirmed(); |
| if (connection_migration_use_new_cid_ && |
| perspective_ == Perspective::IS_SERVER && |
| self_issued_cid_manager_ != nullptr) { |
| self_issued_cid_manager_->MaybeSendNewConnectionIds(); |
| } |
| if (send_ack_frequency_on_handshake_completion_ && |
| sent_packet_manager_.CanSendAckFrequency()) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_can_send_ack_frequency, 2, 3); |
| auto ack_frequency_frame = |
| sent_packet_manager_.GetUpdatedAckFrequencyFrame(); |
| // This AckFrequencyFrame is meant to only update the max_ack_delay. Set |
| // packet tolerance to the default value for now. |
| ack_frequency_frame.packet_tolerance = |
| kDefaultRetransmittablePacketsBeforeAck; |
| visitor_->SendAckFrequency(ack_frequency_frame); |
| if (!connected_) { |
| return; |
| } |
| } |
| // This may have changed the retransmission timer, so re-arm it. |
| SetRetransmissionAlarm(); |
| if (default_enable_5rto_blackhole_detection_) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_enable_5rto_blackhole_detection2, |
| 2, 3); |
| OnForwardProgressMade(); |
| } |
| if (!SupportsMultiplePacketNumberSpaces()) { |
| // The client should immediately ack the SHLO to confirm the handshake is |
| // complete with the server. |
| if (perspective_ == Perspective::IS_CLIENT && ack_frame_updated()) { |
| ack_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero()); |
| } |
| return; |
| } |
| // Stop sending ack of handshake packet number space. |
| uber_received_packet_manager_.ResetAckStates(ENCRYPTION_HANDSHAKE); |
| // Re-arm ack alarm. |
| ack_alarm_->Update(uber_received_packet_manager_.GetEarliestAckTimeout(), |
| kAlarmGranularity); |
| } |
| |
| void QuicConnection::MaybeCreateMultiPortPath() { |
| QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_); |
| auto path_context = visitor_->CreateContextForMultiPortPath(); |
| if (!path_context || path_validator_.HasPendingPathValidation()) { |
| return; |
| } |
| auto multi_port_validation_result_delegate = |
| std::make_unique<MultiPortPathValidationResultDelegate>(this); |
| multi_port_probing_alarm_->Cancel(); |
| multi_port_path_context_ = nullptr; |
| ValidatePath(std::move(path_context), |
| std::move(multi_port_validation_result_delegate)); |
| } |
| |
| void QuicConnection::SendOrQueuePacket(SerializedPacket packet) { |
| // The caller of this function is responsible for checking CanWrite(). |
| WritePacket(&packet); |
| } |
| |
| void QuicConnection::OnPingTimeout() { |
| QUICHE_DCHECK(!use_ping_manager_); |
| if (retransmission_alarm_->IsSet() || |
| !visitor_->ShouldKeepConnectionAlive()) { |
| return; |
| } |
| SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData()); |
| } |
| |
| void QuicConnection::SendAck() { |
| QUICHE_DCHECK(!SupportsMultiplePacketNumberSpaces()); |
| QUIC_DVLOG(1) << ENDPOINT << "Sending an ACK proactively"; |
| QuicFrames frames; |
| frames.push_back(GetUpdatedAckFrame()); |
| if (!no_stop_waiting_frames_) { |
| QuicStopWaitingFrame stop_waiting; |
| PopulateStopWaitingFrame(&stop_waiting); |
| frames.push_back(QuicFrame(stop_waiting)); |
| } |
| if (!packet_creator_.FlushAckFrame(frames)) { |
| return; |
| } |
| ResetAckStates(); |
| if (!ShouldBundleRetransmittableFrameWithAck()) { |
| return; |
| } |
| consecutive_num_packets_with_no_retransmittable_frames_ = 0; |
| if (packet_creator_.HasPendingRetransmittableFrames() || |
| visitor_->WillingAndAbleToWrite()) { |
| // There are pending retransmittable frames. |
| return; |
| } |
| |
| visitor_->OnAckNeedsRetransmittableFrame(); |
| } |
| |
| EncryptionLevel QuicConnection::GetEncryptionLevelToSendPingForSpace( |
| PacketNumberSpace space) const { |
| switch (space) { |
| case INITIAL_DATA: |
| return ENCRYPTION_INITIAL; |
| case HANDSHAKE_DATA: |
| return ENCRYPTION_HANDSHAKE; |
| case APPLICATION_DATA: |
| return framer_.GetEncryptionLevelToSendApplicationData(); |
| default: |
| QUICHE_DCHECK(false); |
| return NUM_ENCRYPTION_LEVELS; |
| } |
| } |
| |
| void QuicConnection::OnRetransmissionTimeout() { |
| ScopedRetransmissionTimeoutIndicator indicator(this); |
| #ifndef NDEBUG |
| if (sent_packet_manager_.unacked_packets().empty()) { |
| QUICHE_DCHECK(sent_packet_manager_.handshake_mode_disabled()); |
| QUICHE_DCHECK(!IsHandshakeComplete()); |
| } |
| #endif |
| if (!connected_) { |
| return; |
| } |
| |
| QuicPacketNumber previous_created_packet_number = |
| packet_creator_.packet_number(); |
| const auto retransmission_mode = |
| sent_packet_manager_.OnRetransmissionTimeout(); |
| if (retransmission_mode == QuicSentPacketManager::PTO_MODE) { |
| // Skip a packet number when PTO fires to elicit an immediate ACK. |
| const QuicPacketCount num_packet_numbers_to_skip = 1; |
| packet_creator_.SkipNPacketNumbers( |
| num_packet_numbers_to_skip, |
| sent_packet_manager_.GetLeastPacketAwaitedByPeer(encryption_level_), |
| sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length())); |
| previous_created_packet_number += num_packet_numbers_to_skip; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnNPacketNumbersSkipped(num_packet_numbers_to_skip, |
| clock_->Now()); |
| } |
| } |
| if (default_enable_5rto_blackhole_detection_ && |
| !sent_packet_manager_.HasInFlightPackets() && |
| blackhole_detector_.IsDetectionInProgress()) { |
| // Stop detection in quiescence. |
| QUICHE_DCHECK_EQ(QuicSentPacketManager::LOSS_MODE, retransmission_mode); |
| blackhole_detector_.StopDetection(/*permanent=*/false); |
| } |
| WriteIfNotBlocked(); |
| |
| // A write failure can result in the connection being closed, don't attempt to |
| // write further packets, or to set alarms. |
| if (!connected_) { |
| return; |
| } |
| // When PTO fires, the SentPacketManager gives the connection the opportunity |
| // to send new data before retransmitting. |
| sent_packet_manager_.MaybeSendProbePacket(); |
| |
| if (packet_creator_.packet_number() == previous_created_packet_number && |
| retransmission_mode == QuicSentPacketManager::PTO_MODE && |
| !visitor_->WillingAndAbleToWrite()) { |
| // Send PING if timer fires in PTO mode but there is no data to send. |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "No packet gets sent when timer fires in mode " |
| << retransmission_mode << ", send PING"; |
| QUICHE_DCHECK_LT(0u, |
| sent_packet_manager_.pending_timer_transmission_count()); |
| if (SupportsMultiplePacketNumberSpaces()) { |
| // Based on https://datatracker.ietf.org/doc/html/rfc9002#appendix-A.9 |
| PacketNumberSpace packet_number_space; |
| if (sent_packet_manager_ |
| .GetEarliestPacketSentTimeForPto(&packet_number_space) |
| .IsInitialized()) { |
| SendPingAtLevel( |
| GetEncryptionLevelToSendPingForSpace(packet_number_space)); |
| } else { |
| // The client must PTO when there is nothing in flight if the server |
| // could be blocked from sending by the amplification limit |
| QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_); |
| if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_HANDSHAKE)) { |
| SendPingAtLevel(ENCRYPTION_HANDSHAKE); |
| } else if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL)) { |
| SendPingAtLevel(ENCRYPTION_INITIAL); |
| } else { |
| QUIC_BUG(quic_bug_no_pto) << "PTO fired but nothing was sent."; |
| } |
| } |
| } else { |
| SendPingAtLevel(encryption_level_); |
| } |
| } |
| if (retransmission_mode == QuicSentPacketManager::PTO_MODE) { |
| // When timer fires in PTO mode, ensure 1) at least one packet is created, |
| // or there is data to send and available credit (such that packets will be |
| // sent eventually). |
| QUIC_BUG_IF( |
| quic_bug_12714_27, |
| packet_creator_.packet_number() == previous_created_packet_number && |
| (!visitor_->WillingAndAbleToWrite() || |
| sent_packet_manager_.pending_timer_transmission_count() == 0u)) |
| << "retransmission_mode: " << retransmission_mode |
| << ", packet_number: " << packet_creator_.packet_number() |
| << ", session has data to write: " << visitor_->WillingAndAbleToWrite() |
| << ", writer is blocked: " << writer_->IsWriteBlocked() |
| << ", pending_timer_transmission_count: " |
| << sent_packet_manager_.pending_timer_transmission_count(); |
| } |
| |
| // Ensure the retransmission alarm is always set if there are unacked packets |
| // and nothing waiting to be sent. |
| // This happens if the loss algorithm invokes a timer based loss, but the |
| // packet doesn't need to be retransmitted. |
| if (!HasQueuedData() && !retransmission_alarm_->IsSet()) { |
| SetRetransmissionAlarm(); |
| } |
| } |
| |
| void QuicConnection::SetEncrypter(EncryptionLevel level, |
| std::unique_ptr<QuicEncrypter> encrypter) { |
| packet_creator_.SetEncrypter(level, std::move(encrypter)); |
| } |
| |
| void QuicConnection::RemoveEncrypter(EncryptionLevel level) { |
| framer_.RemoveEncrypter(level); |
| } |
| |
| void QuicConnection::SetDiversificationNonce( |
| const DiversificationNonce& nonce) { |
| QUICHE_DCHECK_EQ(Perspective::IS_SERVER, perspective_); |
| packet_creator_.SetDiversificationNonce(nonce); |
| } |
| |
| void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) { |
| QUIC_DVLOG(1) << ENDPOINT << "Setting default encryption level from " |
| << encryption_level_ << " to " << level; |
| const bool changing_level = level != encryption_level_; |
| if (changing_level && packet_creator_.HasPendingFrames()) { |
| // Flush all queued frames when encryption level changes. |
| ScopedPacketFlusher flusher(this); |
| packet_creator_.FlushCurrentPacket(); |
| } |
| encryption_level_ = level; |
| packet_creator_.set_encryption_level(level); |
| QUIC_BUG_IF(quic_bug_12714_28, !framer_.HasEncrypterOfEncryptionLevel(level)) |
| << ENDPOINT << "Trying to set encryption level to " |
| << EncryptionLevelToString(level) << " while the key is missing"; |
| |
| if (!changing_level) { |
| return; |
| } |
| // The least packet awaited by the peer depends on the encryption level so |
| // we recalculate it here. |
| packet_creator_.UpdatePacketNumberLength( |
| sent_packet_manager_.GetLeastPacketAwaitedByPeer(encryption_level_), |
| sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length())); |
| } |
| |
| void QuicConnection::SetDecrypter(EncryptionLevel level, |
| std::unique_ptr<QuicDecrypter> decrypter) { |
| framer_.SetDecrypter(level, std::move(decrypter)); |
| |
| if (!undecryptable_packets_.empty() && |
| !process_undecryptable_packets_alarm_->IsSet()) { |
| process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow()); |
| } |
| } |
| |
| void QuicConnection::SetAlternativeDecrypter( |
| EncryptionLevel level, std::unique_ptr<QuicDecrypter> decrypter, |
| bool latch_once_used) { |
| framer_.SetAlternativeDecrypter(level, std::move(decrypter), latch_once_used); |
| |
| if (!undecryptable_packets_.empty() && |
| !process_undecryptable_packets_alarm_->IsSet()) { |
| process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow()); |
| } |
| } |
| |
| void QuicConnection::InstallDecrypter( |
| EncryptionLevel level, std::unique_ptr<QuicDecrypter> decrypter) { |
| if (level == ENCRYPTION_ZERO_RTT) { |
| had_zero_rtt_decrypter_ = true; |
| } |
| framer_.InstallDecrypter(level, std::move(decrypter)); |
| if (!undecryptable_packets_.empty() && |
| !process_undecryptable_packets_alarm_->IsSet()) { |
| process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow()); |
| } |
| } |
| |
| void QuicConnection::RemoveDecrypter(EncryptionLevel level) { |
| framer_.RemoveDecrypter(level); |
| } |
| |
| void QuicConnection::DiscardPreviousOneRttKeys() { |
| framer_.DiscardPreviousOneRttKeys(); |
| } |
| |
| bool QuicConnection::IsKeyUpdateAllowed() const { |
| return support_key_update_for_connection_ && |
| GetLargestAckedPacket().IsInitialized() && |
| lowest_packet_sent_in_current_key_phase_.IsInitialized() && |
| GetLargestAckedPacket() >= lowest_packet_sent_in_current_key_phase_; |
| } |
| |
| bool QuicConnection::HaveSentPacketsInCurrentKeyPhaseButNoneAcked() const { |
| return lowest_packet_sent_in_current_key_phase_.IsInitialized() && |
| (!GetLargestAckedPacket().IsInitialized() || |
| GetLargestAckedPacket() < lowest_packet_sent_in_current_key_phase_); |
| } |
| |
| QuicPacketCount QuicConnection::PotentialPeerKeyUpdateAttemptCount() const { |
| return framer_.PotentialPeerKeyUpdateAttemptCount(); |
| } |
| |
| bool QuicConnection::InitiateKeyUpdate(KeyUpdateReason reason) { |
| QUIC_DLOG(INFO) << ENDPOINT << "InitiateKeyUpdate"; |
| if (!IsKeyUpdateAllowed()) { |
| QUIC_BUG(quic_bug_10511_28) << "key update not allowed"; |
| return false; |
| } |
| return framer_.DoKeyUpdate(reason); |
| } |
| |
| const QuicDecrypter* QuicConnection::decrypter() const { |
| return framer_.decrypter(); |
| } |
| |
| const QuicDecrypter* QuicConnection::alternative_decrypter() const { |
| return framer_.alternative_decrypter(); |
| } |
| |
| void QuicConnection::QueueUndecryptablePacket( |
| const QuicEncryptedPacket& packet, EncryptionLevel decryption_level) { |
| for (const auto& saved_packet : undecryptable_packets_) { |
| if (packet.data() == saved_packet.packet->data() && |
| packet.length() == saved_packet.packet->length()) { |
| QUIC_DVLOG(1) << ENDPOINT << "Not queueing known undecryptable packet"; |
| return; |
| } |
| } |
| QUIC_DVLOG(1) << ENDPOINT << "Queueing undecryptable packet."; |
| undecryptable_packets_.emplace_back(packet, decryption_level, |
| last_received_packet_info_); |
| if (perspective_ == Perspective::IS_CLIENT) { |
| SetRetransmissionAlarm(); |
| } |
| } |
| |
| void QuicConnection::MaybeProcessUndecryptablePackets() { |
| process_undecryptable_packets_alarm_->Cancel(); |
| |
| if (undecryptable_packets_.empty() || |
| encryption_level_ == ENCRYPTION_INITIAL) { |
| return; |
| } |
| |
| auto iter = undecryptable_packets_.begin(); |
| while (connected_ && iter != undecryptable_packets_.end()) { |
| // Making sure there is no pending frames when processing next undecrypted |
| // packet because the queued ack frame may change. |
| packet_creator_.FlushCurrentPacket(); |
| if (!connected_) { |
| return; |
| } |
| UndecryptablePacket* undecryptable_packet = &*iter; |
| QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet"; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnAttemptingToProcessUndecryptablePacket( |
| undecryptable_packet->encryption_level); |
| } |
| last_received_packet_info_ = undecryptable_packet->packet_info; |
| current_packet_data_ = undecryptable_packet->packet->data(); |
| const bool processed = framer_.ProcessPacket(*undecryptable_packet->packet); |
| current_packet_data_ = nullptr; |
| |
| if (processed) { |
| QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!"; |
| iter = undecryptable_packets_.erase(iter); |
| ++stats_.packets_processed; |
| continue; |
| } |
| const bool has_decryption_key = version().KnowsWhichDecrypterToUse() && |
| framer_.HasDecrypterOfEncryptionLevel( |
| undecryptable_packet->encryption_level); |
| if (framer_.error() == QUIC_DECRYPTION_FAILURE && |
| ShouldEnqueueUnDecryptablePacket(undecryptable_packet->encryption_level, |
| has_decryption_key)) { |
| QUIC_DVLOG(1) |
| << ENDPOINT |
| << "Need to attempt to process this undecryptable packet later"; |
| ++iter; |
| continue; |
| } |
| iter = undecryptable_packets_.erase(iter); |
| } |
| |
| // Once handshake is complete, there will be no new keys installed and hence |
| // any undecryptable packets will never be able to be decrypted. |
| if (IsHandshakeComplete()) { |
| if (debug_visitor_ != nullptr) { |
| for (const auto& undecryptable_packet : undecryptable_packets_) { |
| debug_visitor_->OnUndecryptablePacket( |
| undecryptable_packet.encryption_level, /*dropped=*/true); |
| } |
| } |
| undecryptable_packets_.clear(); |
| } |
| if (perspective_ == Perspective::IS_CLIENT) { |
| SetRetransmissionAlarm(); |
| } |
| } |
| |
| void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) { |
| QUIC_DVLOG(1) << ENDPOINT << "Queueing coalesced packet."; |
| received_coalesced_packets_.push_back(packet.Clone()); |
| ++stats_.num_coalesced_packets_received; |
| } |
| |
| bool QuicConnection::MaybeProcessCoalescedPackets() { |
| bool processed = false; |
| while (connected_ && !received_coalesced_packets_.empty()) { |
| // Making sure there are no pending frames when processing the next |
| // coalesced packet because the queued ack frame may change. |
| packet_creator_.FlushCurrentPacket(); |
| if (!connected_) { |
| return processed; |
| } |
| |
| std::unique_ptr<QuicEncryptedPacket> packet = |
| std::move(received_coalesced_packets_.front()); |
| received_coalesced_packets_.pop_front(); |
| |
| QUIC_DVLOG(1) << ENDPOINT << "Processing coalesced packet"; |
| if (framer_.ProcessPacket(*packet)) { |
| processed = true; |
| ++stats_.num_coalesced_packets_processed; |
| } else { |
| // If we are unable to decrypt this packet, it might be |
| // because the CHLO or SHLO packet was lost. |
| } |
| } |
| if (processed) { |
| MaybeProcessUndecryptablePackets(); |
| MaybeSendInResponseToPacket(); |
| } |
| return processed; |
| } |
| |
| void QuicConnection::CloseConnection( |
| QuicErrorCode error, const std::string& details, |
| ConnectionCloseBehavior connection_close_behavior) { |
| CloseConnection(error, NO_IETF_QUIC_ERROR, details, |
| connection_close_behavior); |
| } |
| |
| void QuicConnection::CloseConnection( |
| QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error, |
| const std::string& error_details, |
| ConnectionCloseBehavior connection_close_behavior) { |
| QUICHE_DCHECK(!error_details.empty()); |
| if (!connected_) { |
| QUIC_DLOG(INFO) << "Connection is already closed."; |
| return; |
| } |
| |
| if (ietf_error != NO_IETF_QUIC_ERROR) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Closing connection: " << connection_id() |
| << ", with wire error: " << ietf_error |
| << ", error: " << QuicErrorCodeToString(error) |
| << ", and details: " << error_details; |
| } else { |
| QUIC_DLOG(INFO) << ENDPOINT << "Closing connection: " << connection_id() |
| << ", with error: " << QuicErrorCodeToString(error) << " (" |
| << error << "), and details: " << error_details; |
| } |
| |
| if (connection_close_behavior != ConnectionCloseBehavior::SILENT_CLOSE) { |
| SendConnectionClosePacket(error, ietf_error, error_details); |
| } |
| |
| TearDownLocalConnectionState(error, ietf_error, error_details, |
| ConnectionCloseSource::FROM_SELF); |
| } |
| |
| void QuicConnection::SendConnectionClosePacket( |
| QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error, |
| const std::string& details) { |
| // Always use the current path to send CONNECTION_CLOSE. |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, peer_address(), default_path_.client_connection_id, |
| default_path_.server_connection_id, connection_migration_use_new_cid_); |
| if (!SupportsMultiplePacketNumberSpaces()) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet."; |
| ScopedEncryptionLevelContext context(this, |
| GetConnectionCloseEncryptionLevel()); |
| if (version().CanSendCoalescedPackets()) { |
| coalesced_packet_.Clear(); |
| } |
| ClearQueuedPackets(); |
| // If there was a packet write error, write the smallest close possible. |
| ScopedPacketFlusher flusher(this); |
| // Always bundle an ACK with connection close for debugging purpose. |
| if (error != QUIC_PACKET_WRITE_ERROR && |
| !uber_received_packet_manager_.IsAckFrameEmpty( |
| QuicUtils::GetPacketNumberSpace(encryption_level_)) && |
| !packet_creator_.has_ack()) { |
| SendAck(); |
| } |
| QuicConnectionCloseFrame* frame; |
| |
| frame = new QuicConnectionCloseFrame(transport_version(), error, ietf_error, |
| details, |
| framer_.current_received_frame_type()); |
| packet_creator_.ConsumeRetransmittableControlFrame(QuicFrame(frame)); |
| packet_creator_.FlushCurrentPacket(); |
| if (version().CanSendCoalescedPackets()) { |
| FlushCoalescedPacket(); |
| } |
| ClearQueuedPackets(); |
| return; |
| } |
| ScopedPacketFlusher flusher(this); |
| |
| // Now that the connection is being closed, discard any unsent packets |
| // so the only packets to be sent will be connection close packets. |
| if (version().CanSendCoalescedPackets()) { |
| coalesced_packet_.Clear(); |
| } |
| ClearQueuedPackets(); |
| |
| for (EncryptionLevel level : |
| {ENCRYPTION_INITIAL, ENCRYPTION_HANDSHAKE, ENCRYPTION_ZERO_RTT, |
| ENCRYPTION_FORWARD_SECURE}) { |
| if (!framer_.HasEncrypterOfEncryptionLevel(level)) { |
| continue; |
| } |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Sending connection close packet at level: " << level; |
| ScopedEncryptionLevelContext context(this, level); |
| // Bundle an ACK of the corresponding packet number space for debugging |
| // purpose. |
| if (error != QUIC_PACKET_WRITE_ERROR && |
| !uber_received_packet_manager_.IsAckFrameEmpty( |
| QuicUtils::GetPacketNumberSpace(encryption_level_)) && |
| !packet_creator_.has_ack()) { |
| QuicFrames frames; |
| frames.push_back(GetUpdatedAckFrame()); |
| packet_creator_.FlushAckFrame(frames); |
| } |
| |
| if (level == ENCRYPTION_FORWARD_SECURE && |
| perspective_ == Perspective::IS_SERVER) { |
| visitor_->BeforeConnectionCloseSent(); |
| } |
| |
| auto* frame = new QuicConnectionCloseFrame( |
| transport_version(), error, ietf_error, details, |
| framer_.current_received_frame_type()); |
| packet_creator_.ConsumeRetransmittableControlFrame(QuicFrame(frame)); |
| packet_creator_.FlushCurrentPacket(); |
| } |
| if (version().CanSendCoalescedPackets()) { |
| FlushCoalescedPacket(); |
| } |
| // Since the connection is closing, if the connection close packets were not |
| // sent, then they should be discarded. |
| ClearQueuedPackets(); |
| } |
| |
| void QuicConnection::TearDownLocalConnectionState( |
| QuicErrorCode error, QuicIetfTransportErrorCodes ietf_error, |
| const std::string& error_details, ConnectionCloseSource source) { |
| QuicConnectionCloseFrame frame(transport_version(), error, ietf_error, |
| error_details, |
| framer_.current_received_frame_type()); |
| return TearDownLocalConnectionState(frame, source); |
| } |
| |
| void QuicConnection::TearDownLocalConnectionState( |
| const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) { |
| if (!connected_) { |
| QUIC_DLOG(INFO) << "Connection is already closed."; |
| return; |
| } |
| |
| // If we are using a batch writer, flush packets queued in it, if any. |
| FlushPackets(); |
| connected_ = false; |
| QUICHE_DCHECK(visitor_ != nullptr); |
| visitor_->OnConnectionClosed(frame, source); |
| // LossDetectionTunerInterface::Finish() may be called from |
| // sent_packet_manager_.OnConnectionClosed. Which may require the session to |
| // finish its business first. |
| sent_packet_manager_.OnConnectionClosed(); |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnConnectionClosed(frame, source); |
| } |
| // Cancel the alarms so they don't trigger any action now that the |
| // connection is closed. |
| CancelAllAlarms(); |
| CancelPathValidation(); |
| |
| peer_issued_cid_manager_.reset(); |
| self_issued_cid_manager_.reset(); |
| } |
| |
| void QuicConnection::CancelAllAlarms() { |
| QUIC_DVLOG(1) << "Cancelling all QuicConnection alarms."; |
| |
| ack_alarm_->PermanentCancel(); |
| if (use_ping_manager_) { |
| ping_manager_.Stop(); |
| } else { |
| ping_alarm_->PermanentCancel(); |
| } |
| retransmission_alarm_->PermanentCancel(); |
| send_alarm_->PermanentCancel(); |
| mtu_discovery_alarm_->PermanentCancel(); |
| process_undecryptable_packets_alarm_->PermanentCancel(); |
| discard_previous_one_rtt_keys_alarm_->PermanentCancel(); |
| discard_zero_rtt_decryption_keys_alarm_->PermanentCancel(); |
| blackhole_detector_.StopDetection(/*permanent=*/true); |
| idle_network_detector_.StopDetection(); |
| } |
| |
| QuicByteCount QuicConnection::max_packet_length() const { |
| return packet_creator_.max_packet_length(); |
| } |
| |
| void QuicConnection::SetMaxPacketLength(QuicByteCount length) { |
| long_term_mtu_ = length; |
| stats_.max_egress_mtu = std::max(stats_.max_egress_mtu, long_term_mtu_); |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| } |
| |
| bool QuicConnection::HasQueuedData() const { |
| return packet_creator_.HasPendingFrames() || !buffered_packets_.empty(); |
| } |
| |
| void QuicConnection::SetNetworkTimeouts(QuicTime::Delta handshake_timeout, |
| QuicTime::Delta idle_timeout) { |
| QUIC_BUG_IF(quic_bug_12714_29, idle_timeout > handshake_timeout) |
| << "idle_timeout:" << idle_timeout.ToMilliseconds() |
| << " handshake_timeout:" << handshake_timeout.ToMilliseconds(); |
| // Adjust the idle timeout on client and server to prevent clients from |
| // sending requests to servers which have already closed the connection. |
| if (perspective_ == Perspective::IS_SERVER) { |
| idle_timeout = idle_timeout + QuicTime::Delta::FromSeconds(3); |
| } else if (idle_timeout > QuicTime::Delta::FromSeconds(1)) { |
| idle_timeout = idle_timeout - QuicTime::Delta::FromSeconds(1); |
| } |
| idle_network_detector_.SetTimeouts(handshake_timeout, idle_timeout); |
| } |
| |
| void QuicConnection::SetPingAlarm() { |
| if (!connected_) { |
| return; |
| } |
| if (use_ping_manager_) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_use_ping_manager2); |
| ping_manager_.SetAlarm(clock_->ApproximateNow(), |
| visitor_->ShouldKeepConnectionAlive(), |
| sent_packet_manager_.HasInFlightPackets()); |
| return; |
| } |
| if (perspective_ == Perspective::IS_SERVER && |
| initial_retransmittable_on_wire_timeout_.IsInfinite()) { |
| // The PING alarm exists to support two features: |
| // 1) clients send PINGs every 15s to prevent NAT timeouts, |
| // 2) both clients and servers can send retransmittable on the wire PINGs |
| // (ROWP) while ShouldKeepConnectionAlive is true and there is no packets in |
| // flight. |
| return; |
| } |
| if (!visitor_->ShouldKeepConnectionAlive()) { |
| ping_alarm_->Cancel(); |
| // Don't send a ping unless the application (ie: HTTP/3) says to, usually |
| // because it is expecting a response from the server. |
| return; |
| } |
| if (initial_retransmittable_on_wire_timeout_.IsInfinite() || |
| sent_packet_manager_.HasInFlightPackets() || |
| retransmittable_on_wire_ping_count_ > |
| GetQuicFlag(quic_max_retransmittable_on_wire_ping_count)) { |
| if (perspective_ == Perspective::IS_CLIENT) { |
| // Clients send 15s PINGs to avoid NATs from timing out. |
| ping_alarm_->Update(clock_->ApproximateNow() + keep_alive_ping_timeout_, |
| QuicTime::Delta::FromSeconds(1)); |
| } else { |
| // Servers do not send 15s PINGs. |
| ping_alarm_->Cancel(); |
| } |
| return; |
| } |
| QUICHE_DCHECK_LT(initial_retransmittable_on_wire_timeout_, |
| keep_alive_ping_timeout_); |
| QuicTime::Delta retransmittable_on_wire_timeout = |
| initial_retransmittable_on_wire_timeout_; |
| int max_aggressive_retransmittable_on_wire_ping_count = |
| GetQuicFlag(quic_max_aggressive_retransmittable_on_wire_ping_count); |
| QUICHE_DCHECK_LE(0, max_aggressive_retransmittable_on_wire_ping_count); |
| if (consecutive_retransmittable_on_wire_ping_count_ > |
| max_aggressive_retransmittable_on_wire_ping_count) { |
| // Exponentially back off the timeout if the number of consecutive |
| // retransmittable on wire pings has exceeds the allowance. |
| int shift = consecutive_retransmittable_on_wire_ping_count_ - |
| max_aggressive_retransmittable_on_wire_ping_count; |
| retransmittable_on_wire_timeout = |
| initial_retransmittable_on_wire_timeout_ * (1 << shift); |
| } |
| // If it's already set to an earlier time, then don't update it. |
| if (ping_alarm_->IsSet() && |
| ping_alarm_->deadline() < |
| clock_->ApproximateNow() + retransmittable_on_wire_timeout) { |
| return; |
| } |
| |
| if (retransmittable_on_wire_timeout < keep_alive_ping_timeout_) { |
| // Use a shorter timeout if there are open streams, but nothing on the wire. |
| ping_alarm_->Update( |
| clock_->ApproximateNow() + retransmittable_on_wire_timeout, |
| kAlarmGranularity); |
| if (max_aggressive_retransmittable_on_wire_ping_count != 0) { |
| consecutive_retransmittable_on_wire_ping_count_++; |
| } |
| retransmittable_on_wire_ping_count_++; |
| return; |
| } |
| |
| ping_alarm_->Update(clock_->ApproximateNow() + keep_alive_ping_timeout_, |
| kAlarmGranularity); |
| } |
| |
| void QuicConnection::SetRetransmissionAlarm() { |
| if (!connected_) { |
| if (retransmission_alarm_->IsSet()) { |
| QUIC_BUG(quic_bug_10511_29) |
| << ENDPOINT << "Retransmission alarm is set while disconnected"; |
| retransmission_alarm_->Cancel(); |
| } |
| return; |
| } |
| if (packet_creator_.PacketFlusherAttached()) { |
| pending_retransmission_alarm_ = true; |
| return; |
| } |
| if (LimitedByAmplificationFactor(packet_creator_.max_packet_length())) { |
| // Do not set retransmission timer if connection is anti-amplification limit |
| // throttled. Otherwise, nothing can be sent when timer fires. |
| retransmission_alarm_->Cancel(); |
| return; |
| } |
| PacketNumberSpace packet_number_space; |
| if (SupportsMultiplePacketNumberSpaces() && !IsHandshakeConfirmed() && |
| !sent_packet_manager_ |
| .GetEarliestPacketSentTimeForPto(&packet_number_space) |
| .IsInitialized()) { |
| // Before handshake gets confirmed, GetEarliestPacketSentTimeForPto |
| // returning 0 indicates no packets are in flight or only application data |
| // is in flight. |
| if (perspective_ == Perspective::IS_SERVER) { |
| // No need to arm PTO on server side. |
| retransmission_alarm_->Cancel(); |
| return; |
| } |
| if (retransmission_alarm_->IsSet() && |
| GetRetransmissionDeadline() > retransmission_alarm_->deadline()) { |
| // Do not postpone armed PTO on the client side. |
| return; |
| } |
| } |
| |
| retransmission_alarm_->Update(GetRetransmissionDeadline(), kAlarmGranularity); |
| } |
| |
| void QuicConnection::MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number) { |
| if (mtu_discovery_alarm_->IsSet() || |
| !mtu_discoverer_.ShouldProbeMtu(sent_packet_number)) { |
| return; |
| } |
| mtu_discovery_alarm_->Set(clock_->ApproximateNow()); |
| } |
| |
| QuicConnection::ScopedPacketFlusher::ScopedPacketFlusher( |
| QuicConnection* connection) |
| : connection_(connection), |
| flush_and_set_pending_retransmission_alarm_on_delete_(false), |
| handshake_packet_sent_(connection != nullptr && |
| connection->handshake_packet_sent_) { |
| if (connection_ == nullptr) { |
| return; |
| } |
| |
| if (!connection_->packet_creator_.PacketFlusherAttached()) { |
| flush_and_set_pending_retransmission_alarm_on_delete_ = true; |
| connection->packet_creator_.AttachPacketFlusher(); |
| } |
| } |
| |
| QuicConnection::ScopedPacketFlusher::~ScopedPacketFlusher() { |
| if (connection_ == nullptr || !connection_->connected()) { |
| return; |
| } |
| |
| if (flush_and_set_pending_retransmission_alarm_on_delete_) { |
| const QuicTime ack_timeout = |
| connection_->uber_received_packet_manager_.GetEarliestAckTimeout(); |
| if (ack_timeout.IsInitialized()) { |
| if (ack_timeout <= connection_->clock_->ApproximateNow() && |
| !connection_->CanWrite(NO_RETRANSMITTABLE_DATA)) { |
| // Cancel ACK alarm if connection is write blocked, and ACK will be |
| // sent when connection gets unblocked. |
| connection_->ack_alarm_->Cancel(); |
| } else if (!connection_->ack_alarm_->IsSet() || |
| connection_->ack_alarm_->deadline() > ack_timeout) { |
| connection_->ack_alarm_->Update(ack_timeout, QuicTime::Delta::Zero()); |
| } |
| } |
| if (connection_->ack_alarm_->IsSet() && |
| connection_->ack_alarm_->deadline() <= |
| connection_->clock_->ApproximateNow()) { |
| // An ACK needs to be sent right now. This ACK did not get bundled |
| // because either there was no data to write or packets were marked as |
| // received after frames were queued in the generator. |
| if (connection_->send_alarm_->IsSet() && |
| connection_->send_alarm_->deadline() <= |
| connection_->clock_->ApproximateNow()) { |
| // If send alarm will go off soon, let send alarm send the ACK. |
| connection_->ack_alarm_->Cancel(); |
| } else if (connection_->SupportsMultiplePacketNumberSpaces()) { |
| connection_->SendAllPendingAcks(); |
| } else { |
| connection_->SendAck(); |
| } |
| } |
| |
| // INITIAL or HANDSHAKE retransmission could cause peer to derive new |
| // keys, such that the buffered undecryptable packets may be processed. |
| // This endpoint would derive an inflated RTT sample when receiving ACKs |
| // of those undecryptable packets. To mitigate this, tries to coalesce as |
| // many higher space packets as possible (via for loop inside |
| // MaybeCoalescePacketOfHigherSpace) to fill the remaining space in the |
| // coalescer. |
| if (connection_->version().CanSendCoalescedPackets()) { |
| connection_->MaybeCoalescePacketOfHigherSpace(); |
| } |
| connection_->packet_creator_.Flush(); |
| if (connection_->version().CanSendCoalescedPackets()) { |
| connection_->FlushCoalescedPacket(); |
| } |
| connection_->FlushPackets(); |
| |
| if (!connection_->connected()) { |
| return; |
| } |
| |
| if (!handshake_packet_sent_ && connection_->handshake_packet_sent_) { |
| // This would cause INITIAL key to be dropped. Drop keys here to avoid |
| // missing the write keys in the middle of writing. |
| connection_->visitor_->OnHandshakePacketSent(); |
| } |
| // Reset transmission type. |
| connection_->SetTransmissionType(NOT_RETRANSMISSION); |
| |
| // Once all transmissions are done, check if there is any outstanding data |
| // to send and notify the congestion controller if not. |
| // |
| // Note that this means that the application limited check will happen as |
| // soon as the last flusher gets destroyed, which is typically after a |
| // single stream write is finished. This means that if all the data from a |
| // single write goes through the connection, the application-limited signal |
| // will fire even if the caller does a write operation immediately after. |
| // There are two important approaches to remedy this situation: |
| // (1) Instantiate ScopedPacketFlusher before performing multiple subsequent |
| // writes, thus deferring this check until all writes are done. |
| // (2) Write data in chunks sufficiently large so that they cause the |
| // connection to be limited by the congestion control. Typically, this |
| // would mean writing chunks larger than the product of the current |
| // pacing rate and the pacer granularity. So, for instance, if the |
| // pacing rate of the connection is 1 Gbps, and the pacer granularity is |
| // 1 ms, the caller should send at least 125k bytes in order to not |
| // be marked as application-limited. |
| connection_->CheckIfApplicationLimited(); |
| |
| if (connection_->pending_retransmission_alarm_) { |
| connection_->SetRetransmissionAlarm(); |
| connection_->pending_retransmission_alarm_ = false; |
| } |
| } |
| QUICHE_DCHECK_EQ(flush_and_set_pending_retransmission_alarm_on_delete_, |
| !connection_->packet_creator_.PacketFlusherAttached()); |
| } |
| |
| QuicConnection::ScopedEncryptionLevelContext::ScopedEncryptionLevelContext( |
| QuicConnection* connection, EncryptionLevel encryption_level) |
| : connection_(connection), latched_encryption_level_(ENCRYPTION_INITIAL) { |
| if (connection_ == nullptr) { |
| return; |
| } |
| latched_encryption_level_ = connection_->encryption_level_; |
| connection_->SetDefaultEncryptionLevel(encryption_level); |
| } |
| |
| QuicConnection::ScopedEncryptionLevelContext::~ScopedEncryptionLevelContext() { |
| if (connection_ == nullptr || !connection_->connected_) { |
| return; |
| } |
| connection_->SetDefaultEncryptionLevel(latched_encryption_level_); |
| } |
| |
| QuicConnection::BufferedPacket::BufferedPacket( |
| const SerializedPacket& packet, const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address) |
| : BufferedPacket(packet.encrypted_buffer, packet.encrypted_length, |
| self_address, peer_address) {} |
| |
| QuicConnection::BufferedPacket::BufferedPacket( |
| const char* encrypted_buffer, QuicPacketLength encrypted_length, |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address) |
| : length(encrypted_length), |
| self_address(self_address), |
| peer_address(peer_address) { |
| data = std::make_unique<char[]>(encrypted_length); |
| memcpy(data.get(), encrypted_buffer, encrypted_length); |
| } |
| |
| QuicConnection::BufferedPacket::BufferedPacket( |
| QuicRandom& random, QuicPacketLength encrypted_length, |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address) |
| : length(encrypted_length), |
| self_address(self_address), |
| peer_address(peer_address) { |
| data = std::make_unique<char[]>(encrypted_length); |
| random.RandBytes(data.get(), encrypted_length); |
| } |
| |
| QuicConnection::ReceivedPacketInfo::ReceivedPacketInfo(QuicTime receipt_time) |
| : receipt_time(receipt_time) {} |
| QuicConnection::ReceivedPacketInfo::ReceivedPacketInfo( |
| const QuicSocketAddress& destination_address, |
| const QuicSocketAddress& source_address, QuicTime receipt_time, |
| QuicByteCount length) |
| : destination_address(destination_address), |
| source_address(source_address), |
| receipt_time(receipt_time), |
| length(length) {} |
| |
| std::ostream& operator<<(std::ostream& os, |
| const QuicConnection::ReceivedPacketInfo& info) { |
| os << " { destination_address: " << info.destination_address.ToString() |
| << ", source_address: " << info.source_address.ToString() |
| << ", received_bytes_counted: " << info.received_bytes_counted |
| << ", length: " << info.length |
| << ", destination_connection_id: " << info.destination_connection_id; |
| if (!info.decrypted) { |
| os << " }\n"; |
| return os; |
| } |
| os << ", decrypted: " << info.decrypted |
| << ", decrypted_level: " << EncryptionLevelToString(info.decrypted_level) |
| << ", header: " << info.header << ", frames: "; |
| for (const auto frame : info.frames) { |
| os << frame; |
| } |
| os << " }\n"; |
| return os; |
| } |
| |
| HasRetransmittableData QuicConnection::IsRetransmittable( |
| const SerializedPacket& packet) { |
| // Retransmitted packets retransmittable frames are owned by the unacked |
| // packet map, but are not present in the serialized packet. |
| if (packet.transmission_type != NOT_RETRANSMISSION || |
| !packet.retransmittable_frames.empty()) { |
| return HAS_RETRANSMITTABLE_DATA; |
| } else { |
| return NO_RETRANSMITTABLE_DATA; |
| } |
| } |
| |
| bool QuicConnection::IsTerminationPacket(const SerializedPacket& packet, |
| QuicErrorCode* error_code) { |
| if (packet.retransmittable_frames.empty()) { |
| return false; |
| } |
| for (const QuicFrame& frame : packet.retransmittable_frames) { |
| if (frame.type == CONNECTION_CLOSE_FRAME) { |
| *error_code = frame.connection_close_frame->quic_error_code; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void QuicConnection::SetMtuDiscoveryTarget(QuicByteCount target) { |
| QUIC_DVLOG(2) << ENDPOINT << "SetMtuDiscoveryTarget: " << target; |
| mtu_discoverer_.Disable(); |
| mtu_discoverer_.Enable(max_packet_length(), GetLimitedMaxPacketSize(target)); |
| } |
| |
| QuicByteCount QuicConnection::GetLimitedMaxPacketSize( |
| QuicByteCount suggested_max_packet_size) { |
| if (!peer_address().IsInitialized()) { |
| QUIC_BUG(quic_bug_10511_30) |
| << "Attempted to use a connection without a valid peer address"; |
| return suggested_max_packet_size; |
| } |
| |
| const QuicByteCount writer_limit = writer_->GetMaxPacketSize(peer_address()); |
| |
| QuicByteCount max_packet_size = suggested_max_packet_size; |
| if (max_packet_size > writer_limit) { |
| max_packet_size = writer_limit; |
| } |
| if (max_packet_size > peer_max_packet_size_) { |
| max_packet_size = peer_max_packet_size_; |
| } |
| if (max_packet_size > kMaxOutgoingPacketSize) { |
| max_packet_size = kMaxOutgoingPacketSize; |
| } |
| return max_packet_size; |
| } |
| |
| void QuicConnection::SendMtuDiscoveryPacket(QuicByteCount target_mtu) { |
| // Currently, this limit is ensured by the caller. |
| QUICHE_DCHECK_EQ(target_mtu, GetLimitedMaxPacketSize(target_mtu)); |
| |
| // Send the probe. |
| packet_creator_.GenerateMtuDiscoveryPacket(target_mtu); |
| } |
| |
| // TODO(zhongyi): change this method to generate a connectivity probing packet |
| // and let the caller to call writer to write the packet and handle write |
| // status. |
| bool QuicConnection::SendConnectivityProbingPacket( |
| QuicPacketWriter* probing_writer, const QuicSocketAddress& peer_address) { |
| QUICHE_DCHECK(peer_address.IsInitialized()); |
| if (!connected_) { |
| QUIC_BUG(quic_bug_10511_31) |
| << "Not sending connectivity probing packet as connection is " |
| << "disconnected."; |
| return false; |
| } |
| if (perspective_ == Perspective::IS_SERVER && probing_writer == nullptr) { |
| // Server can use default packet writer to write packet. |
| probing_writer = writer_; |
| } |
| QUICHE_DCHECK(probing_writer); |
| |
| if (probing_writer->IsWriteBlocked()) { |
| QUIC_DLOG(INFO) |
| << ENDPOINT |
| << "Writer blocked when sending connectivity probing packet."; |
| if (probing_writer == writer_) { |
| // Visitor should not be write blocked if the probing writer is not the |
| // default packet writer. |
| visitor_->OnWriteBlocked(); |
| } |
| return true; |
| } |
| |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Sending path probe packet for connection_id = " |
| << default_path_.server_connection_id; |
| |
| std::unique_ptr<SerializedPacket> probing_packet; |
| if (!version().HasIetfQuicFrames()) { |
| // Non-IETF QUIC, generate a padded ping regardless of whether this is a |
| // request or a response. |
| probing_packet = packet_creator_.SerializeConnectivityProbingPacket(); |
| } else { |
| // IETF QUIC path challenge. |
| // Send a path probe request using IETF QUIC PATH_CHALLENGE frame. |
| QuicPathFrameBuffer transmitted_connectivity_probe_payload; |
| random_generator_->RandBytes(&transmitted_connectivity_probe_payload, |
| sizeof(QuicPathFrameBuffer)); |
| probing_packet = |
| packet_creator_.SerializePathChallengeConnectivityProbingPacket( |
| transmitted_connectivity_probe_payload); |
| } |
| QUICHE_DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA); |
| return WritePacketUsingWriter(std::move(probing_packet), probing_writer, |
| self_address(), peer_address, |
| /*measure_rtt=*/true); |
| } |
| |
| bool QuicConnection::WritePacketUsingWriter( |
| std::unique_ptr<SerializedPacket> packet, QuicPacketWriter* writer, |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, bool measure_rtt) { |
| const QuicTime packet_send_time = clock_->Now(); |
| QUIC_DVLOG(2) << ENDPOINT |
| << "Sending path probe packet for server connection ID " |
| << default_path_.server_connection_id << std::endl |
| << quiche::QuicheTextUtils::HexDump(absl::string_view( |
| packet->encrypted_buffer, packet->encrypted_length)); |
| WriteResult result = writer->WritePacket( |
| packet->encrypted_buffer, packet->encrypted_length, self_address.host(), |
| peer_address, per_packet_options_); |
| |
| // If using a batch writer and the probing packet is buffered, flush it. |
| if (writer->IsBatchMode() && result.status == WRITE_STATUS_OK && |
| result.bytes_written == 0) { |
| result = writer->Flush(); |
| } |
| |
| if (IsWriteError(result.status)) { |
| // Write error for any connectivity probe should not affect the connection |
| // as it is sent on a different path. |
| QUIC_DLOG(INFO) << ENDPOINT << "Write probing packet failed with error = " |
| << result.error_code; |
| return false; |
| } |
| |
| // Send in currrent path. Call OnPacketSent regardless of the write result. |
| sent_packet_manager_.OnPacketSent(packet.get(), packet_send_time, |
| packet->transmission_type, |
| NO_RETRANSMITTABLE_DATA, measure_rtt); |
| |
| if (debug_visitor_ != nullptr) { |
| if (sent_packet_manager_.unacked_packets().empty()) { |
| QUIC_BUG(quic_bug_10511_32) |
| << "Unacked map is empty right after packet is sent"; |
| } else { |
| debug_visitor_->OnPacketSent( |
| packet->packet_number, packet->encrypted_length, |
| packet->has_crypto_handshake, packet->transmission_type, |
| packet->encryption_level, |
| sent_packet_manager_.unacked_packets() |
| .rbegin() |
| ->retransmittable_frames, |
| packet->nonretransmittable_frames, packet_send_time); |
| } |
| } |
| |
| if (IsWriteBlockedStatus(result.status)) { |
| if (writer == writer_) { |
| // Visitor should not be write blocked if the probing writer is not the |
| // default packet writer. |
| visitor_->OnWriteBlocked(); |
| } |
| if (result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Write probing packet blocked"; |
| } |
| } |
| |
| return true; |
| } |
| |
| void QuicConnection::DisableMtuDiscovery() { |
| mtu_discoverer_.Disable(); |
| mtu_discovery_alarm_->Cancel(); |
| } |
| |
| void QuicConnection::DiscoverMtu() { |
| QUICHE_DCHECK(!mtu_discovery_alarm_->IsSet()); |
| |
| const QuicPacketNumber largest_sent_packet = |
| sent_packet_manager_.GetLargestSentPacket(); |
| if (mtu_discoverer_.ShouldProbeMtu(largest_sent_packet)) { |
| ++mtu_probe_count_; |
| SendMtuDiscoveryPacket( |
| mtu_discoverer_.GetUpdatedMtuProbeSize(largest_sent_packet)); |
| } |
| QUICHE_DCHECK(!mtu_discovery_alarm_->IsSet()); |
| } |
| |
| void QuicConnection::OnEffectivePeerMigrationValidated() { |
| if (active_effective_peer_migration_type_ == NO_CHANGE) { |
| QUIC_BUG(quic_bug_10511_33) << "No migration underway."; |
| return; |
| } |
| highest_packet_sent_before_effective_peer_migration_.Clear(); |
| const bool send_address_token = |
| active_effective_peer_migration_type_ != PORT_CHANGE; |
| active_effective_peer_migration_type_ = NO_CHANGE; |
| ++stats_.num_validated_peer_migration; |
| if (!validate_client_addresses_) { |
| return; |
| } |
| if (debug_visitor_ != nullptr) { |
| const QuicTime now = clock_->ApproximateNow(); |
| if (now >= stats_.handshake_completion_time) { |
| debug_visitor_->OnPeerMigrationValidated( |
| now - stats_.handshake_completion_time); |
| } else { |
| QUIC_BUG(quic_bug_10511_34) |
| << "Handshake completion time is larger than current time."; |
| } |
| } |
| |
| // Lift anti-amplification limit. |
| default_path_.validated = true; |
| alternative_path_.Clear(); |
| if (send_address_token) { |
| visitor_->MaybeSendAddressToken(); |
| } |
| } |
| |
| void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) { |
| // TODO(fayang): Currently, all peer address change type are allowed. Need to |
| // add a method ShouldAllowPeerAddressChange(PeerAddressChangeType type) to |
| // determine whether |type| is allowed. |
| if (!validate_client_addresses_) { |
| if (type == NO_CHANGE) { |
| QUIC_BUG(quic_bug_10511_35) |
| << "EffectivePeerMigration started without address change."; |
| return; |
| } |
| QUIC_DLOG(INFO) |
| << ENDPOINT << "Effective peer's ip:port changed from " |
| << default_path_.peer_address.ToString() << " to " |
| << GetEffectivePeerAddressFromCurrentPacket().ToString() |
| << ", address change type is " << type |
| << ", migrating connection without validating new client address."; |
| |
| highest_packet_sent_before_effective_peer_migration_ = |
| sent_packet_manager_.GetLargestSentPacket(); |
| default_path_.peer_address = GetEffectivePeerAddressFromCurrentPacket(); |
| active_effective_peer_migration_type_ = type; |
| |
| OnConnectionMigration(); |
| return; |
| } |
| |
| if (type == NO_CHANGE) { |
| UpdatePeerAddress(last_received_packet_info_.source_address); |
| QUIC_BUG(quic_bug_10511_36) |
| << "EffectivePeerMigration started without address change."; |
| return; |
| } |
| if (GetQuicReloadableFlag( |
| quic_flush_pending_frames_and_padding_bytes_on_migration)) { |
| QUIC_RELOADABLE_FLAG_COUNT( |
| quic_flush_pending_frames_and_padding_bytes_on_migration); |
| // There could be pending NEW_TOKEN_FRAME triggered by non-probing |
| // PATH_RESPONSE_FRAME in the same packet or pending padding bytes in the |
| // packet creator. |
| packet_creator_.FlushCurrentPacket(); |
| packet_creator_.SendRemainingPendingPadding(); |
| if (!connected_) { |
| return; |
| } |
| } else { |
| if (packet_creator_.HasPendingFrames()) { |
| packet_creator_.FlushCurrentPacket(); |
| if (!connected_) { |
| return; |
| } |
| } |
| } |
| |
| // Action items: |
| // 1. Switch congestion controller; |
| // 2. Update default_path_ (addresses, validation and bytes accounting); |
| // 3. Save previous default path if needed; |
| // 4. Kick off reverse path validation if needed. |
| // Items 1 and 2 are must-to-do. Items 3 and 4 depends on if the new address |
| // is validated or not and which path the incoming packet is on. |
| |
| const QuicSocketAddress current_effective_peer_address = |
| GetEffectivePeerAddressFromCurrentPacket(); |
| QUIC_DLOG(INFO) << ENDPOINT << "Effective peer's ip:port changed from " |
| << default_path_.peer_address.ToString() << " to " |
| << current_effective_peer_address.ToString() |
| << ", address change type is " << type |
| << ", migrating connection."; |
| |
| const QuicSocketAddress previous_direct_peer_address = direct_peer_address_; |
| PathState previous_default_path = std::move(default_path_); |
| active_effective_peer_migration_type_ = type; |
| MaybeClearQueuedPacketsOnPathChange(); |
| OnConnectionMigration(); |
| |
| // Update congestion controller if the address change type is not PORT_CHANGE. |
| if (type == PORT_CHANGE) { |
| QUICHE_DCHECK(previous_default_path.validated || |
| (alternative_path_.validated && |
| alternative_path_.send_algorithm != nullptr)); |
| // No need to store previous congestion controller because either the new |
| // default path is validated or the alternative path is validated and |
| // already has associated congestion controller. |
| } else { |
| previous_default_path.rtt_stats.emplace(); |
| previous_default_path.rtt_stats->CloneFrom( |
| *sent_packet_manager_.GetRttStats()); |
| // If the new peer address share the same IP with the alternative path, the |
| // connection should switch to the congestion controller of the alternative |
| // path. Otherwise, the connection should use a brand new one. |
| // In order to re-use existing code in sent_packet_manager_, reset |
| // congestion controller to initial state first and then change to the one |
| // on alternative path. |
| // TODO(danzh) combine these two steps into one after deprecating gQUIC. |
| previous_default_path.send_algorithm = OnPeerIpAddressChanged(); |
| |
| if (alternative_path_.peer_address.host() == |
| current_effective_peer_address.host() && |
| alternative_path_.send_algorithm != nullptr) { |
| // Update the default path with the congestion controller of the |
| // alternative path. |
| sent_packet_manager_.SetSendAlgorithm( |
| alternative_path_.send_algorithm.release()); |
| sent_packet_manager_.SetRttStats( |
| std::move(alternative_path_.rtt_stats).value()); |
| } |
| } |
| // Update to the new peer address. |
| UpdatePeerAddress(last_received_packet_info_.source_address); |
| // Update the default path. |
| if (IsAlternativePath(last_received_packet_info_.destination_address, |
| current_effective_peer_address)) { |
| SetDefaultPathState(std::move(alternative_path_)); |
| } else { |
| QuicConnectionId client_connection_id; |
| absl::optional<StatelessResetToken> stateless_reset_token; |
| FindMatchingOrNewClientConnectionIdOrToken( |
| previous_default_path, alternative_path_, |
| last_received_packet_info_.destination_connection_id, |
| &client_connection_id, &stateless_reset_token); |
| SetDefaultPathState( |
| PathState(last_received_packet_info_.destination_address, |
| current_effective_peer_address, client_connection_id, |
| last_received_packet_info_.destination_connection_id, |
| stateless_reset_token)); |
| // The path is considered validated if its peer IP address matches any |
| // validated path's peer IP address. |
| default_path_.validated = |
| (alternative_path_.peer_address.host() == |
| current_effective_peer_address.host() && |
| alternative_path_.validated) || |
| (previous_default_path.validated && type == PORT_CHANGE); |
| } |
| if (!last_received_packet_info_.received_bytes_counted) { |
| // Increment bytes counting on the new default path. |
| default_path_.bytes_received_before_address_validation += |
| last_received_packet_info_.length; |
| last_received_packet_info_.received_bytes_counted = true; |
| } |
| |
| if (!previous_default_path.validated) { |
| // If the old address is under validation, cancel and fail it. Failing to |
| // validate the old path shouldn't take any effect. |
| QUIC_DVLOG(1) << "Cancel validation of previous peer address change to " |
| << previous_default_path.peer_address |
| << " upon peer migration to " << default_path_.peer_address; |
| path_validator_.CancelPathValidation(); |
| ++stats_.num_peer_migration_while_validating_default_path; |
| } |
| |
| // Clear alternative path if the new default path shares the same IP as the |
| // alternative path. |
| if (alternative_path_.peer_address.host() == |
| default_path_.peer_address.host()) { |
| alternative_path_.Clear(); |
| } |
| |
| if (default_path_.validated) { |
| QUIC_DVLOG(1) << "Peer migrated to a validated address."; |
| // No need to save previous default path, validate new peer address or |
| // update bytes sent/received. |
| if (!(previous_default_path.validated && type == PORT_CHANGE)) { |
| // The alternative path was validated because of proactive reverse path |
| // validation. |
| ++stats_.num_peer_migration_to_proactively_validated_address; |
| } |
| OnEffectivePeerMigrationValidated(); |
| return; |
| } |
| |
| // The new default address is not validated yet. Anti-amplification limit is |
| // enforced. |
| QUICHE_DCHECK(EnforceAntiAmplificationLimit()); |
| QUIC_DVLOG(1) << "Apply anti-amplification limit to effective peer address " |
| << default_path_.peer_address << " with " |
| << default_path_.bytes_sent_before_address_validation |
| << " bytes sent and " |
| << default_path_.bytes_received_before_address_validation |
| << " bytes received."; |
| |
| QUICHE_DCHECK(!alternative_path_.peer_address.IsInitialized() || |
| alternative_path_.peer_address.host() != |
| default_path_.peer_address.host()); |
| |
| // Save previous default path to the altenative path. |
| if (previous_default_path.validated) { |
| // The old path is a validated path which the connection might revert back |
| // to later. Store it as the alternative path. |
| alternative_path_ = std::move(previous_default_path); |
| QUICHE_DCHECK(alternative_path_.send_algorithm != nullptr); |
| } |
| |
| // If the new address is not validated and the connection is not already |
| // validating that address, a new reverse path validation is needed. |
| if (!path_validator_.IsValidatingPeerAddress( |
| current_effective_peer_address)) { |
| ++stats_.num_reverse_path_validtion_upon_migration; |
| ValidatePath(std::make_unique<ReversePathValidationContext>( |
| default_path_.self_address, peer_address(), |
| default_path_.peer_address, this), |
| std::make_unique<ReversePathValidationResultDelegate>( |
| this, previous_direct_peer_address)); |
| } else { |
| QUIC_DVLOG(1) << "Peer address " << default_path_.peer_address |
| << " is already under validation, wait for result."; |
| ++stats_.num_peer_migration_to_proactively_validated_address; |
| } |
| } |
| |
| void QuicConnection::OnConnectionMigration() { |
| if (debug_visitor_ != nullptr) { |
| const QuicTime now = clock_->ApproximateNow(); |
| if (now >= stats_.handshake_completion_time) { |
| debug_visitor_->OnPeerAddressChange( |
| active_effective_peer_migration_type_, |
| now - stats_.handshake_completion_time); |
| } |
| } |
| visitor_->OnConnectionMigration(active_effective_peer_migration_type_); |
| if (active_effective_peer_migration_type_ != PORT_CHANGE && |
| active_effective_peer_migration_type_ != IPV4_SUBNET_CHANGE && |
| !validate_client_addresses_) { |
| sent_packet_manager_.OnConnectionMigration(/*reset_send_algorithm=*/false); |
| } |
| } |
| |
| bool QuicConnection::IsCurrentPacketConnectivityProbing() const { |
| return is_current_packet_connectivity_probing_; |
| } |
| |
| bool QuicConnection::ack_frame_updated() const { |
| return uber_received_packet_manager_.IsAckFrameUpdated(); |
| } |
| |
| absl::string_view QuicConnection::GetCurrentPacket() { |
| if (current_packet_data_ == nullptr) { |
| return absl::string_view(); |
| } |
| return absl::string_view(current_packet_data_, |
| last_received_packet_info_.length); |
| } |
| |
| bool QuicConnection::MaybeConsiderAsMemoryCorruption( |
| const QuicStreamFrame& frame) { |
| if (QuicUtils::IsCryptoStreamId(transport_version(), frame.stream_id) || |
| last_received_packet_info_.decrypted_level != ENCRYPTION_INITIAL) { |
| return false; |
| } |
| |
| if (perspective_ == Perspective::IS_SERVER && |
| frame.data_length >= sizeof(kCHLO) && |
| strncmp(frame.data_buffer, reinterpret_cast<const char*>(&kCHLO), |
| sizeof(kCHLO)) == 0) { |
| return true; |
| } |
| |
| if (perspective_ == Perspective::IS_CLIENT && |
| frame.data_length >= sizeof(kREJ) && |
| strncmp(frame.data_buffer, reinterpret_cast<const char*>(&kREJ), |
| sizeof(kREJ)) == 0) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void QuicConnection::CheckIfApplicationLimited() { |
| if (!connected_) { |
| return; |
| } |
| |
| bool application_limited = |
| buffered_packets_.empty() && !visitor_->WillingAndAbleToWrite(); |
| |
| if (!application_limited) { |
| return; |
| } |
| |
| sent_packet_manager_.OnApplicationLimited(); |
| } |
| |
| bool QuicConnection::UpdatePacketContent(QuicFrameType type) { |
| last_received_packet_info_.frames.push_back(type); |
| if (version().HasIetfQuicFrames()) { |
| if (!QuicUtils::IsProbingFrame(type)) { |
| MaybeStartIetfPeerMigration(); |
| return connected_; |
| } |
| QuicSocketAddress current_effective_peer_address = |
| GetEffectivePeerAddressFromCurrentPacket(); |
| if (IsDefaultPath(last_received_packet_info_.destination_address, |
| last_received_packet_info_.source_address)) { |
| return connected_; |
| } |
| if (perspective_ == Perspective::IS_SERVER && |
| type == PATH_CHALLENGE_FRAME && |
| !IsAlternativePath(last_received_packet_info_.destination_address, |
| current_effective_peer_address)) { |
| QUIC_DVLOG(1) |
| << "The peer is probing a new path with effective peer address " |
| << current_effective_peer_address << ", self address " |
| << last_received_packet_info_.destination_address; |
| if (!validate_client_addresses_) { |
| QuicConnectionId client_cid; |
| absl::optional<StatelessResetToken> stateless_reset_token; |
| FindMatchingOrNewClientConnectionIdOrToken( |
| default_path_, alternative_path_, |
| last_received_packet_info_.destination_connection_id, &client_cid, |
| &stateless_reset_token); |
| alternative_path_ = |
| PathState(last_received_packet_info_.destination_address, |
| current_effective_peer_address, client_cid, |
| last_received_packet_info_.destination_connection_id, |
| stateless_reset_token); |
| } else if (!default_path_.validated) { |
| // Skip reverse path validation because either handshake hasn't |
| // completed or the connection is validating the default path. Using |
| // PATH_CHALLENGE to validate alternative client address before |
| // handshake gets comfirmed is meaningless because anyone can respond to |
| // it. If the connection is validating the default path, this |
| // alternative path is currently the only validated path which shouldn't |
| // be overridden. |
| QUIC_DVLOG(1) << "The connection hasn't finished handshake or is " |
| "validating a recent peer address change."; |
| QUIC_BUG_IF(quic_bug_12714_30, |
| IsHandshakeConfirmed() && !alternative_path_.validated) |
| << "No validated peer address to send after handshake comfirmed."; |
| } else if (!IsReceivedPeerAddressValidated()) { |
| QuicConnectionId client_connection_id; |
| absl::optional<StatelessResetToken> stateless_reset_token; |
| FindMatchingOrNewClientConnectionIdOrToken( |
| default_path_, alternative_path_, |
| last_received_packet_info_.destination_connection_id, |
| &client_connection_id, &stateless_reset_token); |
| // Only override alternative path state upon receiving a PATH_CHALLENGE |
| // from an unvalidated peer address, and the connection isn't validating |
| // a recent peer migration. |
| alternative_path_ = |
| PathState(last_received_packet_info_.destination_address, |
| current_effective_peer_address, client_connection_id, |
| last_received_packet_info_.destination_connection_id, |
| stateless_reset_token); |
| should_proactively_validate_peer_address_on_path_challenge_ = true; |
| } |
| } |
| MaybeUpdateBytesReceivedFromAlternativeAddress( |
| last_received_packet_info_.length); |
| return connected_; |
| } |
| // Packet content is tracked to identify connectivity probe in non-IETF |
| // version, where a connectivity probe is defined as |
| // - a padded PING packet with peer address change received by server, |
| // - a padded PING packet on new path received by client. |
| |
| if (current_packet_content_ == NOT_PADDED_PING) { |
| // We have already learned the current packet is not a connectivity |
| // probing packet. Peer migration should have already been started earlier |
| // if needed. |
| return connected_; |
| } |
| |
| if (type == PING_FRAME) { |
| if (current_packet_content_ == NO_FRAMES_RECEIVED) { |
| current_packet_content_ = FIRST_FRAME_IS_PING; |
| return connected_; |
| } |
| } |
| |
| // In Google QUIC, we look for a packet with just a PING and PADDING. |
| // If the condition is met, mark things as connectivity-probing, causing |
| // later processing to generate the correct response. |
| if (type == PADDING_FRAME && current_packet_content_ == FIRST_FRAME_IS_PING) { |
| current_packet_content_ = SECOND_FRAME_IS_PADDING; |
| if (perspective_ == Perspective::IS_SERVER) { |
| is_current_packet_connectivity_probing_ = |
| current_effective_peer_migration_type_ != NO_CHANGE; |
| QUIC_DLOG_IF(INFO, is_current_packet_connectivity_probing_) |
| << ENDPOINT |
| << "Detected connectivity probing packet. " |
| "current_effective_peer_migration_type_:" |
| << current_effective_peer_migration_type_; |
| } else { |
| is_current_packet_connectivity_probing_ = |
| (last_received_packet_info_.source_address != peer_address()) || |
| (last_received_packet_info_.destination_address != |
| default_path_.self_address); |
| QUIC_DLOG_IF(INFO, is_current_packet_connectivity_probing_) |
| << ENDPOINT |
| << "Detected connectivity probing packet. " |
| "last_packet_source_address:" |
| << last_received_packet_info_.source_address |
| << ", peer_address_:" << peer_address() |
| << ", last_packet_destination_address:" |
| << last_received_packet_info_.destination_address |
| << ", default path self_address :" << default_path_.self_address; |
| } |
| return connected_; |
| } |
| |
| current_packet_content_ = NOT_PADDED_PING; |
| if (GetLargestReceivedPacket().IsInitialized() && |
| last_received_packet_info_.header.packet_number == |
| GetLargestReceivedPacket()) { |
| UpdatePeerAddress(last_received_packet_info_.source_address); |
| if (current_effective_peer_migration_type_ != NO_CHANGE) { |
| // Start effective peer migration immediately when the current packet is |
| // confirmed not a connectivity probing packet. |
| StartEffectivePeerMigration(current_effective_peer_migration_type_); |
| } |
| } |
| current_effective_peer_migration_type_ = NO_CHANGE; |
| return connected_; |
| } |
| |
| void QuicConnection::MaybeStartIetfPeerMigration() { |
| QUICHE_DCHECK(version().HasIetfQuicFrames()); |
| if (current_effective_peer_migration_type_ != NO_CHANGE && |
| !IsHandshakeConfirmed()) { |
| QUIC_LOG_EVERY_N_SEC(INFO, 60) |
| << ENDPOINT << "Effective peer's ip:port changed from " |
| << default_path_.peer_address.ToString() << " to " |
| << GetEffectivePeerAddressFromCurrentPacket().ToString() |
| << " before handshake confirmed, " |
| "current_effective_peer_migration_type_: " |
| << current_effective_peer_migration_type_; |
| // Peer migrated before handshake gets confirmed. |
| CloseConnection((current_effective_peer_migration_type_ == PORT_CHANGE |
| ? QUIC_PEER_PORT_CHANGE_HANDSHAKE_UNCONFIRMED |
| : QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED), |
| "Peer address changed before handshake is confirmed.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return; |
| } |
| |
| if (GetLargestReceivedPacket().IsInitialized() && |
| last_received_packet_info_.header.packet_number == |
| GetLargestReceivedPacket()) { |
| if (current_effective_peer_migration_type_ != NO_CHANGE) { |
| // Start effective peer migration when the current packet contains a |
| // non-probing frame. |
| // TODO(fayang): When multiple packet number spaces is supported, only |
| // start peer migration for the application data. |
| if (!validate_client_addresses_) { |
| UpdatePeerAddress(last_received_packet_info_.source_address); |
| } |
| StartEffectivePeerMigration(current_effective_peer_migration_type_); |
| } else { |
| UpdatePeerAddress(last_received_packet_info_.source_address); |
| } |
| } |
| current_effective_peer_migration_type_ = NO_CHANGE; |
| } |
| |
| void QuicConnection::PostProcessAfterAckFrame(bool send_stop_waiting, |
| bool acked_new_packet) { |
| if (no_stop_waiting_frames_ && !packet_creator_.has_ack()) { |
| uber_received_packet_manager_.DontWaitForPacketsBefore( |
| last_received_packet_info_.decrypted_level, |
| SupportsMultiplePacketNumberSpaces() |
| ? sent_packet_manager_.GetLargestPacketPeerKnowsIsAcked( |
| last_received_packet_info_.decrypted_level) |
| : sent_packet_manager_.largest_packet_peer_knows_is_acked()); |
| } |
| // Always reset the retransmission alarm when an ack comes in, since we now |
| // have a better estimate of the current rtt than when it was set. |
| SetRetransmissionAlarm(); |
| if (acked_new_packet) { |
| OnForwardProgressMade(); |
| } else if (default_enable_5rto_blackhole_detection_ && |
| !sent_packet_manager_.HasInFlightPackets() && |
| blackhole_detector_.IsDetectionInProgress()) { |
| // In case no new packets get acknowledged, it is possible packets are |
| // detected lost because of time based loss detection. Cancel blackhole |
| // detection if there is no packets in flight. |
| blackhole_detector_.StopDetection(/*permanent=*/false); |
| } |
| |
| if (send_stop_waiting) { |
| ++stop_waiting_count_; |
| } else { |
| stop_waiting_count_ = 0; |
| } |
| } |
| |
| void QuicConnection::SetSessionNotifier( |
| SessionNotifierInterface* session_notifier) { |
| sent_packet_manager_.SetSessionNotifier(session_notifier); |
| } |
| |
| void QuicConnection::SetDataProducer( |
| QuicStreamFrameDataProducer* data_producer) { |
| framer_.set_data_producer(data_producer); |
| } |
| |
| void QuicConnection::SetTransmissionType(TransmissionType type) { |
| packet_creator_.SetTransmissionType(type); |
| } |
| |
| void QuicConnection::UpdateReleaseTimeIntoFuture() { |
| QUICHE_DCHECK(supports_release_time_); |
| |
| const QuicTime::Delta prior_max_release_time = release_time_into_future_; |
| release_time_into_future_ = std::max( |
| QuicTime::Delta::FromMilliseconds(kMinReleaseTimeIntoFutureMs), |
| std::min(QuicTime::Delta::FromMilliseconds( |
| GetQuicFlag(quic_max_pace_time_into_future_ms)), |
| sent_packet_manager_.GetRttStats()->SmoothedOrInitialRtt() * |
| GetQuicFlag(quic_pace_time_into_future_srtt_fraction))); |
| QUIC_DVLOG(3) << "Updated max release time delay from " |
| << prior_max_release_time << " to " |
| << release_time_into_future_; |
| } |
| |
| void QuicConnection::ResetAckStates() { |
| ack_alarm_->Cancel(); |
| stop_waiting_count_ = 0; |
| uber_received_packet_manager_.ResetAckStates(encryption_level_); |
| } |
| |
| MessageStatus QuicConnection::SendMessage( |
| QuicMessageId message_id, absl::Span<quiche::QuicheMemSlice> message, |
| bool flush) { |
| if (!VersionSupportsMessageFrames(transport_version())) { |
| QUIC_BUG(quic_bug_10511_38) |
| << "MESSAGE frame is not supported for version " << transport_version(); |
| return MESSAGE_STATUS_UNSUPPORTED; |
| } |
| if (MemSliceSpanTotalSize(message) > GetCurrentLargestMessagePayload()) { |
| return MESSAGE_STATUS_TOO_LARGE; |
| } |
| if (!connected_ || (!flush && !CanWrite(HAS_RETRANSMITTABLE_DATA))) { |
| return MESSAGE_STATUS_BLOCKED; |
| } |
| ScopedPacketFlusher flusher(this); |
| return packet_creator_.AddMessageFrame(message_id, message); |
| } |
| |
| QuicPacketLength QuicConnection::GetCurrentLargestMessagePayload() const { |
| return packet_creator_.GetCurrentLargestMessagePayload(); |
| } |
| |
| QuicPacketLength QuicConnection::GetGuaranteedLargestMessagePayload() const { |
| return packet_creator_.GetGuaranteedLargestMessagePayload(); |
| } |
| |
| uint32_t QuicConnection::cipher_id() const { |
| if (version().KnowsWhichDecrypterToUse()) { |
| return framer_.GetDecrypter(last_received_packet_info_.decrypted_level) |
| ->cipher_id(); |
| } |
| return framer_.decrypter()->cipher_id(); |
| } |
| |
| EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const { |
| if (perspective_ == Perspective::IS_CLIENT) { |
| return encryption_level_; |
| } |
| if (IsHandshakeComplete()) { |
| // A forward secure packet has been received. |
| QUIC_BUG_IF(quic_bug_12714_31, |
| encryption_level_ != ENCRYPTION_FORWARD_SECURE) |
| << ENDPOINT << "Unexpected connection close encryption level " |
| << encryption_level_; |
| return ENCRYPTION_FORWARD_SECURE; |
| } |
| if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_ZERO_RTT)) { |
| if (encryption_level_ != ENCRYPTION_ZERO_RTT) { |
| if (version().HasIetfInvariantHeader()) { |
| QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close_ietf); |
| } else { |
| QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close); |
| } |
| } |
| return ENCRYPTION_ZERO_RTT; |
| } |
| return ENCRYPTION_INITIAL; |
| } |
| |
| void QuicConnection::MaybeBundleCryptoDataWithAcks() { |
| QUICHE_DCHECK(SupportsMultiplePacketNumberSpaces()); |
| if (IsHandshakeConfirmed()) { |
| return; |
| } |
| PacketNumberSpace space = HANDSHAKE_DATA; |
| if (perspective() == Perspective::IS_SERVER && |
| framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL)) { |
| // On the server side, sends INITIAL data with INITIAL ACK if initial key is |
| // available. |
| space = INITIAL_DATA; |
| } |
| const QuicTime ack_timeout = |
| uber_received_packet_manager_.GetAckTimeout(space); |
| if (!ack_timeout.IsInitialized() || |
| (ack_timeout > clock_->ApproximateNow() && |
| ack_timeout > uber_received_packet_manager_.GetEarliestAckTimeout())) { |
| // No pending ACK of space. |
| return; |
| } |
| if (coalesced_packet_.length() > 0) { |
| // Do not bundle CRYPTO data if the ACK could be coalesced with other |
| // packets. |
| return; |
| } |
| |
| if (!framer_.HasAnEncrypterForSpace(space)) { |
| QUIC_BUG(quic_bug_10511_39) |
| << ENDPOINT |
| << "Try to bundle crypto with ACK with missing key of space " |
| << PacketNumberSpaceToString(space); |
| return; |
| } |
| |
| sent_packet_manager_.RetransmitDataOfSpaceIfAny(space); |
| } |
| |
| void QuicConnection::SendAllPendingAcks() { |
| QUICHE_DCHECK(SupportsMultiplePacketNumberSpaces()); |
| QUIC_DVLOG(1) << ENDPOINT << "Trying to send all pending ACKs"; |
| ack_alarm_->Cancel(); |
| QuicTime earliest_ack_timeout = |
| uber_received_packet_manager_.GetEarliestAckTimeout(); |
| QUIC_BUG_IF(quic_bug_12714_32, !earliest_ack_timeout.IsInitialized()); |
| MaybeBundleCryptoDataWithAcks(); |
| earliest_ack_timeout = uber_received_packet_manager_.GetEarliestAckTimeout(); |
| if (!earliest_ack_timeout.IsInitialized()) { |
| return; |
| } |
| for (int8_t i = INITIAL_DATA; i <= APPLICATION_DATA; ++i) { |
| const QuicTime ack_timeout = uber_received_packet_manager_.GetAckTimeout( |
| static_cast<PacketNumberSpace>(i)); |
| if (!ack_timeout.IsInitialized()) { |
| continue; |
| } |
| if (!framer_.HasAnEncrypterForSpace(static_cast<PacketNumberSpace>(i))) { |
| // The key has been dropped. |
| continue; |
| } |
| if (ack_timeout > clock_->ApproximateNow() && |
| ack_timeout > earliest_ack_timeout) { |
| // Always send the earliest ACK to make forward progress in case alarm |
| // fires early. |
| continue; |
| } |
| QUIC_DVLOG(1) << ENDPOINT << "Sending ACK of packet number space " |
| << PacketNumberSpaceToString( |
| static_cast<PacketNumberSpace>(i)); |
| ScopedEncryptionLevelContext context( |
| this, QuicUtils::GetEncryptionLevelToSendAckofSpace( |
| static_cast<PacketNumberSpace>(i))); |
| QuicFrames frames; |
| frames.push_back(uber_received_packet_manager_.GetUpdatedAckFrame( |
| static_cast<PacketNumberSpace>(i), clock_->ApproximateNow())); |
| const bool flushed = packet_creator_.FlushAckFrame(frames); |
| if (!flushed) { |
| // Connection is write blocked. |
| QUIC_BUG_IF(quic_bug_12714_33, |
| !writer_->IsWriteBlocked() && |
| !LimitedByAmplificationFactor( |
| packet_creator_.max_packet_length())) |
| << "Writer not blocked and not throttled by amplification factor, " |
| "but ACK not flushed for packet space:" |
| << i; |
| break; |
| } |
| ResetAckStates(); |
| } |
| |
| const QuicTime timeout = |
| uber_received_packet_manager_.GetEarliestAckTimeout(); |
| if (timeout.IsInitialized()) { |
| // If there are ACKs pending, re-arm ack alarm. |
| ack_alarm_->Update(timeout, kAlarmGranularity); |
| } |
| // Only try to bundle retransmittable data with ACK frame if default |
| // encryption level is forward secure. |
| if (encryption_level_ != ENCRYPTION_FORWARD_SECURE || |
| !ShouldBundleRetransmittableFrameWithAck()) { |
| return; |
| } |
| consecutive_num_packets_with_no_retransmittable_frames_ = 0; |
| if (packet_creator_.HasPendingRetransmittableFrames() || |
| visitor_->WillingAndAbleToWrite()) { |
| // There are pending retransmittable frames. |
| return; |
| } |
| |
| visitor_->OnAckNeedsRetransmittableFrame(); |
| } |
| |
| bool QuicConnection::ShouldBundleRetransmittableFrameWithAck() const { |
| if (consecutive_num_packets_with_no_retransmittable_frames_ >= |
| max_consecutive_num_packets_with_no_retransmittable_frames_) { |
| return true; |
| } |
| if (bundle_retransmittable_with_pto_ack_ && |
| sent_packet_manager_.GetConsecutivePtoCount() > 0) { |
| // Bundle a retransmittable frame with an ACK if PTO has fired in order to |
| // recover more quickly in cases of temporary network outage. |
| return true; |
| } |
| return false; |
| } |
| |
| void QuicConnection::MaybeCoalescePacketOfHigherSpace() { |
| if (!connected() || !packet_creator_.HasSoftMaxPacketLength()) { |
| return; |
| } |
| if (fill_coalesced_packet_) { |
| // Make sure MaybeCoalescePacketOfHigherSpace is not re-entrant. |
| QUIC_BUG(quic_coalesce_packet_reentrant); |
| return; |
| } |
| for (EncryptionLevel retransmission_level : |
| {ENCRYPTION_INITIAL, ENCRYPTION_HANDSHAKE}) { |
| // Coalesce HANDSHAKE with INITIAL retransmission, and coalesce 1-RTT with |
| // HANDSHAKE retransmission. |
| const EncryptionLevel coalesced_level = |
| retransmission_level == ENCRYPTION_INITIAL ? ENCRYPTION_HANDSHAKE |
| : ENCRYPTION_FORWARD_SECURE; |
| if (coalesced_packet_.ContainsPacketOfEncryptionLevel( |
| retransmission_level) && |
| coalesced_packet_.TransmissionTypeOfPacket(retransmission_level) != |
| NOT_RETRANSMISSION && |
| framer_.HasEncrypterOfEncryptionLevel(coalesced_level) && |
| !coalesced_packet_.ContainsPacketOfEncryptionLevel(coalesced_level)) { |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Trying to coalesce packet of encryption level: " |
| << EncryptionLevelToString(coalesced_level); |
| fill_coalesced_packet_ = true; |
| sent_packet_manager_.RetransmitDataOfSpaceIfAny( |
| QuicUtils::GetPacketNumberSpace(coalesced_level)); |
| fill_coalesced_packet_ = false; |
| } |
| } |
| } |
| |
| bool QuicConnection::FlushCoalescedPacket() { |
| ScopedCoalescedPacketClearer clearer(&coalesced_packet_); |
| if (!connected_) { |
| return false; |
| } |
| if (!version().CanSendCoalescedPackets()) { |
| QUIC_BUG_IF(quic_bug_12714_34, coalesced_packet_.length() > 0); |
| return true; |
| } |
| if (coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) && |
| !framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_INITIAL)) { |
| // Initial packet will be re-serialized. Neuter it in case initial key has |
| // been dropped. |
| QUIC_BUG(quic_bug_10511_40) |
| << ENDPOINT |
| << "Coalescer contains initial packet while initial key has " |
| "been dropped."; |
| coalesced_packet_.NeuterInitialPacket(); |
| } |
| if (coalesced_packet_.length() == 0) { |
| return true; |
| } |
| |
| char buffer[kMaxOutgoingPacketSize]; |
| const size_t length = packet_creator_.SerializeCoalescedPacket( |
| coalesced_packet_, buffer, coalesced_packet_.max_packet_length()); |
| if (length == 0) { |
| if (connected_) { |
| CloseConnection(QUIC_FAILED_TO_SERIALIZE_PACKET, |
| "Failed to serialize coalesced packet.", |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| } |
| return false; |
| } |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnCoalescedPacketSent(coalesced_packet_, length); |
| } |
| QUIC_DVLOG(1) << ENDPOINT << "Sending coalesced packet " |
| << coalesced_packet_.ToString(length); |
| const size_t padding_size = |
| length - std::min<size_t>(length, coalesced_packet_.length()); |
| // Buffer coalesced packet if padding + bytes_sent exceeds amplifcation limit. |
| if (!buffered_packets_.empty() || HandleWriteBlocked() || |
| (enforce_strict_amplification_factor_ && |
| LimitedByAmplificationFactor(padding_size))) { |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Buffering coalesced packet of len: " << length; |
| buffered_packets_.emplace_back( |
| buffer, static_cast<QuicPacketLength>(length), |
| coalesced_packet_.self_address(), coalesced_packet_.peer_address()); |
| } else { |
| WriteResult result = writer_->WritePacket( |
| buffer, length, coalesced_packet_.self_address().host(), |
| coalesced_packet_.peer_address(), per_packet_options_); |
| if (IsWriteError(result.status)) { |
| OnWriteError(result.error_code); |
| return false; |
| } |
| if (IsWriteBlockedStatus(result.status)) { |
| visitor_->OnWriteBlocked(); |
| if (result.status != WRITE_STATUS_BLOCKED_DATA_BUFFERED) { |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Buffering coalesced packet of len: " << length; |
| buffered_packets_.emplace_back( |
| buffer, static_cast<QuicPacketLength>(length), |
| coalesced_packet_.self_address(), coalesced_packet_.peer_address()); |
| } |
| } |
| } |
| // Account for added padding. |
| if (length > coalesced_packet_.length()) { |
| if (IsDefaultPath(coalesced_packet_.self_address(), |
| coalesced_packet_.peer_address())) { |
| if (EnforceAntiAmplificationLimit()) { |
| // Include bytes sent even if they are not in flight. |
| default_path_.bytes_sent_before_address_validation += padding_size; |
| } |
| } else { |
| MaybeUpdateBytesSentToAlternativeAddress(coalesced_packet_.peer_address(), |
| padding_size); |
| } |
| stats_.bytes_sent += padding_size; |
| if (coalesced_packet_.initial_packet() != nullptr && |
| coalesced_packet_.initial_packet()->transmission_type != |
| NOT_RETRANSMISSION) { |
| stats_.bytes_retransmitted += padding_size; |
| } |
| } |
| return true; |
| } |
| |
| void QuicConnection::MaybeEnableMultiplePacketNumberSpacesSupport() { |
| if (version().handshake_protocol != PROTOCOL_TLS1_3) { |
| return; |
| } |
| QUIC_DVLOG(1) << ENDPOINT << "connection " << connection_id() |
| << " supports multiple packet number spaces"; |
| framer_.EnableMultiplePacketNumberSpacesSupport(); |
| sent_packet_manager_.EnableMultiplePacketNumberSpacesSupport(); |
| uber_received_packet_manager_.EnableMultiplePacketNumberSpacesSupport( |
| perspective_); |
| } |
| |
| bool QuicConnection::SupportsMultiplePacketNumberSpaces() const { |
| return sent_packet_manager_.supports_multiple_packet_number_spaces(); |
| } |
| |
| void QuicConnection::SetLargestReceivedPacketWithAck( |
| QuicPacketNumber new_value) { |
| if (SupportsMultiplePacketNumberSpaces()) { |
| largest_seen_packets_with_ack_[QuicUtils::GetPacketNumberSpace( |
| last_received_packet_info_.decrypted_level)] = new_value; |
| } else { |
| largest_seen_packet_with_ack_ = new_value; |
| } |
| } |
| |
| void QuicConnection::OnForwardProgressMade() { |
| if (!connected_) { |
| return; |
| } |
| if (is_path_degrading_) { |
| visitor_->OnForwardProgressMadeAfterPathDegrading(); |
| is_path_degrading_ = false; |
| } |
| if (sent_packet_manager_.HasInFlightPackets()) { |
| // Restart detections if forward progress has been made. |
| blackhole_detector_.RestartDetection(GetPathDegradingDeadline(), |
| GetNetworkBlackholeDeadline(), |
| GetPathMtuReductionDeadline()); |
| } else { |
| // Stop detections in quiecense. |
| blackhole_detector_.StopDetection(/*permanent=*/false); |
| } |
| QUIC_BUG_IF(quic_bug_12714_35, |
| perspective_ == Perspective::IS_SERVER && |
| default_enable_5rto_blackhole_detection_ && |
| blackhole_detector_.IsDetectionInProgress() && |
| !sent_packet_manager_.HasInFlightPackets()) |
| << ENDPOINT |
| << "Trying to start blackhole detection without no bytes in flight"; |
| } |
| |
| QuicPacketNumber QuicConnection::GetLargestReceivedPacketWithAck() const { |
| if (SupportsMultiplePacketNumberSpaces()) { |
| return largest_seen_packets_with_ack_[QuicUtils::GetPacketNumberSpace( |
| last_received_packet_info_.decrypted_level)]; |
| } |
| return largest_seen_packet_with_ack_; |
| } |
| |
| QuicPacketNumber QuicConnection::GetLargestAckedPacket() const { |
| if (SupportsMultiplePacketNumberSpaces()) { |
| return sent_packet_manager_.GetLargestAckedPacket( |
| last_received_packet_info_.decrypted_level); |
| } |
| return sent_packet_manager_.GetLargestObserved(); |
| } |
| |
| QuicPacketNumber QuicConnection::GetLargestReceivedPacket() const { |
| return uber_received_packet_manager_.GetLargestObserved( |
| last_received_packet_info_.decrypted_level); |
| } |
| |
| bool QuicConnection::EnforceAntiAmplificationLimit() const { |
| return version().SupportsAntiAmplificationLimit() && |
| perspective_ == Perspective::IS_SERVER && !default_path_.validated; |
| } |
| |
| // TODO(danzh) Pass in path object or its reference of some sort to use this |
| // method to check anti-amplification limit on non-default path. |
| bool QuicConnection::LimitedByAmplificationFactor(QuicByteCount bytes) const { |
| return EnforceAntiAmplificationLimit() && |
| (default_path_.bytes_sent_before_address_validation + |
| (enforce_strict_amplification_factor_ ? bytes : 0)) >= |
| anti_amplification_factor_ * |
| default_path_.bytes_received_before_address_validation; |
| } |
| |
| SerializedPacketFate QuicConnection::GetSerializedPacketFate( |
| bool is_mtu_discovery, EncryptionLevel encryption_level) { |
| if (ShouldDiscardPacket(encryption_level)) { |
| return DISCARD; |
| } |
| if (legacy_version_encapsulation_in_progress_) { |
| QUICHE_DCHECK(!is_mtu_discovery); |
| return LEGACY_VERSION_ENCAPSULATE; |
| } |
| if (version().CanSendCoalescedPackets() && !coalescing_done_ && |
| !is_mtu_discovery) { |
| if (!IsHandshakeConfirmed()) { |
| // Before receiving ACK for any 1-RTT packets, always try to coalesce |
| // packet (except MTU discovery packet). |
| return COALESCE; |
| } |
| if (coalesced_packet_.length() > 0) { |
| // If the coalescer is not empty, let this packet go through coalescer |
| // to avoid potential out of order sending. |
| return COALESCE; |
| } |
| } |
| if (!buffered_packets_.empty() || HandleWriteBlocked()) { |
| return BUFFER; |
| } |
| return SEND_TO_WRITER; |
| } |
| |
| bool QuicConnection::IsHandshakeComplete() const { |
| return visitor_->GetHandshakeState() >= HANDSHAKE_COMPLETE; |
| } |
| |
| bool QuicConnection::IsHandshakeConfirmed() const { |
| QUICHE_DCHECK_EQ(PROTOCOL_TLS1_3, version().handshake_protocol); |
| return visitor_->GetHandshakeState() == HANDSHAKE_CONFIRMED; |
| } |
| |
| size_t QuicConnection::min_received_before_ack_decimation() const { |
| return uber_received_packet_manager_.min_received_before_ack_decimation(); |
| } |
| |
| void QuicConnection::set_min_received_before_ack_decimation(size_t new_value) { |
| uber_received_packet_manager_.set_min_received_before_ack_decimation( |
| new_value); |
| } |
| |
| const QuicAckFrame& QuicConnection::ack_frame() const { |
| if (SupportsMultiplePacketNumberSpaces()) { |
| return uber_received_packet_manager_.GetAckFrame( |
| QuicUtils::GetPacketNumberSpace( |
| last_received_packet_info_.decrypted_level)); |
| } |
| return uber_received_packet_manager_.ack_frame(); |
| } |
| |
| void QuicConnection::set_client_connection_id( |
| QuicConnectionId client_connection_id) { |
| if (!version().SupportsClientConnectionIds()) { |
| QUIC_BUG_IF(quic_bug_12714_36, !client_connection_id.IsEmpty()) |
| << ENDPOINT << "Attempted to use client connection ID " |
| << client_connection_id << " with unsupported version " << version(); |
| return; |
| } |
| default_path_.client_connection_id = client_connection_id; |
| |
| client_connection_id_is_set_ = true; |
| if (version().HasIetfQuicFrames() && !client_connection_id.IsEmpty()) { |
| if (perspective_ == Perspective::IS_SERVER) { |
| QUICHE_DCHECK(peer_issued_cid_manager_ == nullptr); |
| peer_issued_cid_manager_ = |
| std::make_unique<QuicPeerIssuedConnectionIdManager>( |
| kMinNumOfActiveConnectionIds, client_connection_id, clock_, |
| alarm_factory_, this, context()); |
| } else { |
| // Note in Chromium client, set_client_connection_id is not called and |
| // thus self_issued_cid_manager_ should be null. |
| self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager(); |
| } |
| } |
| QUIC_DLOG(INFO) << ENDPOINT << "setting client connection ID to " |
| << default_path_.client_connection_id |
| << " for connection with server connection ID " |
| << default_path_.server_connection_id; |
| packet_creator_.SetClientConnectionId(default_path_.client_connection_id); |
| framer_.SetExpectedClientConnectionIdLength( |
| default_path_.client_connection_id.length()); |
| } |
| |
| void QuicConnection::OnPathDegradingDetected() { |
| is_path_degrading_ = true; |
| if (multi_port_stats_) { |
| multi_port_stats_->num_path_degrading++; |
| } |
| visitor_->OnPathDegrading(); |
| } |
| |
| void QuicConnection::OnBlackholeDetected() { |
| if (default_enable_5rto_blackhole_detection_ && |
| !sent_packet_manager_.HasInFlightPackets()) { |
| QUIC_BUG(quic_bug_10511_41) |
| << ENDPOINT |
| << "Blackhole detected, but there is no bytes in flight, version: " |
| << version(); |
| // Do not close connection if there is no bytes in flight. |
| return; |
| } |
| CloseConnection(QUIC_TOO_MANY_RTOS, "Network blackhole detected", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| } |
| |
| void QuicConnection::OnPathMtuReductionDetected() { |
| MaybeRevertToPreviousMtu(); |
| } |
| |
| void QuicConnection::OnHandshakeTimeout() { |
| const QuicTime::Delta duration = |
| clock_->ApproximateNow() - stats_.connection_creation_time; |
| std::string error_details = absl::StrCat( |
| "Handshake timeout expired after ", duration.ToDebuggingValue(), |
| ". Timeout:", |
| idle_network_detector_.handshake_timeout().ToDebuggingValue()); |
| if (perspective() == Perspective::IS_CLIENT && version().UsesTls()) { |
| absl::StrAppend(&error_details, UndecryptablePacketsInfo()); |
| } |
| QUIC_DVLOG(1) << ENDPOINT << error_details; |
| CloseConnection(QUIC_HANDSHAKE_TIMEOUT, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| } |
| |
| void QuicConnection::OnIdleNetworkDetected() { |
| const QuicTime::Delta duration = |
| clock_->ApproximateNow() - |
| idle_network_detector_.last_network_activity_time(); |
| std::string error_details = absl::StrCat( |
| "No recent network activity after ", duration.ToDebuggingValue(), |
| ". Timeout:", |
| idle_network_detector_.idle_network_timeout().ToDebuggingValue()); |
| if (perspective() == Perspective::IS_CLIENT && version().UsesTls() && |
| !IsHandshakeComplete()) { |
| absl::StrAppend(&error_details, UndecryptablePacketsInfo()); |
| } |
| QUIC_DVLOG(1) << ENDPOINT << error_details; |
| const bool has_consecutive_pto = |
| sent_packet_manager_.GetConsecutivePtoCount() > 0; |
| if (has_consecutive_pto || visitor_->ShouldKeepConnectionAlive()) { |
| if (GetQuicReloadableFlag(quic_add_stream_info_to_idle_close_detail) && |
| !has_consecutive_pto) { |
| // Include stream information in error detail if there are open streams. |
| QUIC_RELOADABLE_FLAG_COUNT(quic_add_stream_info_to_idle_close_detail); |
| absl::StrAppend(&error_details, ", ", |
| visitor_->GetStreamsInfoForLogging()); |
| } |
| CloseConnection(QUIC_NETWORK_IDLE_TIMEOUT, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return; |
| } |
| QuicErrorCode error_code = QUIC_NETWORK_IDLE_TIMEOUT; |
| if (idle_timeout_connection_close_behavior_ == |
| ConnectionCloseBehavior:: |
| SILENT_CLOSE_WITH_CONNECTION_CLOSE_PACKET_SERIALIZED) { |
| error_code = QUIC_SILENT_IDLE_TIMEOUT; |
| } |
| CloseConnection(error_code, error_details, |
| idle_timeout_connection_close_behavior_); |
| } |
| |
| void QuicConnection::OnBandwidthUpdateTimeout() { |
| visitor_->OnBandwidthUpdateTimeout(); |
| } |
| |
| void QuicConnection::OnKeepAliveTimeout() { |
| QUICHE_DCHECK(use_ping_manager_); |
| if (retransmission_alarm_->IsSet() || |
| !visitor_->ShouldKeepConnectionAlive()) { |
| return; |
| } |
| SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData()); |
| } |
| |
| void QuicConnection::OnRetransmittableOnWireTimeout() { |
| QUICHE_DCHECK(use_ping_manager_); |
| if (retransmission_alarm_->IsSet() || |
| !visitor_->ShouldKeepConnectionAlive()) { |
| return; |
| } |
| bool packet_buffered = false; |
| switch (retransmittable_on_wire_behavior_) { |
| case DEFAULT: |
| break; |
| case SEND_FIRST_FORWARD_SECURE_PACKET: |
| if (first_serialized_one_rtt_packet_ != nullptr) { |
| buffered_packets_.emplace_back( |
| first_serialized_one_rtt_packet_->data.get(), |
| first_serialized_one_rtt_packet_->length, self_address(), |
| peer_address()); |
| packet_buffered = true; |
| } |
| break; |
| case SEND_RANDOM_BYTES: |
| const QuicPacketLength random_bytes_length = std::max<QuicPacketLength>( |
| QuicFramer::GetMinStatelessResetPacketLength() + 1, |
| random_generator_->RandUint64() % |
| packet_creator_.max_packet_length()); |
| buffered_packets_.emplace_back(*random_generator_, random_bytes_length, |
| self_address(), peer_address()); |
| packet_buffered = true; |
| break; |
| } |
| if (packet_buffered) { |
| if (!writer_->IsWriteBlocked()) { |
| WriteQueuedPackets(); |
| } |
| if (connected_) { |
| // Always reset PING alarm with has_in_flight_packets=true. This is used |
| // to avoid re-arming the alarm in retransmittable-on-wire mode. |
| ping_manager_.SetAlarm(clock_->ApproximateNow(), |
| visitor_->ShouldKeepConnectionAlive(), |
| /*has_in_flight_packets=*/true); |
| } |
| return; |
| } |
| SendPingAtLevel(framer().GetEncryptionLevelToSendApplicationData()); |
| } |
| |
| void QuicConnection::OnPeerIssuedConnectionIdRetired() { |
| QUICHE_DCHECK(peer_issued_cid_manager_ != nullptr); |
| QuicConnectionId* default_path_cid = |
| perspective_ == Perspective::IS_CLIENT |
| ? &default_path_.server_connection_id |
| : &default_path_.client_connection_id; |
| QuicConnectionId* alternative_path_cid = |
| perspective_ == Perspective::IS_CLIENT |
| ? &alternative_path_.server_connection_id |
| : &alternative_path_.client_connection_id; |
| bool default_path_and_alternative_path_use_the_same_peer_connection_id = |
| *default_path_cid == *alternative_path_cid; |
| if (!default_path_cid->IsEmpty() && |
| !peer_issued_cid_manager_->IsConnectionIdActive(*default_path_cid)) { |
| *default_path_cid = QuicConnectionId(); |
| } |
| // TODO(haoyuewang) Handle the change for default_path_ & alternatvie_path_ |
| // via the same helper function. |
| if (default_path_cid->IsEmpty()) { |
| // Try setting a new connection ID now such that subsequent |
| // RetireConnectionId frames can be sent on the default path. |
| const QuicConnectionIdData* unused_connection_id_data = |
| peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); |
| if (unused_connection_id_data != nullptr) { |
| *default_path_cid = unused_connection_id_data->connection_id; |
| default_path_.stateless_reset_token = |
| unused_connection_id_data->stateless_reset_token; |
| if (perspective_ == Perspective::IS_CLIENT) { |
| packet_creator_.SetServerConnectionId( |
| unused_connection_id_data->connection_id); |
| } else { |
| packet_creator_.SetClientConnectionId( |
| unused_connection_id_data->connection_id); |
| } |
| } |
| } |
| if (default_path_and_alternative_path_use_the_same_peer_connection_id) { |
| *alternative_path_cid = *default_path_cid; |
| alternative_path_.stateless_reset_token = |
| default_path_.stateless_reset_token; |
| } else if (!alternative_path_cid->IsEmpty() && |
| !peer_issued_cid_manager_->IsConnectionIdActive( |
| *alternative_path_cid)) { |
| *alternative_path_cid = EmptyQuicConnectionId(); |
| const QuicConnectionIdData* unused_connection_id_data = |
| peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); |
| if (unused_connection_id_data != nullptr) { |
| *alternative_path_cid = unused_connection_id_data->connection_id; |
| alternative_path_.stateless_reset_token = |
| unused_connection_id_data->stateless_reset_token; |
| } |
| } |
| |
| std::vector<uint64_t> retired_cid_sequence_numbers = |
| peer_issued_cid_manager_->ConsumeToBeRetiredConnectionIdSequenceNumbers(); |
| QUICHE_DCHECK(!retired_cid_sequence_numbers.empty()); |
| for (const auto& sequence_number : retired_cid_sequence_numbers) { |
| ++stats_.num_retire_connection_id_sent; |
| visitor_->SendRetireConnectionId(sequence_number); |
| } |
| } |
| |
| bool QuicConnection::SendNewConnectionId( |
| const QuicNewConnectionIdFrame& frame) { |
| visitor_->SendNewConnectionId(frame); |
| ++stats_.num_new_connection_id_sent; |
| return connected_; |
| } |
| |
| bool QuicConnection::MaybeReserveConnectionId( |
| const QuicConnectionId& connection_id) { |
| if (perspective_ == Perspective::IS_SERVER) { |
| return visitor_->MaybeReserveConnectionId(connection_id); |
| } |
| return true; |
| } |
| |
| void QuicConnection::OnSelfIssuedConnectionIdRetired( |
| const QuicConnectionId& connection_id) { |
| if (perspective_ == Perspective::IS_SERVER) { |
| visitor_->OnServerConnectionIdRetired(connection_id); |
| } |
| } |
| |
| void QuicConnection::MaybeUpdateAckTimeout() { |
| if (should_last_packet_instigate_acks_) { |
| return; |
| } |
| should_last_packet_instigate_acks_ = true; |
| uber_received_packet_manager_.MaybeUpdateAckTimeout( |
| /*should_last_packet_instigate_acks=*/true, |
| last_received_packet_info_.decrypted_level, |
| last_received_packet_info_.header.packet_number, |
| last_received_packet_info_.receipt_time, clock_->ApproximateNow(), |
| sent_packet_manager_.GetRttStats()); |
| } |
| |
| QuicTime QuicConnection::GetPathDegradingDeadline() const { |
| if (!ShouldDetectPathDegrading()) { |
| return QuicTime::Zero(); |
| } |
| return clock_->ApproximateNow() + |
| sent_packet_manager_.GetPathDegradingDelay(); |
| } |
| |
| bool QuicConnection::ShouldDetectPathDegrading() const { |
| if (!connected_) { |
| return false; |
| } |
| // No path degrading detection before handshake completes. |
| if (!idle_network_detector_.handshake_timeout().IsInfinite()) { |
| return false; |
| } |
| return perspective_ == Perspective::IS_CLIENT && !is_path_degrading_; |
| } |
| |
| QuicTime QuicConnection::GetNetworkBlackholeDeadline() const { |
| if (!ShouldDetectBlackhole()) { |
| return QuicTime::Zero(); |
| } |
| QUICHE_DCHECK_LT(0u, num_rtos_for_blackhole_detection_); |
| |
| const QuicTime::Delta blackhole_delay = |
| sent_packet_manager_.GetNetworkBlackholeDelay( |
| num_rtos_for_blackhole_detection_); |
| if (!ShouldDetectPathDegrading()) { |
| return clock_->ApproximateNow() + blackhole_delay; |
| } |
| return clock_->ApproximateNow() + |
| CalculateNetworkBlackholeDelay( |
| blackhole_delay, sent_packet_manager_.GetPathDegradingDelay(), |
| sent_packet_manager_.GetPtoDelay()); |
| } |
| |
| // static |
| QuicTime::Delta QuicConnection::CalculateNetworkBlackholeDelay( |
| QuicTime::Delta blackhole_delay, QuicTime::Delta path_degrading_delay, |
| QuicTime::Delta pto_delay) { |
| const QuicTime::Delta min_delay = path_degrading_delay + pto_delay * 2; |
| if (blackhole_delay < min_delay) { |
| QUIC_CODE_COUNT(quic_extending_short_blackhole_delay); |
| } |
| return std::max(min_delay, blackhole_delay); |
| } |
| |
| bool QuicConnection::ShouldDetectBlackhole() const { |
| if (!connected_ || blackhole_detection_disabled_) { |
| return false; |
| } |
| // No blackhole detection before handshake completes. |
| if (default_enable_5rto_blackhole_detection_) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_enable_5rto_blackhole_detection2, |
| 3, 3); |
| return IsHandshakeComplete(); |
| } |
| |
| if (!idle_network_detector_.handshake_timeout().IsInfinite()) { |
| return false; |
| } |
| return num_rtos_for_blackhole_detection_ > 0; |
| } |
| |
| QuicTime QuicConnection::GetRetransmissionDeadline() const { |
| if (perspective_ == Perspective::IS_CLIENT && |
| SupportsMultiplePacketNumberSpaces() && !IsHandshakeConfirmed() && |
| stats_.pto_count == 0 && |
| !framer_.HasDecrypterOfEncryptionLevel(ENCRYPTION_HANDSHAKE) && |
| !undecryptable_packets_.empty()) { |
| // Retransmits ClientHello quickly when a Handshake or 1-RTT packet is |
| // received prior to having Handshake keys. Adding kAlarmGranulary will |
| // avoid spurious retransmissions in the case of small-scale reordering. |
| return clock_->ApproximateNow() + kAlarmGranularity; |
| } |
| return sent_packet_manager_.GetRetransmissionTime(); |
| } |
| |
| bool QuicConnection::SendPathChallenge( |
| const QuicPathFrameBuffer& data_buffer, |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const QuicSocketAddress& effective_peer_address, QuicPacketWriter* writer) { |
| if (!framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_FORWARD_SECURE)) { |
| return connected_; |
| } |
| if (connection_migration_use_new_cid_) { |
| QuicConnectionId client_cid, server_cid; |
| FindOnPathConnectionIds(self_address, effective_peer_address, &client_cid, |
| &server_cid); |
| if (writer == writer_) { |
| ScopedPacketFlusher flusher(this); |
| { |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, peer_address, client_cid, server_cid, |
| connection_migration_use_new_cid_); |
| // It's using the default writer, add the PATH_CHALLENGE the same way as |
| // other frames. This may cause connection to be closed. |
| packet_creator_.AddPathChallengeFrame(data_buffer); |
| } |
| } else { |
| // Switch to the right CID and source/peer addresses. |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, peer_address, client_cid, server_cid, |
| connection_migration_use_new_cid_); |
| std::unique_ptr<SerializedPacket> probing_packet = |
| packet_creator_.SerializePathChallengeConnectivityProbingPacket( |
| data_buffer); |
| QUICHE_DCHECK_EQ(IsRetransmittable(*probing_packet), |
| NO_RETRANSMITTABLE_DATA); |
| QUICHE_DCHECK_EQ(self_address, alternative_path_.self_address); |
| WritePacketUsingWriter(std::move(probing_packet), writer, self_address, |
| peer_address, /*measure_rtt=*/false); |
| } |
| return connected_; |
| } |
| if (writer == writer_) { |
| ScopedPacketFlusher flusher(this); |
| { |
| // It's on current path, add the PATH_CHALLENGE the same way as other |
| // frames. |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, peer_address, /*update_connection_id=*/false); |
| // This may cause connection to be closed. |
| packet_creator_.AddPathChallengeFrame(data_buffer); |
| } |
| // Return outside of the scope so that the flush result can be reflected. |
| return connected_; |
| } |
| std::unique_ptr<SerializedPacket> probing_packet = |
| packet_creator_.SerializePathChallengeConnectivityProbingPacket( |
| data_buffer); |
| QUICHE_DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA); |
| QUICHE_DCHECK_EQ(self_address, alternative_path_.self_address); |
| WritePacketUsingWriter(std::move(probing_packet), writer, self_address, |
| peer_address, /*measure_rtt=*/false); |
| return true; |
| } |
| |
| QuicTime QuicConnection::GetRetryTimeout( |
| const QuicSocketAddress& peer_address_to_use, |
| QuicPacketWriter* writer_to_use) const { |
| if (writer_to_use == writer_ && peer_address_to_use == peer_address()) { |
| return clock_->ApproximateNow() + sent_packet_manager_.GetPtoDelay(); |
| } |
| return clock_->ApproximateNow() + |
| QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs); |
| } |
| |
| void QuicConnection::ValidatePath( |
| std::unique_ptr<QuicPathValidationContext> context, |
| std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) { |
| if (!connection_migration_use_new_cid_ && |
| perspective_ == Perspective::IS_CLIENT && |
| !IsDefaultPath(context->self_address(), context->peer_address())) { |
| alternative_path_ = PathState( |
| context->self_address(), context->peer_address(), |
| default_path_.client_connection_id, default_path_.server_connection_id, |
| default_path_.stateless_reset_token); |
| } |
| if (path_validator_.HasPendingPathValidation()) { |
| // Cancel and fail any earlier validation. |
| path_validator_.CancelPathValidation(); |
| } |
| if (connection_migration_use_new_cid_ && |
| perspective_ == Perspective::IS_CLIENT && |
| !IsDefaultPath(context->self_address(), context->peer_address())) { |
| if (self_issued_cid_manager_ != nullptr) { |
| self_issued_cid_manager_->MaybeSendNewConnectionIds(); |
| if (!connected_) { |
| return; |
| } |
| } |
| if ((self_issued_cid_manager_ != nullptr && |
| !self_issued_cid_manager_->HasConnectionIdToConsume()) || |
| (peer_issued_cid_manager_ != nullptr && |
| !peer_issued_cid_manager_->HasUnusedConnectionId())) { |
| QUIC_DVLOG(1) << "Client cannot start new path validation as there is no " |
| "requried connection ID is available."; |
| result_delegate->OnPathValidationFailure(std::move(context)); |
| return; |
| } |
| QuicConnectionId client_connection_id, server_connection_id; |
| absl::optional<StatelessResetToken> stateless_reset_token; |
| if (self_issued_cid_manager_ != nullptr) { |
| client_connection_id = |
| *self_issued_cid_manager_->ConsumeOneConnectionId(); |
| } |
| if (peer_issued_cid_manager_ != nullptr) { |
| const auto* connection_id_data = |
| peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); |
| server_connection_id = connection_id_data->connection_id; |
| stateless_reset_token = connection_id_data->stateless_reset_token; |
| } |
| alternative_path_ = PathState(context->self_address(), |
| context->peer_address(), client_connection_id, |
| server_connection_id, stateless_reset_token); |
| } |
| path_validator_.StartPathValidation(std::move(context), |
| std::move(result_delegate)); |
| } |
| |
| bool QuicConnection::SendPathResponse( |
| const QuicPathFrameBuffer& data_buffer, |
| const QuicSocketAddress& peer_address_to_send, |
| const QuicSocketAddress& effective_peer_address) { |
| if (!framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_FORWARD_SECURE)) { |
| return false; |
| } |
| QuicConnectionId client_cid, server_cid; |
| if (connection_migration_use_new_cid_) { |
| FindOnPathConnectionIds(last_received_packet_info_.destination_address, |
| effective_peer_address, &client_cid, &server_cid); |
| } |
| // Send PATH_RESPONSE using the provided peer address. If the creator has been |
| // using a different peer address, it will flush before and after serializing |
| // the current PATH_RESPONSE. |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, peer_address_to_send, client_cid, server_cid, |
| connection_migration_use_new_cid_); |
| QUIC_DVLOG(1) << ENDPOINT << "Send PATH_RESPONSE to " << peer_address_to_send; |
| if (default_path_.self_address == |
| last_received_packet_info_.destination_address) { |
| // The PATH_CHALLENGE is received on the default socket. Respond on the same |
| // socket. |
| return packet_creator_.AddPathResponseFrame(data_buffer); |
| } |
| |
| QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_); |
| // This PATH_CHALLENGE is received on an alternative socket which should be |
| // used to send PATH_RESPONSE. |
| if (!path_validator_.HasPendingPathValidation() || |
| path_validator_.GetContext()->self_address() != |
| last_received_packet_info_.destination_address) { |
| // Ignore this PATH_CHALLENGE if it's received from an uninteresting |
| // socket. |
| return true; |
| } |
| QuicPacketWriter* writer = path_validator_.GetContext()->WriterToUse(); |
| |
| std::unique_ptr<SerializedPacket> probing_packet = |
| packet_creator_.SerializePathResponseConnectivityProbingPacket( |
| {data_buffer}, /*is_padded=*/true); |
| QUICHE_DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA); |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Send PATH_RESPONSE from alternative socket with address " |
| << last_received_packet_info_.destination_address; |
| // Ignore the return value to treat write error on the alternative writer as |
| // part of network error. If the writer becomes blocked, wait for the peer to |
| // send another PATH_CHALLENGE. |
| WritePacketUsingWriter(std::move(probing_packet), writer, |
| last_received_packet_info_.destination_address, |
| peer_address_to_send, |
| /*measure_rtt=*/false); |
| return true; |
| } |
| |
| void QuicConnection::UpdatePeerAddress(QuicSocketAddress peer_address) { |
| direct_peer_address_ = peer_address; |
| packet_creator_.SetDefaultPeerAddress(peer_address); |
| } |
| |
| void QuicConnection::SendPingAtLevel(EncryptionLevel level) { |
| ScopedEncryptionLevelContext context(this, level); |
| SendControlFrame(QuicFrame(QuicPingFrame())); |
| } |
| |
| bool QuicConnection::HasPendingPathValidation() const { |
| return path_validator_.HasPendingPathValidation(); |
| } |
| |
| QuicPathValidationContext* QuicConnection::GetPathValidationContext() const { |
| return path_validator_.GetContext(); |
| } |
| |
| void QuicConnection::CancelPathValidation() { |
| path_validator_.CancelPathValidation(); |
| } |
| |
| bool QuicConnection::UpdateConnectionIdsOnClientMigration( |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address) { |
| QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); |
| if (IsAlternativePath(self_address, peer_address)) { |
| // Client migration is after path validation. |
| default_path_.client_connection_id = alternative_path_.client_connection_id; |
| default_path_.server_connection_id = alternative_path_.server_connection_id; |
| default_path_.stateless_reset_token = |
| alternative_path_.stateless_reset_token; |
| return true; |
| } |
| // Client migration is without path validation. |
| if (self_issued_cid_manager_ != nullptr) { |
| self_issued_cid_manager_->MaybeSendNewConnectionIds(); |
| if (!connected_) { |
| return false; |
| } |
| } |
| if ((self_issued_cid_manager_ != nullptr && |
| !self_issued_cid_manager_->HasConnectionIdToConsume()) || |
| (peer_issued_cid_manager_ != nullptr && |
| !peer_issued_cid_manager_->HasUnusedConnectionId())) { |
| return false; |
| } |
| if (self_issued_cid_manager_ != nullptr) { |
| default_path_.client_connection_id = |
| *self_issued_cid_manager_->ConsumeOneConnectionId(); |
| } |
| if (peer_issued_cid_manager_ != nullptr) { |
| const auto* connection_id_data = |
| peer_issued_cid_manager_->ConsumeOneUnusedConnectionId(); |
| default_path_.server_connection_id = connection_id_data->connection_id; |
| default_path_.stateless_reset_token = |
| connection_id_data->stateless_reset_token; |
| } |
| return true; |
| } |
| |
| void QuicConnection::RetirePeerIssuedConnectionIdsNoLongerOnPath() { |
| if (!connection_migration_use_new_cid_ || |
| peer_issued_cid_manager_ == nullptr) { |
| return; |
| } |
| if (perspective_ == Perspective::IS_CLIENT) { |
| peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds( |
| {default_path_.server_connection_id, |
| alternative_path_.server_connection_id}); |
| } else { |
| peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds( |
| {default_path_.client_connection_id, |
| alternative_path_.client_connection_id}); |
| } |
| } |
| |
| void QuicConnection::RetirePeerIssuedConnectionIdsOnPathValidationFailure() { |
| // The alarm to retire connection IDs no longer on paths is scheduled at the |
| // end of writing and reading packet. On path validation failure, there could |
| // be no packet to write or read. Hence the retirement alarm for the |
| // connection ID associated with the failed path needs to be proactively |
| // scheduled here. |
| if (GetQuicReloadableFlag( |
| quic_retire_cid_on_reverse_path_validation_failure) || |
| perspective_ == Perspective::IS_CLIENT) { |
| QUIC_RELOADABLE_FLAG_COUNT( |
| quic_retire_cid_on_reverse_path_validation_failure); |
| RetirePeerIssuedConnectionIdsNoLongerOnPath(); |
| } |
| } |
| |
| bool QuicConnection::MigratePath(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| QuicPacketWriter* writer, bool owns_writer) { |
| QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); |
| if (!connected_) { |
| if (owns_writer) { |
| delete writer; |
| } |
| return false; |
| } |
| QUICHE_DCHECK(!version().UsesHttp3() || IsHandshakeConfirmed()); |
| |
| if (connection_migration_use_new_cid_) { |
| if (!UpdateConnectionIdsOnClientMigration(self_address, peer_address)) { |
| if (owns_writer) { |
| delete writer; |
| } |
| return false; |
| } |
| if (packet_creator_.GetServerConnectionId().length() != |
| default_path_.server_connection_id.length()) { |
| packet_creator_.FlushCurrentPacket(); |
| } |
| packet_creator_.SetClientConnectionId(default_path_.client_connection_id); |
| packet_creator_.SetServerConnectionId(default_path_.server_connection_id); |
| } |
| |
| const auto self_address_change_type = QuicUtils::DetermineAddressChangeType( |
| default_path_.self_address, self_address); |
| const auto peer_address_change_type = QuicUtils::DetermineAddressChangeType( |
| default_path_.peer_address, peer_address); |
| QUICHE_DCHECK(self_address_change_type != NO_CHANGE || |
| peer_address_change_type != NO_CHANGE); |
| const bool is_port_change = (self_address_change_type == PORT_CHANGE || |
| self_address_change_type == NO_CHANGE) && |
| (peer_address_change_type == PORT_CHANGE || |
| peer_address_change_type == NO_CHANGE); |
| SetSelfAddress(self_address); |
| UpdatePeerAddress(peer_address); |
| SetQuicPacketWriter(writer, owns_writer); |
| MaybeClearQueuedPacketsOnPathChange(); |
| OnSuccessfulMigration(is_port_change); |
| return true; |
| } |
| |
| void QuicConnection::OnPathValidationFailureAtClient(bool is_multi_port) { |
| if (connection_migration_use_new_cid_) { |
| QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT); |
| alternative_path_.Clear(); |
| } |
| |
| if (is_multi_port && multi_port_stats_ != nullptr) { |
| if (is_path_degrading_) { |
| multi_port_stats_->num_multi_port_probe_failures_when_path_degrading++; |
| } else { |
| multi_port_stats_ |
| ->num_multi_port_probe_failures_when_path_not_degrading++; |
| } |
| } |
| |
| RetirePeerIssuedConnectionIdsOnPathValidationFailure(); |
| } |
| |
| QuicConnectionId QuicConnection::GetOneActiveServerConnectionId() const { |
| if (perspective_ == Perspective::IS_CLIENT || |
| self_issued_cid_manager_ == nullptr) { |
| return connection_id(); |
| } |
| auto active_connection_ids = GetActiveServerConnectionIds(); |
| QUIC_BUG_IF(quic_bug_6944, active_connection_ids.empty()); |
| if (active_connection_ids.empty() || |
| std::find(active_connection_ids.begin(), active_connection_ids.end(), |
| connection_id()) != active_connection_ids.end()) { |
| return connection_id(); |
| } |
| QUICHE_CODE_COUNT(connection_id_on_default_path_has_been_retired); |
| auto active_connection_id = |
| self_issued_cid_manager_->GetOneActiveConnectionId(); |
| return active_connection_id; |
| } |
| |
| std::vector<QuicConnectionId> QuicConnection::GetActiveServerConnectionIds() |
| const { |
| QUICHE_DCHECK_EQ(Perspective::IS_SERVER, perspective_); |
| std::vector<QuicConnectionId> result; |
| if (self_issued_cid_manager_ == nullptr) { |
| result.push_back(default_path_.server_connection_id); |
| } else { |
| QUICHE_DCHECK(version().HasIetfQuicFrames()); |
| result = self_issued_cid_manager_->GetUnretiredConnectionIds(); |
| } |
| if (!original_destination_connection_id_.has_value()) { |
| return result; |
| } |
| // Add the original connection ID |
| if (std::find(result.begin(), result.end(), |
| original_destination_connection_id_.value()) != result.end()) { |
| QUIC_BUG(quic_unexpected_original_destination_connection_id) |
| << "original_destination_connection_id: " |
| << original_destination_connection_id_.value() |
| << " is unexpectedly in active list"; |
| } else { |
| result.insert(result.end(), original_destination_connection_id_.value()); |
| } |
| return result; |
| } |
| |
| void QuicConnection::CreateConnectionIdManager() { |
| if (!version().HasIetfQuicFrames()) { |
| return; |
| } |
| |
| if (perspective_ == Perspective::IS_CLIENT) { |
| if (!default_path_.server_connection_id.IsEmpty()) { |
| peer_issued_cid_manager_ = |
| std::make_unique<QuicPeerIssuedConnectionIdManager>( |
| kMinNumOfActiveConnectionIds, default_path_.server_connection_id, |
| clock_, alarm_factory_, this, context()); |
| } |
| } else { |
| if (!default_path_.server_connection_id.IsEmpty()) { |
| self_issued_cid_manager_ = MakeSelfIssuedConnectionIdManager(); |
| } |
| } |
| } |
| |
| void QuicConnection::QuicBugIfHasPendingFrames(QuicStreamId id) const { |
| QUIC_BUG_IF(quic_has_pending_frames_unexpectedly, |
| connected_ && packet_creator_.HasPendingStreamFramesOfStream(id)) |
| << "Stream " << id |
| << " has pending frames unexpectedly. Received packet info: " |
| << last_received_packet_info_; |
| } |
| |
| void QuicConnection::SetUnackedMapInitialCapacity() { |
| sent_packet_manager_.ReserveUnackedPacketsInitialCapacity( |
| GetUnackedMapInitialCapacity()); |
| } |
| |
| void QuicConnection::SetSourceAddressTokenToSend(absl::string_view token) { |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| if (!packet_creator_.HasRetryToken()) { |
| // Ignore received tokens (via NEW_TOKEN frame) from previous connections |
| // when a RETRY token has been received. |
| packet_creator_.SetRetryToken(std::string(token.data(), token.length())); |
| } |
| } |
| |
| void QuicConnection::MaybeUpdateBytesSentToAlternativeAddress( |
| const QuicSocketAddress& peer_address, QuicByteCount sent_packet_size) { |
| if (!version().SupportsAntiAmplificationLimit() || |
| perspective_ != Perspective::IS_SERVER) { |
| return; |
| } |
| QUICHE_DCHECK(!IsDefaultPath(default_path_.self_address, peer_address)); |
| if (!IsAlternativePath(default_path_.self_address, peer_address)) { |
| QUIC_DLOG(INFO) << "Wrote to uninteresting peer address: " << peer_address |
| << " default direct_peer_address_ " << direct_peer_address_ |
| << " alternative path peer address " |
| << alternative_path_.peer_address; |
| return; |
| } |
| if (alternative_path_.validated) { |
| return; |
| } |
| if (alternative_path_.bytes_sent_before_address_validation >= |
| anti_amplification_factor_ * |
| alternative_path_.bytes_received_before_address_validation) { |
| QUIC_LOG_FIRST_N(WARNING, 100) |
| << "Server sent more data than allowed to unverified alternative " |
| "peer address " |
| << peer_address << " bytes sent " |
| << alternative_path_.bytes_sent_before_address_validation |
| << ", bytes received " |
| << alternative_path_.bytes_received_before_address_validation; |
| } |
| alternative_path_.bytes_sent_before_address_validation += sent_packet_size; |
| } |
| |
| void QuicConnection::MaybeUpdateBytesReceivedFromAlternativeAddress( |
| QuicByteCount received_packet_size) { |
| if (!version().SupportsAntiAmplificationLimit() || |
| perspective_ != Perspective::IS_SERVER || |
| !IsAlternativePath(last_received_packet_info_.destination_address, |
| GetEffectivePeerAddressFromCurrentPacket()) || |
| last_received_packet_info_.received_bytes_counted) { |
| return; |
| } |
| // Only update bytes received if this probing frame is received on the most |
| // recent alternative path. |
| QUICHE_DCHECK(!IsDefaultPath(last_received_packet_info_.destination_address, |
| GetEffectivePeerAddressFromCurrentPacket())); |
| if (!alternative_path_.validated) { |
| alternative_path_.bytes_received_before_address_validation += |
| received_packet_size; |
| } |
| last_received_packet_info_.received_bytes_counted = true; |
| } |
| |
| bool QuicConnection::IsDefaultPath( |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address) const { |
| return direct_peer_address_ == peer_address && |
| default_path_.self_address == self_address; |
| } |
| |
| bool QuicConnection::IsAlternativePath( |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address) const { |
| return alternative_path_.peer_address == peer_address && |
| alternative_path_.self_address == self_address; |
| } |
| |
| void QuicConnection::PathState::Clear() { |
| self_address = QuicSocketAddress(); |
| peer_address = QuicSocketAddress(); |
| client_connection_id = {}; |
| server_connection_id = {}; |
| validated = false; |
| bytes_received_before_address_validation = 0; |
| bytes_sent_before_address_validation = 0; |
| send_algorithm = nullptr; |
| rtt_stats = absl::nullopt; |
| stateless_reset_token.reset(); |
| } |
| |
| QuicConnection::PathState::PathState(PathState&& other) { |
| *this = std::move(other); |
| } |
| |
| QuicConnection::PathState& QuicConnection::PathState::operator=( |
| QuicConnection::PathState&& other) { |
| if (this != &other) { |
| self_address = other.self_address; |
| peer_address = other.peer_address; |
| client_connection_id = other.client_connection_id; |
| server_connection_id = other.server_connection_id; |
| stateless_reset_token = other.stateless_reset_token; |
| validated = other.validated; |
| bytes_received_before_address_validation = |
| other.bytes_received_before_address_validation; |
| bytes_sent_before_address_validation = |
| other.bytes_sent_before_address_validation; |
| send_algorithm = std::move(other.send_algorithm); |
| if (other.rtt_stats.has_value()) { |
| rtt_stats.emplace(); |
| rtt_stats->CloneFrom(other.rtt_stats.value()); |
| } else { |
| rtt_stats.reset(); |
| } |
| other.Clear(); |
| } |
| return *this; |
| } |
| |
| bool QuicConnection::IsReceivedPeerAddressValidated() const { |
| QuicSocketAddress current_effective_peer_address = |
| GetEffectivePeerAddressFromCurrentPacket(); |
| QUICHE_DCHECK(current_effective_peer_address.IsInitialized()); |
| return (alternative_path_.peer_address.host() == |
| current_effective_peer_address.host() && |
| alternative_path_.validated) || |
| (default_path_.validated && default_path_.peer_address.host() == |
| current_effective_peer_address.host()); |
| } |
| |
| void QuicConnection::OnMultiPortPathProbingSuccess( |
| std::unique_ptr<QuicPathValidationContext> context, QuicTime start_time) { |
| QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective()); |
| alternative_path_.validated = true; |
| multi_port_path_context_ = std::move(context); |
| multi_port_probing_alarm_->Set(clock_->ApproximateNow() + |
| multi_port_probing_interval_); |
| if (multi_port_stats_ != nullptr) { |
| auto now = clock_->Now(); |
| auto time_delta = now - start_time; |
| multi_port_stats_->rtt_stats.UpdateRtt(time_delta, QuicTime::Delta::Zero(), |
| now); |
| if (is_path_degrading_) { |
| multi_port_stats_->rtt_stats_when_default_path_degrading.UpdateRtt( |
| time_delta, QuicTime::Delta::Zero(), now); |
| } |
| } |
| } |
| |
| void QuicConnection::ProbeMultiPortPath() { |
| if (!connected_ || path_validator_.HasPendingPathValidation() || |
| !multi_port_path_context_ || |
| alternative_path_.self_address != |
| multi_port_path_context_->self_address() || |
| alternative_path_.peer_address != |
| multi_port_path_context_->peer_address()) { |
| return; |
| } |
| auto multi_port_validation_result_delegate = |
| std::make_unique<MultiPortPathValidationResultDelegate>(this); |
| path_validator_.StartPathValidation( |
| std::move(multi_port_path_context_), |
| std::move(multi_port_validation_result_delegate)); |
| } |
| |
| QuicConnection::MultiPortPathValidationResultDelegate:: |
| MultiPortPathValidationResultDelegate(QuicConnection* connection) |
| : connection_(connection) { |
| QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, connection->perspective()); |
| } |
| |
| void QuicConnection::MultiPortPathValidationResultDelegate:: |
| OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context, |
| QuicTime start_time) { |
| connection_->OnMultiPortPathProbingSuccess(std::move(context), start_time); |
| } |
| |
| void QuicConnection::MultiPortPathValidationResultDelegate:: |
| OnPathValidationFailure( |
| std::unique_ptr<QuicPathValidationContext> /*context*/) { |
| connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/true); |
| } |
| |
| QuicConnection::ReversePathValidationResultDelegate:: |
| ReversePathValidationResultDelegate( |
| QuicConnection* connection, |
| const QuicSocketAddress& direct_peer_address) |
| : QuicPathValidator::ResultDelegate(), |
| connection_(connection), |
| original_direct_peer_address_(direct_peer_address), |
| peer_address_default_path_(connection->direct_peer_address_), |
| peer_address_alternative_path_( |
| connection_->alternative_path_.peer_address), |
| active_effective_peer_migration_type_( |
| connection_->active_effective_peer_migration_type_) {} |
| |
| void QuicConnection::ReversePathValidationResultDelegate:: |
| OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context, |
| QuicTime start_time) { |
| QUIC_DLOG(INFO) << "Successfully validated new path " << *context |
| << ", validation started at " << start_time; |
| if (connection_->IsDefaultPath(context->self_address(), |
| context->peer_address())) { |
| QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 3, 6); |
| if (connection_->active_effective_peer_migration_type_ == NO_CHANGE) { |
| std::string error_detail = absl::StrCat( |
| "Reverse path validation on default path from ", |
| context->self_address().ToString(), " to ", |
| context->peer_address().ToString(), |
| " completed without active peer address change: current " |
| "peer address on default path ", |
| connection_->direct_peer_address_.ToString(), |
| ", peer address on default path when the reverse path " |
| "validation was kicked off ", |
| peer_address_default_path_.ToString(), |
| ", peer address on alternative path when the reverse " |
| "path validation was kicked off ", |
| peer_address_alternative_path_.ToString(), |
| ", with active_effective_peer_migration_type_ = ", |
| AddressChangeTypeToString(active_effective_peer_migration_type_), |
| ". The last received packet number ", |
| connection_->last_received_packet_info_.header.packet_number |
| .ToString(), |
| " Connection is connected: ", connection_->connected_); |
| QUIC_BUG(quic_bug_10511_43) << error_detail; |
| } |
| connection_->OnEffectivePeerMigrationValidated(); |
| } else { |
| QUICHE_DCHECK(connection_->IsAlternativePath( |
| context->self_address(), context->effective_peer_address())); |
| QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 4, 6); |
| QUIC_DVLOG(1) << "Mark alternative peer address " |
| << context->effective_peer_address() << " validated."; |
| connection_->alternative_path_.validated = true; |
| } |
| } |
| |
| void QuicConnection::ReversePathValidationResultDelegate:: |
| OnPathValidationFailure( |
| std::unique_ptr<QuicPathValidationContext> context) { |
| if (!connection_->connected()) { |
| return; |
| } |
| QUIC_DLOG(INFO) << "Fail to validate new path " << *context; |
| if (connection_->IsDefaultPath(context->self_address(), |
| context->peer_address())) { |
| // Only act upon validation failure on the default path. |
| QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 5, 6); |
| connection_->RestoreToLastValidatedPath(original_direct_peer_address_); |
| } else if (connection_->IsAlternativePath( |
| context->self_address(), context->effective_peer_address())) { |
| QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 6, 6); |
| connection_->alternative_path_.Clear(); |
| } |
| connection_->RetirePeerIssuedConnectionIdsOnPathValidationFailure(); |
| } |
| |
| QuicConnection::ScopedRetransmissionTimeoutIndicator:: |
| ScopedRetransmissionTimeoutIndicator(QuicConnection* connection) |
| : connection_(connection) { |
| QUICHE_DCHECK(!connection_->in_on_retransmission_time_out_) |
| << "ScopedRetransmissionTimeoutIndicator is not supposed to be nested"; |
| connection_->in_on_retransmission_time_out_ = true; |
| } |
| |
| QuicConnection::ScopedRetransmissionTimeoutIndicator:: |
| ~ScopedRetransmissionTimeoutIndicator() { |
| QUICHE_DCHECK(connection_->in_on_retransmission_time_out_); |
| connection_->in_on_retransmission_time_out_ = false; |
| } |
| |
| void QuicConnection::RestoreToLastValidatedPath( |
| QuicSocketAddress original_direct_peer_address) { |
| QUIC_DLOG(INFO) << "Switch back to use the old peer address " |
| << alternative_path_.peer_address; |
| if (!alternative_path_.validated) { |
| // If not validated by now, close connection silently so that the following |
| // packets received will be rejected. |
| CloseConnection(QUIC_INTERNAL_ERROR, |
| "No validated peer address to use after reverse path " |
| "validation failure.", |
| ConnectionCloseBehavior::SILENT_CLOSE); |
| return; |
| } |
| MaybeClearQueuedPacketsOnPathChange(); |
| |
| // Revert congestion control context to old state. |
| OnPeerIpAddressChanged(); |
| |
| if (alternative_path_.send_algorithm != nullptr) { |
| sent_packet_manager_.SetSendAlgorithm( |
| alternative_path_.send_algorithm.release()); |
| sent_packet_manager_.SetRttStats(alternative_path_.rtt_stats.value()); |
| } else { |
| QUIC_BUG(quic_bug_10511_42) |
| << "Fail to store congestion controller before migration."; |
| } |
| |
| UpdatePeerAddress(original_direct_peer_address); |
| SetDefaultPathState(std::move(alternative_path_)); |
| |
| active_effective_peer_migration_type_ = NO_CHANGE; |
| ++stats_.num_invalid_peer_migration; |
| // The reverse path validation failed because of alarm firing, flush all the |
| // pending writes previously throttled by anti-amplification limit. |
| WriteIfNotBlocked(); |
| } |
| |
| std::unique_ptr<SendAlgorithmInterface> |
| QuicConnection::OnPeerIpAddressChanged() { |
| QUICHE_DCHECK(validate_client_addresses_); |
| std::unique_ptr<SendAlgorithmInterface> old_send_algorithm = |
| sent_packet_manager_.OnConnectionMigration( |
| /*reset_send_algorithm=*/true); |
| // OnConnectionMigration() should have marked in-flight packets to be |
| // retransmitted if there is any. |
| QUICHE_DCHECK(!sent_packet_manager_.HasInFlightPackets()); |
| // OnConnectionMigration() may have changed the retransmission timer, so |
| // re-arm it. |
| SetRetransmissionAlarm(); |
| // Stop detections in quiecense. |
| blackhole_detector_.StopDetection(/*permanent=*/false); |
| return old_send_algorithm; |
| } |
| |
| void QuicConnection::set_keep_alive_ping_timeout( |
| QuicTime::Delta keep_alive_ping_timeout) { |
| if (use_ping_manager_) { |
| ping_manager_.set_keep_alive_timeout(keep_alive_ping_timeout); |
| return; |
| } |
| QUICHE_DCHECK(!ping_alarm_->IsSet()); |
| keep_alive_ping_timeout_ = keep_alive_ping_timeout; |
| } |
| |
| void QuicConnection::set_initial_retransmittable_on_wire_timeout( |
| QuicTime::Delta retransmittable_on_wire_timeout) { |
| if (use_ping_manager_) { |
| ping_manager_.set_initial_retransmittable_on_wire_timeout( |
| retransmittable_on_wire_timeout); |
| return; |
| } |
| QUICHE_DCHECK(!ping_alarm_->IsSet()); |
| initial_retransmittable_on_wire_timeout_ = retransmittable_on_wire_timeout; |
| } |
| |
| #undef ENDPOINT // undef for jumbo builds |
| } // namespace quic |