| // 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_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 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_->MaybeProbeMultiPortPath(); |
| } |
| }; |
| |
| // 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), |
| 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_)), |
| 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; |
| } |
| } |
| if (perspective_ == Perspective::IS_CLIENT) { |
| AddKnownServerAddress(initial_peer_address); |
| } |
| 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 (connection_migration_use_new_cid_ && |
| config.HasReceivedPreferredAddressConnectionIdAndToken() && |
| config.HasClientSentConnectionOption(kSPAD, perspective_)) { |
| if (self_address().host().IsIPv4() && |
| config.HasReceivedIPv4AlternateServerAddress()) { |
| server_preferred_address_ = config.ReceivedIPv4AlternateServerAddress(); |
| } else if (self_address().host().IsIPv6() && |
| config.HasReceivedIPv6AlternateServerAddress()) { |
| server_preferred_address_ = config.ReceivedIPv6AlternateServerAddress(); |
| } |
| if (server_preferred_address_.IsInitialized()) { |
| QUICHE_DLOG(INFO) << ENDPOINT << "Received server preferred address: " |
| << server_preferred_address_; |
| if (config.HasClientRequestedIndependentOption(kSPA2, perspective_)) { |
| accelerated_server_preferred_address_ = true; |
| visitor_->OnServerPreferredAddressAvailable(server_preferred_address_); |
| } |
| } |
| } |
| if (config.HasReceivedMaxPacketSize()) { |
| peer_max_packet_size_ = config.ReceivedMaxPacketSize(); |
| packet_creator_.SetMaxPacketLength( |
| GetLimitedMaxPacketSize(packet_creator_.max_packet_length())); |
| } |
| if (config.HasReceivedMaxDatagramFrameSize()) { |
| packet_creator_.SetMaxDatagramFrameSize( |
| config.ReceivedMaxDatagramFrameSize()); |
| } |
| |
| supports_release_time_ = |
| writer_ != nullptr && writer_->SupportsReleaseTime() && |
| !config.HasClientSentConnectionOption(kNPCO, perspective_); |
| |
| if (supports_release_time_) { |
| UpdateReleaseTimeIntoFuture(); |
| } |
| |
| if (perspective_ == Perspective::IS_CLIENT && |
| connection_migration_use_new_cid_ && |
| config.HasClientRequestedIndependentOption(kMPQC, perspective_)) { |
| multi_port_stats_ = std::make_unique<MultiPortStats>(); |
| } |
| } |
| |
| bool QuicConnection::MaybeTestLiveness() { |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| if (liveness_testing_disabled_ || |
| 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; |
| } |
| QUIC_LOG_EVERY_N_SEC(INFO, 60) |
| << "Testing liveness, idle_network_timeout: " |
| << idle_network_detector_.idle_network_timeout() |
| << ", timeout: " << timeout |
| << ", Pto delay: " << sent_packet_manager_.GetPtoDelay() |
| << ", smoothed_rtt: " |
| << sent_packet_manager_.GetRttStats()->smoothed_rtt() |
| << ", mean deviation: " |
| << sent_packet_manager_.GetRttStats()->mean_deviation(); |
| 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()) { |
| if (version().HasIetfQuicFrames()) { |
| // Client processes packets from any known server address. Client only |
| // updates peer address on initialization and/or to validated server |
| // preferred address. |
| } else { |
| // 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, |
| last_received_packet_info_.ecn_codepoint); |
| 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; |
| ping_manager_.reset_consecutive_retransmittable_on_wire_count(); |
| 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; |
| } |
| |
| void QuicConnection::OnAckEcnCounts(const QuicEcnCounts& ecn_counts) { |
| QUIC_DVLOG(1) << ENDPOINT << "OnAckEcnCounts: [" << ecn_counts.ToString() |
| << "]"; |
| PacketNumberSpace space = QuicUtils::GetPacketNumberSpace( |
| last_received_packet_info_.decrypted_level); |
| peer_ack_ecn_counts_[space] = ecn_counts; |
| } |
| |
| 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); |
| } |
| // On the server side, send response to the source address of the current |
| // incoming packet according to RFC9000. |
| // On the client side, send response to the default peer address which should |
| // be on an existing path with a pre-assigned a destination CID. |
| const QuicSocketAddress effective_peer_address_to_respond = |
| perspective_ == Perspective::IS_CLIENT |
| ? effective_peer_address() |
| : GetEffectivePeerAddressFromCurrentPacket(); |
| const QuicSocketAddress direct_peer_address_to_respond = |
| perspective_ == Perspective::IS_CLIENT |
| ? direct_peer_address_ |
| : last_received_packet_info_.source_address; |
| QuicConnectionId client_cid, server_cid; |
| FindOnPathConnectionIds(last_received_packet_info_.destination_address, |
| effective_peer_address_to_respond, &client_cid, |
| &server_cid); |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, direct_peer_address_to_respond, 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 " |
| << effective_peer_address_to_respond; |
| QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 2, 6); |
| ValidatePath(std::make_unique<ReversePathValidationContext>( |
| default_path_.self_address, direct_peer_address_to_respond, |
| effective_peer_address_to_respond, this), |
| std::make_unique<ReversePathValidationResultDelegate>( |
| this, peer_address()), |
| PathValidationReason::kReversePathValidation); |
| } |
| has_path_challenge_in_current_packet_ = true; |
| MaybeUpdateAckTimeout(); |
| // Queue or send PATH_RESPONSE. |
| if (!SendPathResponse(frame.data_buffer, direct_peer_address_to_respond, |
| effective_peer_address_to_respond)) { |
| 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 (multi_port_stats_ != nullptr) { |
| 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(); |
| } |
| } |
| |
| 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; |
| } |
| ScopedPacketFlusher flusher(this); |
| return packet_creator_.ConsumeCryptoData(level, write_length, offset); |
| } |
| |
| 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 (perspective_ == Perspective::IS_SERVER && |
| version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) { |
| if (in_probe_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); |
| return QuicConsumedData(0, false); |
| } |
| if (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); |
| } |
| } |
| // 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); |
| return packet_creator_.ConsumeData(id, write_length, offset, state); |
| } |
| |
| 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::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."; |
| QuicSocketAddress normalized_self_address = self_address; |
| QuicSocketAddress normalized_peer_address = peer_address; |
| if (normalize_incoming_packets_addresses_) { |
| normalized_self_address = self_address.Normalized(); |
| normalized_peer_address = peer_address.Normalized(); |
| if (normalized_self_address != self_address) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_normalize_incoming_packets_addresses, 1, |
| 3); |
| } |
| if (normalized_peer_address != peer_address) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_normalize_incoming_packets_addresses, 2, |
| 3); |
| } |
| } |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPacketReceived(normalized_self_address, |
| normalized_peer_address, packet); |
| } |
| last_received_packet_info_ = ReceivedPacketInfo( |
| normalized_self_address, normalized_peer_address, packet.receipt_time(), |
| packet.length(), packet.ecn_codepoint()); |
| 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()) { |
| if (perspective_ == Perspective::IS_CLIENT) { |
| AddKnownServerAddress(last_received_packet_info_.source_address); |
| } |
| 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 << ", to " |
| << last_received_packet_info_.destination_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(/*is_migration_linkable=*/true); |
| } |
| } |
| |
| 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; |
| } |
| // Client should only send packets on either default or alternative path, so |
| // it shouldn't fail here. If the server fail to find CID to use, no packet |
| // will be generated on this path. |
| QUIC_BUG_IF(failed to find on path connection ids, |
| perspective_ == Perspective:: |