| // 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 "quic/core/quic_connection.h" |
| |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #include <algorithm> |
| #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 "quic/core/congestion_control/rtt_stats.h" |
| #include "quic/core/congestion_control/send_algorithm_interface.h" |
| #include "quic/core/crypto/crypto_protocol.h" |
| #include "quic/core/crypto/crypto_utils.h" |
| #include "quic/core/crypto/quic_decrypter.h" |
| #include "quic/core/crypto/quic_encrypter.h" |
| #include "quic/core/proto/cached_network_parameters_proto.h" |
| #include "quic/core/quic_bandwidth.h" |
| #include "quic/core/quic_config.h" |
| #include "quic/core/quic_connection_id.h" |
| #include "quic/core/quic_constants.h" |
| #include "quic/core/quic_error_codes.h" |
| #include "quic/core/quic_legacy_version_encapsulator.h" |
| #include "quic/core/quic_packet_creator.h" |
| #include "quic/core/quic_packet_writer.h" |
| #include "quic/core/quic_path_validator.h" |
| #include "quic/core/quic_types.h" |
| #include "quic/core/quic_utils.h" |
| #include "quic/platform/api/quic_bug_tracker.h" |
| #include "quic/platform/api/quic_client_stats.h" |
| #include "quic/platform/api/quic_error_code_wrappers.h" |
| #include "quic/platform/api/quic_exported_stats.h" |
| #include "quic/platform/api/quic_flag_utils.h" |
| #include "quic/platform/api/quic_flags.h" |
| #include "quic/platform/api/quic_hostname_utils.h" |
| #include "quic/platform/api/quic_logging.h" |
| #include "quic/platform/api/quic_map_util.h" |
| #include "quic/platform/api/quic_server_stats.h" |
| #include "quic/platform/api/quic_socket_address.h" |
| #include "common/platform/api/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; |
| |
| // An alarm that is scheduled to send an ack if a timeout occurs. |
| class AckAlarmDelegate : public QuicAlarm::Delegate { |
| public: |
| explicit AckAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| AckAlarmDelegate(const AckAlarmDelegate&) = delete; |
| AckAlarmDelegate& operator=(const AckAlarmDelegate&) = delete; |
| |
| 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(); |
| } |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| // 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 QuicAlarm::Delegate { |
| public: |
| explicit RetransmissionAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| RetransmissionAlarmDelegate(const RetransmissionAlarmDelegate&) = delete; |
| RetransmissionAlarmDelegate& operator=(const RetransmissionAlarmDelegate&) = |
| delete; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->OnRetransmissionTimeout(); |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| // 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 QuicAlarm::Delegate { |
| public: |
| explicit SendAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| SendAlarmDelegate(const SendAlarmDelegate&) = delete; |
| SendAlarmDelegate& operator=(const SendAlarmDelegate&) = delete; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->WriteAndBundleAcksIfNotBlocked(); |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| class PingAlarmDelegate : public QuicAlarm::Delegate { |
| public: |
| explicit PingAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| PingAlarmDelegate(const PingAlarmDelegate&) = delete; |
| PingAlarmDelegate& operator=(const PingAlarmDelegate&) = delete; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->OnPingTimeout(); |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| class MtuDiscoveryAlarmDelegate : public QuicAlarm::Delegate { |
| public: |
| explicit MtuDiscoveryAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| MtuDiscoveryAlarmDelegate(const MtuDiscoveryAlarmDelegate&) = delete; |
| MtuDiscoveryAlarmDelegate& operator=(const MtuDiscoveryAlarmDelegate&) = |
| delete; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->DiscoverMtu(); |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| class ProcessUndecryptablePacketsAlarmDelegate : public QuicAlarm::Delegate { |
| public: |
| explicit ProcessUndecryptablePacketsAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| ProcessUndecryptablePacketsAlarmDelegate( |
| const ProcessUndecryptablePacketsAlarmDelegate&) = delete; |
| ProcessUndecryptablePacketsAlarmDelegate& operator=( |
| const ProcessUndecryptablePacketsAlarmDelegate&) = delete; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| QuicConnection::ScopedPacketFlusher flusher(connection_); |
| connection_->MaybeProcessUndecryptablePackets(); |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| class DiscardPreviousOneRttKeysAlarmDelegate : public QuicAlarm::Delegate { |
| public: |
| explicit DiscardPreviousOneRttKeysAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| DiscardPreviousOneRttKeysAlarmDelegate( |
| const DiscardPreviousOneRttKeysAlarmDelegate&) = delete; |
| DiscardPreviousOneRttKeysAlarmDelegate& operator=( |
| const DiscardPreviousOneRttKeysAlarmDelegate&) = delete; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| connection_->DiscardPreviousOneRttKeys(); |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| class DiscardZeroRttDecryptionKeysAlarmDelegate : public QuicAlarm::Delegate { |
| public: |
| explicit DiscardZeroRttDecryptionKeysAlarmDelegate(QuicConnection* connection) |
| : connection_(connection) {} |
| DiscardZeroRttDecryptionKeysAlarmDelegate( |
| const DiscardZeroRttDecryptionKeysAlarmDelegate&) = delete; |
| DiscardZeroRttDecryptionKeysAlarmDelegate& operator=( |
| const DiscardZeroRttDecryptionKeysAlarmDelegate&) = delete; |
| |
| void OnAlarm() override { |
| QUICHE_DCHECK(connection_->connected()); |
| QUIC_DLOG(INFO) << "0-RTT discard alarm fired"; |
| connection_->RemoveDecrypter(ENCRYPTION_ZERO_RTT); |
| } |
| |
| private: |
| QuicConnection* connection_; |
| }; |
| |
| // 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 PacketCanReplaceConnectionId(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); |
| } |
| |
| CongestionControlType GetDefaultCongestionControlType() { |
| if (GetQuicReloadableFlag(quic_default_to_bbr_v2)) { |
| return kBBRv2; |
| } |
| |
| if (GetQuicReloadableFlag(quic_default_to_bbr)) { |
| return kBBR; |
| } |
| |
| return kCubicBytes; |
| } |
| |
| } // 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) |
| : 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()), |
| server_connection_id_(server_connection_id), |
| client_connection_id_(EmptyQuicConnectionId()), |
| client_connection_id_is_set_(false), |
| direct_peer_address_(initial_peer_address), |
| default_path_(initial_self_address, QuicSocketAddress()), |
| active_effective_peer_migration_type_(NO_CHANGE), |
| support_key_update_for_connection_(false), |
| last_packet_decrypted_(false), |
| last_size_(0), |
| current_packet_data_(nullptr), |
| last_decrypted_packet_level_(ENCRYPTION_INITIAL), |
| should_last_packet_instigate_acks_(false), |
| max_undecryptable_packets_(0), |
| max_tracked_packets_(GetQuicFlag(FLAGS_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), |
| ping_timeout_(QuicTime::Delta::FromSeconds(kPingTimeoutSecs)), |
| initial_retransmittable_on_wire_timeout_(QuicTime::Delta::Infinite()), |
| consecutive_retransmittable_on_wire_ping_count_(0), |
| retransmittable_on_wire_ping_count_(0), |
| arena_(), |
| ack_alarm_(alarm_factory_->CreateAlarm(arena_.New<AckAlarmDelegate>(this), |
| &arena_)), |
| retransmission_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<RetransmissionAlarmDelegate>(this), |
| &arena_)), |
| send_alarm_( |
| alarm_factory_->CreateAlarm(arena_.New<SendAlarmDelegate>(this), |
| &arena_)), |
| ping_alarm_( |
| alarm_factory_->CreateAlarm(arena_.New<PingAlarmDelegate>(this), |
| &arena_)), |
| mtu_discovery_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<MtuDiscoveryAlarmDelegate>(this), |
| &arena_)), |
| process_undecryptable_packets_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<ProcessUndecryptablePacketsAlarmDelegate>(this), |
| &arena_)), |
| discard_previous_one_rtt_keys_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<DiscardPreviousOneRttKeysAlarmDelegate>(this), |
| &arena_)), |
| discard_zero_rtt_decryption_keys_alarm_(alarm_factory_->CreateAlarm( |
| arena_.New<DiscardZeroRttDecryptionKeysAlarmDelegate>(this), |
| &arena_)), |
| visitor_(nullptr), |
| debug_visitor_(nullptr), |
| packet_creator_(server_connection_id_, &framer_, random_generator_, this), |
| time_of_last_received_packet_(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), |
| fill_up_link_during_probing_(false), |
| probing_retransmission_pending_(false), |
| stateless_reset_token_received_(false), |
| received_stateless_reset_token_(0), |
| 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_), |
| idle_network_detector_(this, |
| clock_->ApproximateNow(), |
| &arena_, |
| alarm_factory_), |
| encrypted_control_frames_( |
| GetQuicReloadableFlag(quic_encrypted_control_frames)), |
| use_encryption_level_context_( |
| encrypted_control_frames_ && |
| GetQuicReloadableFlag(quic_use_encryption_level_context)), |
| path_validator_(alarm_factory_, &arena_, this, random_generator_), |
| alternative_path_(QuicSocketAddress(), QuicSocketAddress()), |
| most_recent_frame_type_(NUM_FRAME_TYPES), |
| validate_client_addresses_( |
| framer_.version().HasIetfQuicFrames() && use_path_validator_ && |
| count_bytes_on_alternative_path_separately_ && |
| update_packet_content_returns_connected_ && |
| GetQuicReloadableFlag(quic_server_reverse_validate_new_path)) { |
| QUIC_BUG_IF(!start_peer_migration_earlier_ && send_path_response_); |
| |
| QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT || |
| default_path_.self_address.IsInitialized()); |
| |
| if (use_encryption_level_context_) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_use_encryption_level_context); |
| } |
| QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " |
| << server_connection_id |
| << " and version: " << ParsedQuicVersionToString(version()); |
| |
| QUIC_BUG_IF(!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(server_connection_id_); |
| |
| // On the server side, version negotiation has been done by the dispatcher, |
| // and the server connection is created with the right version. |
| if (perspective_ == Perspective::IS_SERVER) { |
| SetVersionNegotiated(); |
| } |
| if (default_enable_5rto_blackhole_detection_) { |
| num_rtos_for_blackhole_detection_ = 5; |
| if (GetQuicReloadableFlag(quic_disable_server_blackhole_detection) && |
| perspective_ == Perspective::IS_SERVER) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_disable_server_blackhole_detection); |
| blackhole_detection_disabled_ = true; |
| } |
| } |
| packet_creator_.SetDefaultPeerAddress(initial_peer_address); |
| } |
| |
| void QuicConnection::InstallInitialCrypters(QuicConnectionId connection_id) { |
| CrypterPair crypters; |
| CryptoUtils::CreateInitialObfuscators(perspective_, version(), connection_id, |
| &crypters); |
| SetEncrypter(ENCRYPTION_INITIAL, std::move(crypters.encrypter)); |
| if (version().KnowsWhichDecrypterToUse()) { |
| InstallDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter)); |
| } else { |
| SetDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter)); |
| } |
| } |
| |
| QuicConnection::~QuicConnection() { |
| 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 = server_connection_id_; |
| } else { |
| expected_initial_source_connection_id = 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_ = |
| config.KeyUpdateSupportedForConnection(); |
| framer_.SetKeyUpdateSupportForConnection( |
| support_key_update_for_connection_); |
| } else { |
| SetNetworkTimeouts(config.max_time_before_crypto_handshake(), |
| config.max_idle_time_before_crypto_handshake()); |
| } |
| |
| 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.HasClientSentConnectionOption(k2RTO, perspective_)) { |
| QUIC_CODE_COUNT(quic_2rto_blackhole_detection); |
| num_rtos_for_blackhole_detection_ = 2; |
| } |
| if (config.HasClientSentConnectionOption(k3RTO, perspective_)) { |
| QUIC_CODE_COUNT(quic_3rto_blackhole_detection); |
| num_rtos_for_blackhole_detection_ = 3; |
| } |
| if (config.HasClientSentConnectionOption(k4RTO, perspective_)) { |
| QUIC_CODE_COUNT(quic_4rto_blackhole_detection); |
| num_rtos_for_blackhole_detection_ = 4; |
| } |
| if (config.HasClientSentConnectionOption(k6RTO, perspective_)) { |
| QUIC_CODE_COUNT(quic_6rto_blackhole_detection); |
| num_rtos_for_blackhole_detection_ = 6; |
| } |
| } |
| |
| if (config.HasClientRequestedIndependentOption(kFIDT, perspective_)) { |
| idle_network_detector_.enable_shorter_idle_timeout_on_sent_packet(); |
| } |
| 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 (sent_packet_manager_.pto_enabled()) { |
| 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()) { |
| stateless_reset_token_received_ = true; |
| received_stateless_reset_token_ = config.ReceivedStatelessResetToken(); |
| } |
| if (config.HasReceivedAckDelayExponent()) { |
| framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent()); |
| } |
| if (GetQuicReloadableFlag(quic_send_timestamps) && |
| config.HasClientSentConnectionOption(kSTMP, perspective_)) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_send_timestamps); |
| framer_.set_process_timestamps(true); |
| uber_received_packet_manager_.set_save_timestamps(true); |
| } |
| if (config.HasClientSentConnectionOption(kEACK, perspective_)) { |
| bundle_retransmittable_with_pto_ack_ = true; |
| } |
| if (GetQuicReloadableFlag(quic_dont_defer_sending) && |
| config.HasClientSentConnectionOption(kDFER, perspective_)) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_dont_defer_sending); |
| defer_send_in_response_to_packets_ = false; |
| } |
| if (config.HasReceivedMaxPacketSize()) { |
| peer_max_packet_size_ = config.ReceivedMaxPacketSize(); |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| } |
| if (config.HasReceivedMaxDatagramFrameSize()) { |
| packet_creator_.SetMaxDatagramFrameSize( |
| config.ReceivedMaxDatagramFrameSize()); |
| } |
| |
| supports_release_time_ = |
| writer_ != nullptr && writer_->SupportsReleaseTime() && |
| !config.HasClientSentConnectionOption(kNPCO, perspective_); |
| |
| if (supports_release_time_) { |
| UpdateReleaseTimeIntoFuture(); |
| } |
| } |
| |
| void QuicConnection::EnableLegacyVersionEncapsulation( |
| const std::string& server_name) { |
| if (perspective_ != Perspective::IS_CLIENT) { |
| QUIC_BUG << "Cannot enable Legacy Version Encapsulation on the server"; |
| return; |
| } |
| if (legacy_version_encapsulation_enabled_) { |
| QUIC_BUG << "Do not call EnableLegacyVersionEncapsulation twice"; |
| return; |
| } |
| if (!QuicHostnameUtils::IsValidSNI(server_name)) { |
| // Legacy Version Encapsulation is only used when SNI is transmitted. |
| QUIC_DLOG(INFO) |
| << "Refusing to use Legacy Version Encapsulation with invalid SNI \"" |
| << server_name << "\""; |
| return; |
| } |
| QUIC_DLOG(INFO) << "Enabling Legacy Version Encapsulation with SNI \"" |
| << server_name << "\""; |
| legacy_version_encapsulation_enabled_ = true; |
| legacy_version_encapsulation_sni_ = server_name; |
| } |
| |
| bool QuicConnection::MaybeTestLiveness() { |
| QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT); |
| if (encryption_level_ != ENCRYPTION_FORWARD_SECURE) { |
| return false; |
| } |
| const QuicTime idle_network_deadline = |
| idle_network_detector_.GetIdleNetworkDeadline(); |
| if (!idle_network_deadline.IsInitialized()) { |
| return false; |
| } |
| const QuicTime now = clock_->ApproximateNow(); |
| if (now > idle_network_deadline) { |
| QUIC_DLOG(WARNING) << "Idle network deadline has passed"; |
| return false; |
| } |
| const QuicTime::Delta timeout = idle_network_deadline - now; |
| if (2 * timeout > idle_network_detector_.idle_network_timeout()) { |
| // Do not test liveness if timeout is > half timeout. This is used to |
| // prevent an infinite loop for short idle timeout. |
| return false; |
| } |
| if (!sent_packet_manager_.IsLessThanThreePTOs(timeout)) { |
| return false; |
| } |
| SendConnectivityProbingPacket(writer_, peer_address()); |
| return true; |
| } |
| |
| void QuicConnection::ApplyConnectionOptions( |
| const QuicTagVector& connection_options) { |
| sent_packet_manager_.ApplyConnectionOptions(connection_options); |
| } |
| |
| void QuicConnection::OnSendConnectionState( |
| const CachedNetworkParameters& cached_network_params) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnSendConnectionState(cached_network_params); |
| } |
| } |
| |
| void QuicConnection::OnReceiveConnectionState( |
| const CachedNetworkParameters& cached_network_params) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnReceiveConnectionState(cached_network_params); |
| } |
| } |
| |
| void QuicConnection::ResumeConnectionState( |
| const CachedNetworkParameters& cached_network_params, |
| bool max_bandwidth_resumption) { |
| sent_packet_manager_.ResumeConnectionState(cached_network_params, |
| max_bandwidth_resumption); |
| } |
| |
| void QuicConnection::SetMaxPacingRate(QuicBandwidth max_pacing_rate) { |
| sent_packet_manager_.SetMaxPacingRate(max_pacing_rate); |
| } |
| |
| void QuicConnection::AdjustNetworkParameters( |
| const SendAlgorithmInterface::NetworkParams& params) { |
| sent_packet_manager_.AdjustNetworkParameters(params); |
| } |
| |
| void QuicConnection::SetLossDetectionTuner( |
| std::unique_ptr<LossDetectionTunerInterface> tuner) { |
| sent_packet_manager_.SetLossDetectionTuner(std::move(tuner)); |
| } |
| |
| void QuicConnection::OnConfigNegotiated() { |
| sent_packet_manager_.OnConfigNegotiated(); |
| |
| if (GetQuicReloadableFlag(quic_enable_mtu_discovery_at_server) && |
| perspective_ == Perspective::IS_SERVER) { |
| QUIC_RELOADABLE_FLAG_COUNT(quic_enable_mtu_discovery_at_server); |
| SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh); |
| } |
| } |
| |
| QuicBandwidth QuicConnection::MaxPacingRate() const { |
| return sent_packet_manager_.MaxPacingRate(); |
| } |
| |
| bool QuicConnection::SelectMutualVersion( |
| const ParsedQuicVersionVector& available_versions) { |
| // Try to find the highest mutual version by iterating over supported |
| // versions, starting with the highest, and breaking out of the loop once we |
| // find a matching version in the provided available_versions vector. |
| const ParsedQuicVersionVector& supported_versions = |
| framer_.supported_versions(); |
| for (size_t i = 0; i < supported_versions.size(); ++i) { |
| const ParsedQuicVersion& version = supported_versions[i]; |
| if (QuicContainsValue(available_versions, version)) { |
| 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_packet_decrypted_ == false) { |
| return; |
| } |
| CloseConnection(framer->error(), framer->detailed_error(), |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| } |
| |
| void QuicConnection::OnPacket() { |
| last_packet_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(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 << 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(server_connection_id_, packet.connection_id); |
| if (perspective_ == Perspective::IS_SERVER) { |
| const std::string error_details = |
| "Server received version negotiation packet."; |
| QUIC_BUG << 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 (QuicContainsValue(packet.versions, version())) { |
| 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), "}"), |
| 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(), server_connection_id_, retry_without_tag, |
| retry_integrity_tag)) { |
| QUIC_DLOG(ERROR) << "Ignoring RETRY with invalid integrity tag"; |
| return; |
| } |
| } else { |
| if (original_connection_id != server_connection_id_) { |
| QUIC_DLOG(ERROR) << "Ignoring RETRY with original connection ID " |
| << original_connection_id << " not matching expected " |
| << 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 " |
| << server_connection_id_ << " with " << new_connection_id |
| << ", received token " << absl::BytesToHexString(retry_token); |
| if (!original_destination_connection_id_.has_value()) { |
| original_destination_connection_id_ = server_connection_id_; |
| } |
| QUICHE_DCHECK(!retry_source_connection_id_.has_value()) |
| << retry_source_connection_id_.value(); |
| retry_source_connection_id_ = new_connection_id; |
| server_connection_id_ = new_connection_id; |
| packet_creator_.SetServerConnectionId(server_connection_id_); |
| packet_creator_.SetRetryToken(retry_token); |
| |
| // Reinstall initial crypters because the connection ID changed. |
| InstallInitialCrypters(server_connection_id_); |
| |
| sent_packet_manager_.MarkInitialPacketsForRetransmission(); |
| } |
| |
| bool QuicConnection::HasIncomingConnectionId(QuicConnectionId connection_id) { |
| for (QuicConnectionId const& incoming_connection_id : |
| incoming_connection_ids_) { |
| if (incoming_connection_id == connection_id) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| 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 " |
| << server_connection_id_; |
| QUICHE_DCHECK_NE(original_destination_connection_id, server_connection_id_); |
| if (!HasIncomingConnectionId(original_destination_connection_id)) { |
| incoming_connection_ids_.push_back(original_destination_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; |
| } |
| |
| QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() { |
| if (original_destination_connection_id_.has_value()) { |
| return original_destination_connection_id_.value(); |
| } |
| return server_connection_id_; |
| } |
| |
| bool QuicConnection::OnUnauthenticatedPublicHeader( |
| const QuicPacketHeader& header) { |
| // 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); |
| } |
| |
| QuicConnectionId server_connection_id = |
| GetServerConnectionIdAsRecipient(header, perspective_); |
| |
| if (server_connection_id != server_connection_id_ && |
| !HasIncomingConnectionId(server_connection_id)) { |
| if (PacketCanReplaceConnectionId(header, perspective_)) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID " |
| << server_connection_id << " instead of " |
| << server_connection_id_; |
| return true; |
| } |
| |
| ++stats_.packets_dropped; |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Ignoring packet from unexpected server connection ID " |
| << server_connection_id << " instead of " |
| << server_connection_id_; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnIncorrectConnectionId(server_connection_id); |
| } |
| // If this is a server, the dispatcher routes each packet to the |
| // QuicConnection responsible for the packet's connection ID. So if control |
| // arrives here and this is a server, the dispatcher must be malfunctioning. |
| QUICHE_DCHECK_NE(Perspective::IS_SERVER, perspective_); |
| return false; |
| } |
| |
| if (!version().SupportsClientConnectionIds()) { |
| return true; |
| } |
| |
| QuicConnectionId client_connection_id = |
| GetClientConnectionIdAsRecipient(header, perspective_); |
| |
| if (client_connection_id == 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; |
| } |
| |
| ++stats_.packets_dropped; |
| QUIC_DLOG(INFO) << ENDPOINT |
| << "Ignoring packet from unexpected client connection ID " |
| << client_connection_id << " instead of " |
| << client_connection_id_; |
| return false; |
| } |
| |
| bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnUnauthenticatedHeader(header); |
| } |
| |
| // Check that any public reset packet with a different connection ID that was |
| // routed to this QuicConnection has been redirected before control reaches |
| // here. |
| QUICHE_DCHECK(GetServerConnectionIdAsRecipient(header, perspective_) == |
| server_connection_id_ || |
| HasIncomingConnectionId( |
| GetServerConnectionIdAsRecipient(header, perspective_)) || |
| PacketCanReplaceConnectionId(header, perspective_)); |
| |
| 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 << 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::OnDecryptedPacket(size_t /*length*/, |
| EncryptionLevel level) { |
| last_decrypted_packet_level_ = level; |
| last_packet_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() && |
| (last_decrypted_packet_level_ == ENCRYPTION_HANDSHAKE || |
| last_decrypted_packet_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(time_of_last_received_packet_); |
| |
| 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_packet_source_address_; |
| } |
| |
| bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPacketHeader(header, clock_->ApproximateNow(), |
| last_decrypted_packet_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. |
| most_recent_frame_type_ = NUM_FRAME_TYPES; |
| current_packet_content_ = NO_FRAMES_RECEIVED; |
| is_current_packet_connectivity_probing_ = false; |
| has_path_challenge_in_current_packet_ = false; |
| current_effective_peer_migration_type_ = NO_CHANGE; |
| |
| if (perspective_ == Perspective::IS_CLIENT) { |
| if (!GetLargestReceivedPacket().IsInitialized() || |
| header.packet_number > GetLargestReceivedPacket()) { |
| // Update direct_peer_address_ and default path peer_address immediately |
| // for client connections. |
| // TODO(fayang): only change peer addresses in application data packet |
| // number space. |
| UpdatePeerAddress(last_packet_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()); |
| |
| 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_header_ = header; |
| if (!stats_.first_decrypted_packet.IsInitialized()) { |
| stats_.first_decrypted_packet = last_header_.packet_number; |
| } |
| |
| // Record packet receipt to populate ack info before processing stream |
| // frames, since the processing may result in sending a bundled ack. |
| uber_received_packet_manager_.RecordPacketReceived( |
| last_decrypted_packet_level_, last_header_, |
| idle_network_detector_.time_of_last_received_packet()); |
| if (GetQuicReloadableFlag(quic_enable_token_based_address_validation)) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_token_based_address_validation, 2, |
| 2); |
| 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(!connected_) |
| << "Processing STREAM frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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_decrypted_packet_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 << ENDPOINT |
| << "Received an unencrypted data frame: closing connection" |
| << " packet_number:" << last_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; |
| } |
| MaybeUpdateAckTimeout(); |
| visitor_->OnStreamFrame(frame); |
| stats_.stream_bytes_received += frame.data_length; |
| consecutive_retransmittable_on_wire_ping_count_ = 0; |
| return connected_; |
| } |
| |
| bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) { |
| QUIC_BUG_IF(!connected_) |
| << "Processing CRYPTO frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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(!connected_) |
| << "Processing ACK frame start when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| 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_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_decrypted_packet_level_:" |
| << last_decrypted_packet_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(!connected_) |
| << "Processing ACK frame range when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")"; |
| |
| if (GetLargestReceivedPacketWithAck().IsInitialized() && |
| last_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(!connected_) << "Processing ACK frame time stamp when connection " |
| "is closed. Last frame: " |
| << most_recent_frame_type_; |
| QUIC_DVLOG(1) << ENDPOINT << "OnAckTimestamp: [" << packet_number << ", " |
| << timestamp.ToDebuggingValue() << ")"; |
| |
| if (GetLargestReceivedPacketWithAck().IsInitialized() && |
| last_header_.packet_number <= GetLargestReceivedPacketWithAck()) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring"; |
| return true; |
| } |
| |
| sent_packet_manager_.OnAckTimestamp(packet_number, timestamp); |
| return true; |
| } |
| |
| bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) { |
| QUIC_BUG_IF(!connected_) |
| << "Processing ACK frame end when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start; |
| |
| if (GetLargestReceivedPacketWithAck().IsInitialized() && |
| last_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_header_.packet_number, last_decrypted_packet_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_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(!connected_) |
| << "Processing STOP_WAITING frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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_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_header_.packet_number; |
| uber_received_packet_manager_.DontWaitForPacketsBefore( |
| last_decrypted_packet_level_, frame.least_unacked); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) { |
| QUIC_BUG_IF(!connected_) |
| << "Processing PADDING frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| 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(!connected_) |
| << "Processing PING frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| 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_header_.packet_number) { |
| QUIC_DLOG(ERROR) << ENDPOINT |
| << "Peer sent least_unacked:" << stop_waiting.least_unacked |
| << " greater than the enclosing packet number:" |
| << last_header_.packet_number; |
| return "Least unacked too large."; |
| } |
| |
| return nullptr; |
| } |
| |
| bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { |
| QUIC_BUG_IF(!connected_) |
| << "Processing RST_STREAM frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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(!connected_) |
| << "Processing STOP_SENDING frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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; |
| |
| 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(!connected_) << "Processing PATH_CHALLENGE frame when connection " |
| "is closed. Last frame: " |
| << most_recent_frame_type_; |
| if (has_path_challenge_in_current_packet_) { |
| QUICHE_DCHECK(send_path_response_); |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 2, 5); |
| // Only respond to the 1st PATH_CHALLENGE in the packet. |
| return true; |
| } |
| if (!validate_client_addresses_) { |
| return OnPathChallengeFrameInternal(frame); |
| } |
| { |
| // UpdatePacketStateAndReplyPathChallenge() may start reverse path |
| // validation, if so bundle the PATH_CHALLENGE together with the |
| // PATH_RESPONSE. This context needs to be out of scope before returning. |
| // TODO(danzh) inline OnPathChallengeFrameInternal() once |
| // support_reverse_path_validation_ is deprecated. |
| QuicPacketCreator::ScopedPeerAddressContext context( |
| &packet_creator_, last_packet_source_address_); |
| if (!OnPathChallengeFrameInternal(frame)) { |
| return false; |
| } |
| } |
| return connected_; |
| } |
| |
| bool QuicConnection::OnPathChallengeFrameInternal( |
| const QuicPathChallengeFrame& frame) { |
| // UpdatePacketContent() may start reverse path validation. |
| if (!UpdatePacketContent(PATH_CHALLENGE_FRAME)) { |
| return false; |
| } |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPathChallengeFrame(frame); |
| } |
| |
| if (!send_path_response_) { |
| // Save the path challenge's payload, for later use in generating the |
| // response. |
| received_path_challenge_payloads_.push_back(frame.data_buffer); |
| |
| MaybeUpdateAckTimeout(); |
| return true; |
| } |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 3, 5); |
| has_path_challenge_in_current_packet_ = true; |
| MaybeUpdateAckTimeout(); |
| // Queue or send PATH_RESPONSE. Send PATH_RESPONSE to the source address of |
| // the current incoming packet, even if it's not the default path or the |
| // alternative path. |
| if (!SendPathResponse(frame.data_buffer, last_packet_source_address_)) { |
| // Queue the payloads to re-try later. |
| pending_path_challenge_payloads_.push_back( |
| {frame.data_buffer, last_packet_source_address_}); |
| } |
| // 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(!connected_) << "Processing PATH_RESPONSE frame when connection " |
| "is closed. Last frame: " |
| << most_recent_frame_type_; |
| if (!UpdatePacketContent(PATH_RESPONSE_FRAME)) { |
| return false; |
| } |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPathResponseFrame(frame); |
| } |
| MaybeUpdateAckTimeout(); |
| if (use_path_validator_) { |
| path_validator_.OnPathResponse(frame.data_buffer, |
| last_packet_destination_address_); |
| } else { |
| if (!transmitted_connectivity_probe_payload_ || |
| *transmitted_connectivity_probe_payload_ != frame.data_buffer) { |
| // Is not for the probe we sent, ignore it. |
| return true; |
| } |
| // Have received the matching PATH RESPONSE, saved payload no longer valid. |
| transmitted_connectivity_probe_payload_ = nullptr; |
| } |
| return connected_; |
| } |
| |
| bool QuicConnection::OnConnectionCloseFrame( |
| const QuicConnectionCloseFrame& frame) { |
| QUIC_BUG_IF(!connected_) << "Processing CONNECTION_CLOSE frame when " |
| "connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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: " << 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_header_ |
| << " encryption_level: " << encryption_level_; |
| } |
| TearDownLocalConnectionState(frame, ConnectionCloseSource::FROM_PEER); |
| return connected_; |
| } |
| |
| bool QuicConnection::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) { |
| QUIC_BUG_IF(!connected_) |
| << "Processing MAX_STREAMS frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| if (!UpdatePacketContent(MAX_STREAMS_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnMaxStreamsFrame(frame); |
| } |
| return visitor_->OnMaxStreamsFrame(frame) && connected_; |
| } |
| |
| bool QuicConnection::OnStreamsBlockedFrame( |
| const QuicStreamsBlockedFrame& frame) { |
| QUIC_BUG_IF(!connected_) << "Processing STREAMS_BLOCKED frame when " |
| "connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| if (!UpdatePacketContent(STREAMS_BLOCKED_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnStreamsBlockedFrame(frame); |
| } |
| return visitor_->OnStreamsBlockedFrame(frame) && connected_; |
| } |
| |
| bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { |
| QUIC_BUG_IF(!connected_) |
| << "Processing GOAWAY frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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(!connected_) << "Processing WINDOW_UPDATE frame when connection " |
| "is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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_; |
| } |
| |
| bool QuicConnection::OnNewConnectionIdFrame( |
| const QuicNewConnectionIdFrame& frame) { |
| QUIC_BUG_IF(!connected_) << "Processing NEW_CONNECTION_ID frame when " |
| "connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| if (!UpdatePacketContent(NEW_CONNECTION_ID_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnNewConnectionIdFrame(frame); |
| } |
| return true; |
| } |
| |
| bool QuicConnection::OnRetireConnectionIdFrame( |
| const QuicRetireConnectionIdFrame& frame) { |
| QUIC_BUG_IF(!connected_) << "Processing RETIRE_CONNECTION_ID frame when " |
| "connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| if (!UpdatePacketContent(RETIRE_CONNECTION_ID_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnRetireConnectionIdFrame(frame); |
| } |
| return true; |
| } |
| |
| bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) { |
| QUIC_BUG_IF(!connected_) |
| << "Processing NEW_TOKEN frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| if (!UpdatePacketContent(NEW_TOKEN_FRAME)) { |
| return false; |
| } |
| |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnNewTokenFrame(frame); |
| } |
| if (GetQuicReloadableFlag(quic_enable_token_based_address_validation)) { |
| 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(!connected_) |
| << "Processing MESSAGE frame when connection is closed. Last frame: " |
| << most_recent_frame_type_; |
| |
| // 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(!connected_) << "Processing HANDSHAKE_DONE frame when connection " |
| "is closed. Last frame: " |
| << most_recent_frame_type_; |
| 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(!connected_) << "Processing ACK_FREQUENCY frame when connection " |
| "is closed. Last frame: " |
| << most_recent_frame_type_; |
| 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_decrypted_packet_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(!connected_) |
| << "Processing BLOCKED frame when connection is closed. Last frame was " |
| << most_recent_frame_type_; |
| |
| // 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_decrypted_packet_level_)) |
| : "") |
| << " packet " << last_header_.packet_number << " for " |
| << GetServerConnectionIdAsRecipient(last_header_, perspective_); |
| |
| QUIC_DLOG_IF(INFO, current_packet_content_ == SECOND_FRAME_IS_PADDING) |
| << ENDPOINT << "Received a padded PING packet. is_probing: " |
| << IsCurrentPacketConnectivityProbing(); |
| |
| 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_decrypted_packet_level_, |
| last_header_.packet_number, clock_->ApproximateNow(), |
| sent_packet_manager_.GetRttStats()); |
| } |
| |
| ClearLastFrames(); |
| CloseIfTooManyOutstandingSentPackets(); |
| } |
| |
| void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() { |
| if (version().HasIetfQuicFrames()) { |
| if (send_path_response_) { |
| return; |
| } |
| if (perspective_ == Perspective::IS_CLIENT) { |
| // This node is a client, notify that a speculative connectivity probing |
| // packet has been received anyway. |
| visitor_->OnPacketReceived(last_packet_destination_address_, |
| last_packet_source_address_, |
| /*is_connectivity_probe=*/false); |
| return; |
| } |
| if (!received_path_challenge_payloads_.empty()) { |
| if (current_effective_peer_migration_type_ != NO_CHANGE) { |
| // TODO(b/150095588): change the stats to |
| // num_valid_path_challenge_received. |
| ++stats_.num_connectivity_probing_received; |
| } |
| // If the packet contains PATH CHALLENGE, send appropriate RESPONSE. |
| // There was at least one PATH CHALLENGE in the received packet, |
| // Generate the required PATH RESPONSE. |
| SendGenericPathProbePacket(nullptr, last_packet_source_address_, |
| /* is_response=*/true); |
| return; |
| } |
| } else { |
| if (IsCurrentPacketConnectivityProbing()) { |
| visitor_->OnPacketReceived(last_packet_destination_address_, |
| last_packet_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_header_, |
| perspective_) |
| << " from ip:port: " |
| << last_packet_source_address_.ToString() << " to ip:port: " |
| << last_packet_destination_address_.ToString(); |
| visitor_->OnPacketReceived(last_packet_destination_address_, |
| last_packet_source_address_, |
| /*is_connectivity_probe=*/false); |
| return; |
| } |
| } |
| // Server starts to migrate connection upon receiving of non-probing packet |
| // from a new peer address. |
| if (!start_peer_migration_earlier_ && |
| last_header_.packet_number == GetLargestReceivedPacket()) { |
| direct_peer_address_ = last_packet_source_address_; |
| if (current_effective_peer_migration_type_ != NO_CHANGE) { |
| // TODO(fayang): When multiple packet number spaces is supported, only |
| // start peer migration for the application data. |
| StartEffectivePeerMigration(current_effective_peer_migration_type_); |
| } |
| } |
| } |
| |
| bool QuicConnection::IsValidStatelessResetToken(QuicUint128 token) const { |
| return stateless_reset_token_received_ && |
| token == received_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 (use_path_validator_) { |
| if (!IsDefaultPath(last_packet_destination_address_, |
| last_packet_source_address_)) { |
| // This packet is received on a probing path. Do not close connection. |
| if (IsAlternativePath(last_packet_destination_address_, |
| GetEffectivePeerAddressFromCurrentPacket())) { |
| QUIC_BUG_IF(alternative_path_.validated) |
| << "STATELESS_RESET received on alternate path after it's " |
| "validated."; |
| path_validator_.CancelPathValidation(); |
| } else { |
| QUIC_BUG << "Received Stateless Reset on unknown socket."; |
| } |
| return; |
| } |
| } else if (!visitor_->ValidateStatelessReset(last_packet_destination_address_, |
| last_packet_source_address_)) { |
| // This packet is received on a probing path. Do not close connection. |
| 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() { |
| bool should_close; |
| if (GetQuicReloadableFlag( |
| quic_close_connection_with_too_many_outstanding_packets)) { |
| QUIC_RELOADABLE_FLAG_COUNT( |
| quic_close_connection_with_too_many_outstanding_packets); |
| should_close = |
| sent_packet_manager_.GetLargestSentPacket().IsInitialized() && |
| sent_packet_manager_.GetLargestSentPacket() > |
| sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_; |
| } else { |
| should_close = |
| sent_packet_manager_.GetLargestObserved().IsInitialized() && |
| sent_packet_manager_.GetLargestObserved() > |
| sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_; |
| } |
| // This occurs if we don't discard old packets we've seen fast enough. It's |
| // possible largest observed is less than leaset unacked. |
| 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_decrypted_packet_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 { |
| WriteAndBundleAcksIfNotBlocked(); |
| } |
| } |
| |
| void QuicConnection::MaybeActivateLegacyVersionEncapsulation() { |
| if (!legacy_version_encapsulation_enabled_) { |
| return; |
| } |
| QUICHE_DCHECK(!legacy_version_encapsulation_in_progress_); |
| QUIC_BUG_IF(!packet_creator_.CanSetMaxPacketLength()) |
| << "Cannot activate Legacy Version Encapsulation mid-packet"; |
| QUIC_BUG_IF(coalesced_packet_.length() != 0u) |
| << "Cannot activate Legacy Version Encapsulation mid-coalesced-packet"; |
| legacy_version_encapsulation_in_progress_ = true; |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| } |
| void QuicConnection::MaybeDisactivateLegacyVersionEncapsulation() { |
| if (!legacy_version_encapsulation_in_progress_) { |
| return; |
| } |
| // Flush any remaining packet before disactivating encapsulation. |
| packet_creator_.FlushCurrentPacket(); |
| QUICHE_DCHECK(legacy_version_encapsulation_enabled_); |
| legacy_version_encapsulation_in_progress_ = false; |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| } |
| |
| size_t QuicConnection::SendCryptoData(EncryptionLevel level, |
| size_t write_length, |
| QuicStreamOffset offset) { |
| if (write_length == 0) { |
| QUIC_BUG << "Attempt to send empty crypto frame"; |
| return 0; |
| } |
| if (level == ENCRYPTION_INITIAL) { |
| MaybeActivateLegacyVersionEncapsulation(); |
| } |
| size_t consumed_length; |
| { |
| ScopedPacketFlusher flusher(this); |
| consumed_length = |
| packet_creator_.ConsumeCryptoData(level, write_length, offset); |
| } // Added scope ensures packets are flushed before continuing. |
| MaybeDisactivateLegacyVersionEncapsulation(); |
| return consumed_length; |
| } |
| |
| QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, |
| size_t write_length, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| if (state == NO_FIN && write_length == 0) { |
| QUIC_BUG << "Attempt to send empty stream frame"; |
| return QuicConsumedData(0, false); |
| } |
| |
| if (packet_creator_.encryption_level() == ENCRYPTION_INITIAL && |
| QuicUtils::IsCryptoStreamId(transport_version(), id)) { |
| MaybeActivateLegacyVersionEncapsulation(); |
| } |
| QuicConsumedData consumed_data(0, false); |
| { |
| // Opportunistically bundle an ack with every outgoing packet. |
| // Particularly, we want to bundle with handshake packets since we don't |
| // know which decrypter will be used on an ack packet following a handshake |
| // packet (a handshake packet from client to server could result in a REJ or |
| // a SHLO from the server, leading to two different decrypters at the |
| // server.) |
| ScopedPacketFlusher flusher(this); |
| consumed_data = |
| packet_creator_.ConsumeData(id, write_length, offset, state); |
| } // Added scope ensures packets are flushed before continuing. |
| MaybeDisactivateLegacyVersionEncapsulation(); |
| return consumed_data; |
| } |
| |
| bool QuicConnection::SendControlFrame(const QuicFrame& frame) { |
| if (SupportsMultiplePacketNumberSpaces() && |
| (encryption_level_ == ENCRYPTION_INITIAL || |
| encryption_level_ == ENCRYPTION_HANDSHAKE) && |
| frame.type != PING_FRAME) { |
| // Allow PING frame to be sent without APPLICATION key. For example, when |
| // anti-amplification limit is used, client needs to send something to avoid |
| // handshake deadlock. |
| QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame |
| << " at encryption level: " << encryption_level_; |
| return false; |
| } |
| ScopedPacketFlusher flusher(this); |
| const bool consumed = |
| packet_creator_.ConsumeRetransmittableControlFrame(frame); |
| if (!consumed) { |
| QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame; |
| return false; |
| } |
| if (frame.type == PING_FRAME) { |
| // Flush PING frame immediately. |
| packet_creator_.FlushCurrentPacket(); |
| stats_.ping_frames_sent++; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPingSent(); |
| } |
| } |
| if (frame.type == BLOCKED_FRAME) { |
| stats_.blocked_frames_sent++; |
| } |
| return true; |
| } |
| |
| void QuicConnection::OnStreamReset(QuicStreamId id, |
| QuicRstStreamErrorCode error) { |
| if (error == QUIC_STREAM_NO_ERROR) { |
| // All data for streams which are reset with QUIC_STREAM_NO_ERROR must |
| // be received by the peer. |
| return; |
| } |
| // Flush stream frames of reset stream. |
| if (packet_creator_.HasPendingStreamFramesOfStream(id)) { |
| ScopedPacketFlusher flusher(this); |
| packet_creator_.FlushCurrentPacket(); |
| } |
| // TODO(ianswett): Consider checking for 3 RTOs when the last stream is |
| // cancelled as well. |
| } |
| |
| const QuicConnectionStats& QuicConnection::GetStats() { |
| const RttStats* rtt_stats = sent_packet_manager_.GetRttStats(); |
| |
| // Update rtt and estimated bandwidth. |
| QuicTime::Delta min_rtt = rtt_stats->min_rtt(); |
| if (min_rtt.IsZero()) { |
| // If min RTT has not been set, use initial RTT instead. |
| min_rtt = rtt_stats->initial_rtt(); |
| } |
| stats_.min_rtt_us = min_rtt.ToMicroseconds(); |
| |
| QuicTime::Delta srtt = rtt_stats->SmoothedOrInitialRtt(); |
| stats_.srtt_us = srtt.ToMicroseconds(); |
| |
| stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate(); |
| sent_packet_manager_.GetSendAlgorithm()->PopulateConnectionStats(&stats_); |
| stats_.max_packet_size = packet_creator_.max_packet_length(); |
| stats_.max_received_packet_size = 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 (encryption_level_ == ENCRYPTION_FORWARD_SECURE) { |
| // 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 (has_decryption_key) { |
| // We already have the key for this decryption level, therefore no |
| // future keys will allow it be decrypted. |
| return false; |
| } |
| if (version().KnowsWhichDecrypterToUse() && |
| decryption_level <= encryption_level_) { |
| // On versions that know which decrypter to use, we install keys in order |
| // so we will not get newer keys for lower encryption levels. |
| return false; |
| } |
| return true; |
| } |
| |
| std::string QuicConnection::UndecryptablePacketsInfo() const { |
| std::string info = absl::StrCat( |
| "num_undecryptable_packets: ", undecryptable_packets_.size(), " {"); |
| for (const auto& packet : undecryptable_packets_) { |
| absl::StrAppend(&info, "[", |
| EncryptionLevelToString(packet.encryption_level), ", ", |
| packet.packet->length(), "]"); |
| } |
| absl::StrAppend(&info, "}"); |
| return info; |
| } |
| |
| void QuicConnection::MaybeUpdatePacketCreatorMaxPacketLengthAndPadding() { |
| QuicByteCount max_packet_length = GetLimitedMaxPacketSize(long_term_mtu_); |
| if (legacy_version_encapsulation_in_progress_) { |
| QUICHE_DCHECK(legacy_version_encapsulation_enabled_); |
| const QuicByteCount minimum_overhead = |
| QuicLegacyVersionEncapsulator::GetMinimumOverhead( |
| legacy_version_encapsulation_sni_); |
| if (max_packet_length < minimum_overhead) { |
| QUIC_BUG << "Cannot apply Legacy Version Encapsulation overhead because " |
| << "max_packet_length " << max_packet_length |
| << " < minimum_overhead " << minimum_overhead; |
| legacy_version_encapsulation_in_progress_ = false; |
| legacy_version_encapsulation_enabled_ = false; |
| MaybeUpdatePacketCreatorMaxPacketLengthAndPadding(); |
| return; |
| } |
| max_packet_length -= minimum_overhead; |
| } |
| packet_creator_.SetMaxPacketLength(max_packet_length); |
| } |
| |
| void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const QuicReceivedPacket& packet) { |
| if (!connected_) { |
| return; |
| } |
| QUIC_DVLOG(2) << ENDPOINT << "Received encrypted " << packet.length() |
| << " bytes:" << std::endl |
| << quiche::QuicheTextUtils::HexDump( |
| absl::string_view(packet.data(), packet.length())); |
| QUIC_BUG_IF(current_packet_data_ != nullptr) |
| << "ProcessUdpPacket must not be called while processing a packet."; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnPacketReceived(self_address, peer_address, packet); |
| } |
| current_incoming_packet_received_bytes_counted_ = false; |
| last_size_ = packet.length(); |
| current_packet_data_ = packet.data(); |
| |
| last_packet_destination_address_ = self_address; |
| last_packet_source_address_ = peer_address; |
| if (!default_path_.self_address.IsInitialized()) { |
| default_path_.self_address = last_packet_destination_address_; |
| } |
| |
| if (!direct_peer_address_.IsInitialized()) { |
| UpdatePeerAddress(last_packet_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 (!count_bytes_on_alternative_path_separately_) { |
| if (EnforceAntiAmplificationLimit()) { |
| default_path_.bytes_received_before_address_validation += last_size_; |
| } |
| } else if (IsDefaultPath(last_packet_destination_address_, |
| last_packet_source_address_) && |
| EnforceAntiAmplificationLimit()) { |
| QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 1, 5); |
| current_incoming_packet_received_bytes_counted_ = true; |
| default_path_.bytes_received_before_address_validation += last_size_; |
| } |
| |
| // 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 << "Packet receipt time:" |
| << packet.receipt_time().ToDebuggingValue() |
| << " too far from current time:" |
| << clock_->ApproximateNow().ToDebuggingValue(); |
| } |
| time_of_last_received_packet_ = packet.receipt_time(); |
| QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: " |
| << packet.receipt_time().ToDebuggingValue() << " from peer " |
| << last_packet_source_address_; |
| |
| ScopedPacketFlusher flusher(this); |
| if (!framer_.ProcessPacket(packet)) { |
| // If we are unable to decrypt this packet, it might be |
| // because the CHLO or SHLO packet was lost. |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Unable to process packet. Last packet processed: " |
| << last_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(); |
| } |
| } |
| |
| MaybeProcessCoalescedPackets(); |
| MaybeProcessUndecryptablePackets(); |
| MaybeSendInResponseToPacket(); |
| SetPingAlarm(); |
| 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 << ENDPOINT << error_details; |
| CloseConnection(QUIC_INTERNAL_ERROR, error_details, |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return; |
| } |
| |
| // Add a flusher to ensure the connection is marked app-limited. |
| 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(); |
| } |
| } |
| |
| // TODO(danzh) PATH_RESPONSE is of more interest to the peer than ACK, |
| // evaluate if it's worth to send them before sending ACKs. |
| while (!pending_path_challenge_payloads_.empty()) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 4, 5); |
| const PendingPathChallenge& pending_path_challenge = |
| pending_path_challenge_payloads_.front(); |
| if (!SendPathResponse(pending_path_challenge.received_path_challenge, |
| pending_path_challenge.peer_address)) { |
| break; |
| } |
| pending_path_challenge_payloads_.pop_front(); |
| } |
| WriteNewData(); |
| } |
| |
| void QuicConnection::WriteNewData() { |
| // Sending queued packets may have caused the socket to become write blocked, |
| // or the congestion manager to prohibit sending. If we've sent everything |
| // we had queued and we're still not blocked, let the visitor know it can |
| // write more. |
| if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) { |
| return; |
| } |
| |
| { |
| ScopedPacketFlusher flusher(this); |
| 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 stream didn't write out all of its |
| // bytes. Register for 'immediate' resumption so we'll keep writing after |
| // other connections and events have had a chance to use the thread. |
| send_alarm_->Set(clock_->ApproximateNow()); |
| } |
| } |
| |
| void QuicConnection::WriteIfNotBlocked() { |
| if (!HandleWriteBlocked()) { |
| OnCanWrite(); |
| } |
| } |
| |
| void QuicConnection::WriteAndBundleAcksIfNotBlocked() { |
| if (!HandleWriteBlocked()) { |
| ScopedPacketFlusher flusher(this); |
| WriteIfNotBlocked(); |
| } |
| } |
| |
| bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) { |
| if (perspective_ == Perspective::IS_SERVER && |
| default_path_.self_address.IsInitialized() && |
| last_packet_destination_address_.IsInitialized() && |
| default_path_.self_address != last_packet_destination_address_) { |
| // Allow change between pure IPv4 and equivalent mapped IPv4 address. |
| if (default_path_.self_address.port() != |
| last_packet_destination_address_.port() || |
| default_path_.self_address.host().Normalized() != |
| last_packet_destination_address_.host().Normalized()) { |
| if (!visitor_->AllowSelfAddressChange()) { |
| CloseConnection( |
| QUIC_ERROR_MIGRATING_ADDRESS, |
| "Self address migration is not supported at the server.", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| return false; |
| } |
| } |
| default_path_.self_address = last_packet_destination_address_; |
| } |
| |
| if (PacketCanReplaceConnectionId(header, perspective_) && |
| server_connection_id_ != header.source_connection_id) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID " |
| << server_connection_id_ << " with " |
| << header.source_connection_id; |
| if (!original_destination_connection_id_.has_value()) { |
| original_destination_connection_id_ = server_connection_id_; |
| } |
| server_connection_id_ = header.source_connection_id; |
| packet_creator_.SetServerConnectionId(server_connection_id_); |
| } |
| |
| if (!ValidateReceivedPacketNumber(header.packet_number)) { |
| return false; |
| } |
| |
| if (!version_negotiated_) { |
| if (perspective_ == Perspective::IS_CLIENT) { |
| QUICHE_DCHECK(!header.version_flag || header.form != GOOGLE_QUIC_PACKET); |
| if (!version().HasIetfInvariantHeader()) { |
| // If the client gets a packet without the version flag from the server |
| // it should stop sending version since the version negotiation is done. |
| // IETF QUIC stops sending version once encryption level switches to |
| // forward secure. |
| packet_creator_.StopSendingVersion(); |
| } |
| version_negotiated_ = true; |
| OnSuccessfulVersionNegotiation(); |
| } |
| } |
| |
| if (last_size_ > largest_received_packet_size_) { |
| largest_received_packet_size_ = last_size_; |
| } |
| |
| if (perspective_ == Perspective::IS_SERVER && |
| encryption_level_ == ENCRYPTION_INITIAL && |
| last_size_ > packet_creator_.max_packet_length()) { |
| SetMaxPacketLength(last_size_); |
| } |
| return true; |
| } |
| |
| bool QuicConnection::ValidateReceivedPacketNumber( |
| QuicPacketNumber packet_number) { |
| // If this packet has already been seen, or the sender has told us that it |
| // will not be retransmitted, then stop processing the packet. |
| if (!uber_received_packet_manager_.IsAwaitingPacket( |
| last_decrypted_packet_level_, packet_number)) { |
| QUIC_DLOG(INFO) << ENDPOINT << "Packet " << packet_number |
| << " no longer being waited for at level " |
| << static_cast<int>(last_decrypted_packet_level_) |
| << ". Discarding."; |
| if (debug_visitor_ != nullptr) { |
| debug_visitor_->OnDuplicatePacket(packet_number); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void QuicConnection::WriteQueuedPackets() { |
| QUICHE_DCHECK(!writer_->IsWriteBlocked()); |
| |
| QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsBeforeWrite", |
| buffered_packets_.size(), 1, 1000, 50, ""); |
| |
| while (!buffered_packets_.empty()) { |
| if (HandleWriteBlocked()) { |
| break; |
| } |
| const BufferedPacket& packet = buffered_packets_.front(); |
| WriteResult result = writer_->WritePacket( |
| packet.encrypted_buffer.data(), packet.encrypted_buffer.length(), |
| packet.self_address.host(), packet.peer_address, per_packet_options_); |
| QUIC_DVLOG(1) << ENDPOINT << "Sending buffered packet, result: " << result; |
| if (IsMsgTooBig(result) && |
| packet.encrypted_buffer.length() > long_term_mtu_) { |
| // When MSG_TOO_BIG is returned, the system typically knows what the |
| // actual MTU is, so there is no need to probe further. |
| // TODO(wub): Reduce max packet size to a safe default, or the actual MTU. |
| mtu_discoverer_.Disable(); |
| mtu_discovery_alarm_->Cancel(); |
| buffered_packets_.pop_front(); |
| continue; |
| } |
| if (IsWriteError(result.status)) { |
| OnWriteError(result.error_code); |
| break; |
| } |
| if (result.status == WRITE_STATUS_OK || |
| result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) { |
| buffered_packets_.pop_front(); |
| } |
| if (IsWriteBlockedStatus(result.status)) { |
| visitor_->OnWriteBlocked(); |
| break; |
| } |
| } |
| } |
| |
| void QuicConnection::SendProbingRetransmissions() { |
| while (sent_packet_manager_.GetSendAlgorithm()->ShouldSendProbingPacket() && |
| CanWrite(HAS_RETRANSMITTABLE_DATA)) { |
| if (!visitor_->SendProbingData()) { |
| QUIC_DVLOG(1) |
| << "Cannot send probing retransmissions: nothing to retransmit."; |
| break; |
| } |
| } |
| } |
| |
| void QuicConnection::MarkZeroRttPacketsForRetransmission(int reject_reason) { |
| sent_packet_manager_.MarkZeroRttPacketsForRetransmission(); |
| if (debug_visitor_ != nullptr && version().UsesTls()) { |
| debug_visitor_->OnZeroRttRejected(reject_reason); |
| } |
| } |
| |
| void QuicConnection::NeuterUnencryptedPackets() { |
| sent_packet_manager_.NeuterUnencryptedPackets(); |
| // This may have changed the retransmission timer, so re-arm it. |
| SetRetransmissionAlarm(); |
| if (default_enable_5rto_blackhole_detection_) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_default_enable_5rto_blackhole_detection2, |
| 1, 3); |
| // Consider this as forward progress since this is called when initial key |
| // gets discarded (or previous unencrypted data is not needed anymore). |
| OnForwardProgressMade(); |
| } |
| if (SupportsMultiplePacketNumberSpaces()) { |
| // Stop sending ack of initial packet number space. |
| uber_received_packet_manager_.ResetAckStates(ENCRYPTION_INITIAL); |
| // Re-arm ack alarm. |
| ack_alarm_->Update(uber_received_packet_manager_.GetEarliestAckTimeout(), |
| kAlarmGranularity); |
| } |
| } |
| |
| bool QuicConnection::ShouldGeneratePacket( |
| HasRetransmittableData retransmittable, |
| IsHandshake handshake) { |
| QUICHE_DCHECK(handshake != IS_HANDSHAKE || |
| QuicVersionUsesCryptoFrames(transport_version())) |
| << ENDPOINT |
| << "Handshake in STREAM frames should not check ShouldGeneratePacket"; |
| if (!count_bytes_on_alternative_path_separately_) { |
| return CanWrite(retransmittable); |
| } |
| QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 4, 5); |
| if (IsDefaultPath(default_path_.self_address, |
| packet_creator_.peer_address())) { |
| return CanWrite(retransmittable); |
| } |
| // This is checking on the alternative path with a different peer address. The |
| // self address and the writer used are the same as the default path. In the |
| // case of different self address and writer, writing packet would use a |
| // differnt code path without checking the states of the default writer. |
| return connected_ && !HandleWriteBlocked(); |
| } |
| |
| const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() { |
| if (!ack_frequency_sent_ && sent_packet_manager_.CanSendAckFrequency()) { |
| if (packet_creator_.NextSendingPacketNumber() >= |
| FirstSendingPacketNumber() + kMinReceivedBeforeAckDecimation) { |
| QUIC_RELOADABLE_FLAG_COUNT_N(quic_can_send_ack_frequency, 3, 3); |
| ack_frequency_sent_ = true; |
| auto frame = sent_packet_manager_.GetUpdatedAckFrequencyFrame(); |
| visitor_->SendAckFrequency(frame); |
| } |
| } |
| |
| QuicFrames frames; |
| const bool has_pending_ack = |
| uber_received_packet_manager_ |
| .GetAckTimeout(QuicUtils::GetPacketNumberSpace(encryption_level_)) |
| .IsInitialized(); |
| if (!has_pending_ack && stop_waiting_count_ <= 1) { |
| // No need to send an ACK. |
| return frames; |
| } |
| ResetAckStates(); |
| |
| QUIC_DVLOG(1) << ENDPOINT << "Bundle an ACK opportunistically"; |
| QuicFrame updated_ack_frame = GetUpdatedAckFrame(); |
| QUIC_BUG_IF(updated_ack_frame.ack_frame->packets.Empty()) |
| << ENDPOINT << "Attempted to opportunistically bundle an empty " |
| << encryption_level_ << " ACK, " << (has_pending_ack ? "" : "!") |
| << "has_pending_ack, stop_waiting_count_ " << stop_waiting_count_; |
| frames.push_back(updated_ack_frame); |
| |
| if (!no_stop_waiting_frames_) { |
| QuicStopWaitingFrame stop_waiting; |
| PopulateStopWaitingFrame(&stop_waiting); |
| frames.push_back(QuicFrame(stop_waiting)); |
| } |
| return frames; |
| } |
| |
| bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) { |
| if (!connected_) { |
| return false; |
| } |
| |
| if (fill_coalesced_packet_) { |
| // Try to coalesce packet, only allow to write when creator is on soft max |
| // packet length. Given the next created packet is going to fill current |
| // coalesced packet, do not check amplification factor. |
| return packet_creator_.HasSoftMaxPacketLength(); |
| } |
| |
| if (LimitedByAmplificationFactor()) { |
| // Server is constrained by the amplification restriction. |
| QUIC_CODE_COUNT(quic_throttled_by_amplification_limit); |
| QUIC_DVLOG(1) << ENDPOINT |
| << "Constrained by amplification restriction to peer address " |
| << default_path_.peer_address << " bytes received " |
| << default_path_.bytes_received_before_address_validation |
| << ", bytes sent" |
| << default_path_.bytes_sent_before_address_validation; |
| ++stats_.num_amplification_throttling; |
| return false; |
| } |
| |
| if (sent_packet_manager_.pending_timer_transmission_count() > 0) { |
| // Force sending the retransmissions for HANDSHAKE, TLP, RTO, PROBING cases. |
| return true; |
| } |
| |
| if (HandleWriteBlocked()) { |
| return false; |
| } |
| |
| // Allow acks and probing frames to be sent immediately. |
| if (retransmittable == NO_RETRANSMITTABLE_DATA) { |
| return true; |
| } |
|