blob: d6d07c11bed2eadff3d04c5494c98d506a69af86 [file] [log] [blame]
// 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_server_stats.h"
#include "quic/platform/api/quic_socket_address.h"
#include "common/platform/api/quiche_flag_utils.h"
#include "common/quiche_text_utils.h"
namespace quic {
class QuicDecrypter;
class QuicEncrypter;
namespace {
// Maximum number of consecutive sent nonretransmittable packets.
const QuicPacketCount kMaxConsecutiveNonRetransmittablePackets = 19;
// The minimum release time into future in ms.
const int kMinReleaseTimeIntoFutureMs = 1;
// Base class of all alarms owned by a QuicConnection.
class QuicConnectionAlarmDelegate : public QuicAlarm::Delegate {
public:
explicit QuicConnectionAlarmDelegate(QuicConnection* connection)
: connection_(connection) {}
QuicConnectionAlarmDelegate(const QuicConnectionAlarmDelegate&) = delete;
QuicConnectionAlarmDelegate& operator=(const QuicConnectionAlarmDelegate&) =
delete;
QuicConnectionContext* GetConnectionContext() override {
return (connection_ == nullptr) ? nullptr : connection_->context();
}
protected:
QuicConnection* connection_;
};
// An alarm that is scheduled to send an ack if a timeout occurs.
class AckAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->ack_frame_updated());
QUICHE_DCHECK(connection_->connected());
QuicConnection::ScopedPacketFlusher flusher(connection_);
if (connection_->SupportsMultiplePacketNumberSpaces()) {
connection_->SendAllPendingAcks();
} else {
connection_->SendAck();
}
}
};
// This alarm will be scheduled any time a data-bearing packet is sent out.
// When the alarm goes off, the connection checks to see if the oldest packets
// have been acked, and retransmit them if they have not.
class RetransmissionAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->OnRetransmissionTimeout();
}
};
// An alarm that is scheduled when the SentPacketManager requires a delay
// before sending packets and fires when the packet may be sent.
class SendAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->WriteIfNotBlocked();
}
};
class PingAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->OnPingTimeout();
}
};
class MtuDiscoveryAlarmDelegate : public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->DiscoverMtu();
}
};
class ProcessUndecryptablePacketsAlarmDelegate
: public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
QuicConnection::ScopedPacketFlusher flusher(connection_);
connection_->MaybeProcessUndecryptablePackets();
}
};
class DiscardPreviousOneRttKeysAlarmDelegate
: public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
connection_->DiscardPreviousOneRttKeys();
}
};
class DiscardZeroRttDecryptionKeysAlarmDelegate
: public QuicConnectionAlarmDelegate {
public:
using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
void OnAlarm() override {
QUICHE_DCHECK(connection_->connected());
QUIC_DLOG(INFO) << "0-RTT discard alarm fired";
connection_->RemoveDecrypter(ENCRYPTION_ZERO_RTT);
}
};
// When the clearer goes out of scope, the coalesced packet gets cleared.
class ScopedCoalescedPacketClearer {
public:
explicit ScopedCoalescedPacketClearer(QuicCoalescedPacket* coalesced)
: coalesced_(coalesced) {}
~ScopedCoalescedPacketClearer() { coalesced_->Clear(); }
private:
QuicCoalescedPacket* coalesced_; // Unowned.
};
// Whether this incoming packet is allowed to replace our connection ID.
bool PacketCanReplaceServerConnectionId(const QuicPacketHeader& header,
Perspective perspective) {
return perspective == Perspective::IS_CLIENT &&
header.form == IETF_QUIC_LONG_HEADER_PACKET &&
header.version.IsKnown() &&
header.version.AllowsVariableLengthConnectionIds() &&
(header.long_packet_type == INITIAL ||
header.long_packet_type == RETRY);
}
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()),
client_connection_id_is_set_(false),
direct_peer_address_(initial_peer_address),
default_path_(initial_self_address, QuicSocketAddress(),
/*client_connection_id=*/EmptyQuicConnectionId(),
server_connection_id,
/*stateless_reset_token=*/absl::nullopt),
active_effective_peer_migration_type_(NO_CHANGE),
support_key_update_for_connection_(false),
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),
last_received_packet_info_(clock_->ApproximateNow()),
sent_packet_manager_(perspective, clock_, random_generator_, &stats_,
GetDefaultCongestionControlType()),
version_negotiated_(false),
perspective_(perspective),
connected_(true),
can_truncate_connection_ids_(perspective == Perspective::IS_SERVER),
mtu_probe_count_(0),
previous_validated_mtu_(0),
peer_max_packet_size_(kDefaultMaxPacketSizeTransportParam),
largest_received_packet_size_(0),
write_error_occurred_(false),
no_stop_waiting_frames_(version().HasIetfInvariantHeader()),
consecutive_num_packets_with_no_retransmittable_frames_(0),
max_consecutive_num_packets_with_no_retransmittable_frames_(
kMaxConsecutiveNonRetransmittablePackets),
bundle_retransmittable_with_pto_ack_(false),
fill_up_link_during_probing_(false),
probing_retransmission_pending_(false),
last_control_frame_id_(kInvalidControlFrameId),
is_path_degrading_(false),
processing_ack_frame_(false),
supports_release_time_(false),
release_time_into_future_(QuicTime::Delta::Zero()),
blackhole_detector_(this, &arena_, alarm_factory_, &context_),
idle_network_detector_(this, clock_->ApproximateNow(), &arena_,
alarm_factory_, &context_),
path_validator_(alarm_factory_, &arena_, this, random_generator_,
&context_),
most_recent_frame_type_(NUM_FRAME_TYPES) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
default_path_.self_address.IsInitialized());
support_multiple_connection_ids_ =
version().HasIetfQuicFrames() &&
GetQuicRestartFlag(
quic_dispatcher_support_multiple_cid_per_connection_v2);
QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID "
<< server_connection_id
<< " and version: " << ParsedQuicVersionToString(version());
QUIC_BUG_IF(quic_bug_12714_2, !QuicUtils::IsConnectionIdValidForVersion(
server_connection_id, transport_version()))
<< "QuicConnection: attempted to use server connection ID "
<< server_connection_id << " which is invalid with version " << version();
framer_.set_visitor(this);
stats_.connection_creation_time = clock_->ApproximateNow();
// TODO(ianswett): Supply the NetworkChangeVisitor as a constructor argument
// and make it required non-null, because it's always used.
sent_packet_manager_.SetNetworkChangeVisitor(this);
if (GetQuicRestartFlag(quic_offload_pacing_to_usps2)) {
sent_packet_manager_.SetPacingAlarmGranularity(QuicTime::Delta::Zero());
release_time_into_future_ =
QuicTime::Delta::FromMilliseconds(kMinReleaseTimeIntoFutureMs);
}
// Allow the packet writer to potentially reduce the packet size to a value
// even smaller than kDefaultMaxPacketSize.
SetMaxPacketLength(perspective_ == Perspective::IS_SERVER
? kDefaultServerMaxPacketSize
: kDefaultMaxPacketSize);
uber_received_packet_manager_.set_max_ack_ranges(255);
MaybeEnableMultiplePacketNumberSpacesSupport();
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
supported_versions.size() == 1);
InstallInitialCrypters(default_path_.server_connection_id);
// On the server side, version negotiation has been done by the dispatcher,
// and the server connection is created with the right version.
if (perspective_ == Perspective::IS_SERVER) {
SetVersionNegotiated();
}
if (default_enable_5rto_blackhole_detection_) {
num_rtos_for_blackhole_detection_ = 5;
if (GetQuicReloadableFlag(quic_disable_server_blackhole_detection) &&
perspective_ == Perspective::IS_SERVER) {
QUIC_RELOADABLE_FLAG_COUNT(quic_disable_server_blackhole_detection);
blackhole_detection_disabled_ = true;
}
}
packet_creator_.SetDefaultPeerAddress(initial_peer_address);
}
void QuicConnection::InstallInitialCrypters(QuicConnectionId connection_id) {
CrypterPair crypters;
CryptoUtils::CreateInitialObfuscators(perspective_, version(), connection_id,
&crypters);
SetEncrypter(ENCRYPTION_INITIAL, std::move(crypters.encrypter));
if (version().KnowsWhichDecrypterToUse()) {
InstallDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter));
} else {
SetDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter));
}
}
QuicConnection::~QuicConnection() {
QUICHE_DCHECK_GE(stats_.max_egress_mtu, long_term_mtu_);
if (owns_writer_) {
delete writer_;
}
ClearQueuedPackets();
if (stats_
.num_tls_server_zero_rtt_packets_received_after_discarding_decrypter >
0) {
QUIC_CODE_COUNT_N(
quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 2,
3);
} else {
QUIC_CODE_COUNT_N(
quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 3,
3);
}
}
void QuicConnection::ClearQueuedPackets() {
buffered_packets_.clear();
}
bool QuicConnection::ValidateConfigConnectionIds(const QuicConfig& config) {
QUICHE_DCHECK(config.negotiated());
if (!version().UsesTls()) {
// QUIC+TLS is required to transmit connection ID transport parameters.
return true;
}
// This function validates connection IDs as defined in IETF draft-28 and
// later.
// Validate initial_source_connection_id.
QuicConnectionId expected_initial_source_connection_id;
if (perspective_ == Perspective::IS_CLIENT) {
expected_initial_source_connection_id = default_path_.server_connection_id;
} else {
expected_initial_source_connection_id = default_path_.client_connection_id;
}
if (!config.HasReceivedInitialSourceConnectionId() ||
config.ReceivedInitialSourceConnectionId() !=
expected_initial_source_connection_id) {
std::string received_value;
if (config.HasReceivedInitialSourceConnectionId()) {
received_value = config.ReceivedInitialSourceConnectionId().ToString();
} else {
received_value = "none";
}
std::string error_details =
absl::StrCat("Bad initial_source_connection_id: expected ",
expected_initial_source_connection_id.ToString(),
", received ", received_value);
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
if (perspective_ == Perspective::IS_CLIENT) {
// Validate original_destination_connection_id.
if (!config.HasReceivedOriginalConnectionId() ||
config.ReceivedOriginalConnectionId() !=
GetOriginalDestinationConnectionId()) {
std::string received_value;
if (config.HasReceivedOriginalConnectionId()) {
received_value = config.ReceivedOriginalConnectionId().ToString();
} else {
received_value = "none";
}
std::string error_details =
absl::StrCat("Bad original_destination_connection_id: expected ",
GetOriginalDestinationConnectionId().ToString(),
", received ", received_value);
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// Validate retry_source_connection_id.
if (retry_source_connection_id_.has_value()) {
// We received a RETRY packet, validate that the retry source
// connection ID from the config matches the one from the RETRY.
if (!config.HasReceivedRetrySourceConnectionId() ||
config.ReceivedRetrySourceConnectionId() !=
retry_source_connection_id_.value()) {
std::string received_value;
if (config.HasReceivedRetrySourceConnectionId()) {
received_value = config.ReceivedRetrySourceConnectionId().ToString();
} else {
received_value = "none";
}
std::string error_details =
absl::StrCat("Bad retry_source_connection_id: expected ",
retry_source_connection_id_.value().ToString(),
", received ", received_value);
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
} else {
// We did not receive a RETRY packet, make sure we did not receive the
// retry_source_connection_id transport parameter.
if (config.HasReceivedRetrySourceConnectionId()) {
std::string error_details = absl::StrCat(
"Bad retry_source_connection_id: did not receive RETRY but "
"received ",
config.ReceivedRetrySourceConnectionId().ToString());
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
}
}
return true;
}
void QuicConnection::SetFromConfig(const QuicConfig& config) {
if (config.negotiated()) {
// Handshake complete, set handshake timeout to Infinite.
SetNetworkTimeouts(QuicTime::Delta::Infinite(),
config.IdleNetworkTimeout());
idle_timeout_connection_close_behavior_ =
ConnectionCloseBehavior::SILENT_CLOSE;
if (perspective_ == Perspective::IS_SERVER) {
idle_timeout_connection_close_behavior_ = ConnectionCloseBehavior::
SILENT_CLOSE_WITH_CONNECTION_CLOSE_PACKET_SERIALIZED;
}
if (config.HasClientRequestedIndependentOption(kNSLC, perspective_)) {
idle_timeout_connection_close_behavior_ =
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET;
}
if (!ValidateConfigConnectionIds(config)) {
return;
}
support_key_update_for_connection_ =
config.KeyUpdateSupportedForConnection();
framer_.SetKeyUpdateSupportForConnection(
support_key_update_for_connection_);
} else {
SetNetworkTimeouts(config.max_time_before_crypto_handshake(),
config.max_idle_time_before_crypto_handshake());
if (config.HasClientRequestedIndependentOption(kNCHP, perspective_)) {
packet_creator_.set_chaos_protection_enabled(false);
}
}
if (support_multiple_connection_ids_ &&
config.HasReceivedPreferredAddressConnectionIdAndToken()) {
QuicNewConnectionIdFrame frame;
std::tie(frame.connection_id, frame.stateless_reset_token) =
config.ReceivedPreferredAddressConnectionIdAndToken();
frame.sequence_number = 1u;
frame.retire_prior_to = 0u;
OnNewConnectionIdFrameInner(frame);
}
sent_packet_manager_.SetFromConfig(config);
if (perspective_ == Perspective::IS_SERVER &&
config.HasClientSentConnectionOption(kAFF2, perspective_)) {
send_ack_frequency_on_handshake_completion_ = true;
}
if (config.HasReceivedBytesForConnectionId() &&
can_truncate_connection_ids_) {
packet_creator_.SetServerConnectionIdLength(
config.ReceivedBytesForConnectionId());
}
max_undecryptable_packets_ = config.max_undecryptable_packets();
if (!GetQuicReloadableFlag(quic_enable_mtu_discovery_at_server)) {
if (config.HasClientRequestedIndependentOption(kMTUH, perspective_)) {
SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh);
}
}
if (config.HasClientRequestedIndependentOption(kMTUL, perspective_)) {
SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeLow);
}
if (default_enable_5rto_blackhole_detection_) {
if (config.HasClientRequestedIndependentOption(kCBHD, perspective_)) {
QUIC_CODE_COUNT(quic_client_only_blackhole_detection);
blackhole_detection_disabled_ = true;
}
if (config.HasClientSentConnectionOption(kNBHD, perspective_)) {
blackhole_detection_disabled_ = true;
}
if (config.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()) {
default_path_.stateless_reset_token = config.ReceivedStatelessResetToken();
}
if (config.HasReceivedAckDelayExponent()) {
framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent());
}
if (config.HasClientSentConnectionOption(kEACK, perspective_)) {
bundle_retransmittable_with_pto_ack_ = true;
}
if (config.HasClientSentConnectionOption(kDFER, perspective_)) {
defer_send_in_response_to_packets_ = false;
}
const bool remove_connection_migration_connection_option =
GetQuicReloadableFlag(quic_remove_connection_migration_connection_option);
if (remove_connection_migration_connection_option) {
QUIC_RELOADABLE_FLAG_COUNT(
quic_remove_connection_migration_connection_option);
}
if (framer_.version().HasIetfQuicFrames() && use_path_validator_ &&
count_bytes_on_alternative_path_separately_ &&
GetQuicReloadableFlag(quic_server_reverse_validate_new_path3) &&
(remove_connection_migration_connection_option ||
config.HasClientSentConnectionOption(kRVCM, perspective_))) {
QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 6, 6);
validate_client_addresses_ = true;
}
// Having connection_migration_use_new_cid_ depends on the same set of flags
// and connection option on both client and server sides has the advantage of:
// 1) Less chance of skew in using new connection ID or not between client
// and server in unit tests with random flag combinations.
// 2) Client side's rollout can be protected by the same connection option.
connection_migration_use_new_cid_ =
support_multiple_connection_ids_ && validate_client_addresses_ &&
GetQuicReloadableFlag(quic_drop_unsent_path_response) &&
GetQuicReloadableFlag(quic_connection_migration_use_new_cid_v2);
if (config.HasReceivedMaxPacketSize()) {
peer_max_packet_size_ = config.ReceivedMaxPacketSize();
MaybeUpdatePacketCreatorMaxPacketLengthAndPadding();
}
if (config.HasReceivedMaxDatagramFrameSize()) {
packet_creator_.SetMaxDatagramFrameSize(
config.ReceivedMaxDatagramFrameSize());
}
supports_release_time_ =
writer_ != nullptr && writer_->SupportsReleaseTime() &&
!config.HasClientSentConnectionOption(kNPCO, perspective_);
if (supports_release_time_) {
UpdateReleaseTimeIntoFuture();
}
}
void QuicConnection::EnableLegacyVersionEncapsulation(
const std::string& server_name) {
if (perspective_ != Perspective::IS_CLIENT) {
QUIC_BUG(quic_bug_10511_1)
<< "Cannot enable Legacy Version Encapsulation on the server";
return;
}
if (legacy_version_encapsulation_enabled_) {
QUIC_BUG(quic_bug_10511_2)
<< "Do not call EnableLegacyVersionEncapsulation twice";
return;
}
if (!QuicHostnameUtils::IsValidSNI(server_name)) {
// Legacy Version Encapsulation is only used when SNI is transmitted.
QUIC_DLOG(INFO)
<< "Refusing to use Legacy Version Encapsulation with invalid SNI \""
<< server_name << "\"";
return;
}
QUIC_DLOG(INFO) << "Enabling Legacy Version Encapsulation with SNI \""
<< server_name << "\"";
legacy_version_encapsulation_enabled_ = true;
legacy_version_encapsulation_sni_ = server_name;
}
bool QuicConnection::MaybeTestLiveness() {
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
if (encryption_level_ != ENCRYPTION_FORWARD_SECURE) {
return false;
}
const QuicTime idle_network_deadline =
idle_network_detector_.GetIdleNetworkDeadline();
if (!idle_network_deadline.IsInitialized()) {
return false;
}
const QuicTime now = clock_->ApproximateNow();
if (now > idle_network_deadline) {
QUIC_DLOG(WARNING) << "Idle network deadline has passed";
return false;
}
const QuicTime::Delta timeout = idle_network_deadline - now;
if (2 * timeout > idle_network_detector_.idle_network_timeout()) {
// Do not test liveness if timeout is > half timeout. This is used to
// prevent an infinite loop for short idle timeout.
return false;
}
if (!sent_packet_manager_.IsLessThanThreePTOs(timeout)) {
return false;
}
SendConnectivityProbingPacket(writer_, peer_address());
return true;
}
void QuicConnection::ApplyConnectionOptions(
const QuicTagVector& connection_options) {
sent_packet_manager_.ApplyConnectionOptions(connection_options);
}
void QuicConnection::OnSendConnectionState(
const CachedNetworkParameters& cached_network_params) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnSendConnectionState(cached_network_params);
}
}
void QuicConnection::OnReceiveConnectionState(
const CachedNetworkParameters& cached_network_params) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnReceiveConnectionState(cached_network_params);
}
}
void QuicConnection::ResumeConnectionState(
const CachedNetworkParameters& cached_network_params,
bool max_bandwidth_resumption) {
sent_packet_manager_.ResumeConnectionState(cached_network_params,
max_bandwidth_resumption);
}
void QuicConnection::SetMaxPacingRate(QuicBandwidth max_pacing_rate) {
sent_packet_manager_.SetMaxPacingRate(max_pacing_rate);
}
void QuicConnection::AdjustNetworkParameters(
const SendAlgorithmInterface::NetworkParams& params) {
sent_packet_manager_.AdjustNetworkParameters(params);
}
void QuicConnection::SetLossDetectionTuner(
std::unique_ptr<LossDetectionTunerInterface> tuner) {
sent_packet_manager_.SetLossDetectionTuner(std::move(tuner));
}
void QuicConnection::OnConfigNegotiated() {
sent_packet_manager_.OnConfigNegotiated();
if (GetQuicReloadableFlag(quic_enable_mtu_discovery_at_server) &&
perspective_ == Perspective::IS_SERVER) {
QUIC_RELOADABLE_FLAG_COUNT(quic_enable_mtu_discovery_at_server);
SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh);
}
}
QuicBandwidth QuicConnection::MaxPacingRate() const {
return sent_packet_manager_.MaxPacingRate();
}
bool QuicConnection::SelectMutualVersion(
const ParsedQuicVersionVector& available_versions) {
// Try to find the highest mutual version by iterating over supported
// versions, starting with the highest, and breaking out of the loop once we
// find a matching version in the provided available_versions vector.
const ParsedQuicVersionVector& supported_versions =
framer_.supported_versions();
for (size_t i = 0; i < supported_versions.size(); ++i) {
const ParsedQuicVersion& version = supported_versions[i];
if (std::find(available_versions.begin(), available_versions.end(),
version) != available_versions.end()) {
framer_.set_version(version);
return true;
}
}
return false;
}
void QuicConnection::OnError(QuicFramer* framer) {
// Packets that we can not or have not decrypted are dropped.
// TODO(rch): add stats to measure this.
if (!connected_ || last_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(default_path_.server_connection_id, packet.connection_id);
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
QUICHE_DCHECK(!version().HasIetfInvariantHeader());
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPublicResetPacket(packet);
}
std::string error_details = "Received public reset.";
if (perspective_ == Perspective::IS_CLIENT && !packet.endpoint_id.empty()) {
absl::StrAppend(&error_details, " From ", packet.endpoint_id, ".");
}
QUIC_DLOG(INFO) << ENDPOINT << error_details;
QUIC_CODE_COUNT(quic_tear_down_local_connection_on_public_reset);
TearDownLocalConnectionState(QUIC_PUBLIC_RESET, NO_IETF_QUIC_ERROR,
error_details, ConnectionCloseSource::FROM_PEER);
}
bool QuicConnection::OnProtocolVersionMismatch(
ParsedQuicVersion received_version) {
QUIC_DLOG(INFO) << ENDPOINT << "Received packet with mismatched version "
<< ParsedQuicVersionToString(received_version);
if (perspective_ == Perspective::IS_CLIENT) {
const std::string error_details = "Protocol version mismatch.";
QUIC_BUG(quic_bug_10511_3) << ENDPOINT << error_details;
CloseConnection(QUIC_INTERNAL_ERROR, error_details,
ConnectionCloseBehavior::SILENT_CLOSE);
}
// Server drops old packets that were sent by the client before the version
// was negotiated.
return false;
}
// Handles version negotiation for client connection.
void QuicConnection::OnVersionNegotiationPacket(
const QuicVersionNegotiationPacket& packet) {
// Check that any public reset packet with a different connection ID that was
// routed to this QuicConnection has been redirected before control reaches
// here. (Check for a bug regression.)
QUICHE_DCHECK_EQ(default_path_.server_connection_id, packet.connection_id);
if (perspective_ == Perspective::IS_SERVER) {
const std::string error_details =
"Server received version negotiation packet.";
QUIC_BUG(quic_bug_10511_4) << error_details;
QUIC_CODE_COUNT(quic_tear_down_local_connection_on_version_negotiation);
CloseConnection(QUIC_INTERNAL_ERROR, error_details,
ConnectionCloseBehavior::SILENT_CLOSE);
return;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnVersionNegotiationPacket(packet);
}
if (version_negotiated_) {
// Possibly a duplicate version negotiation packet.
return;
}
if (std::find(packet.versions.begin(), packet.versions.end(), version()) !=
packet.versions.end()) {
const std::string error_details = absl::StrCat(
"Server already supports client's version ",
ParsedQuicVersionToString(version()),
" and should have accepted the connection instead of sending {",
ParsedQuicVersionVectorToString(packet.versions), "}.");
QUIC_DLOG(WARNING) << error_details;
CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, error_details,
ConnectionCloseBehavior::SILENT_CLOSE);
return;
}
server_supported_versions_ = packet.versions;
CloseConnection(
QUIC_INVALID_VERSION,
absl::StrCat(
"Client may support one of the versions in the server's list, but "
"it's going to close the connection anyway. Supported versions: {",
ParsedQuicVersionVectorToString(framer_.supported_versions()),
"}, peer supported versions: {",
ParsedQuicVersionVectorToString(packet.versions), "}"),
ConnectionCloseBehavior::SILENT_CLOSE);
}
// Handles retry for client connection.
void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id,
QuicConnectionId new_connection_id,
absl::string_view retry_token,
absl::string_view retry_integrity_tag,
absl::string_view retry_without_tag) {
QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
if (version().UsesTls()) {
if (!CryptoUtils::ValidateRetryIntegrityTag(
version(), default_path_.server_connection_id, retry_without_tag,
retry_integrity_tag)) {
QUIC_DLOG(ERROR) << "Ignoring RETRY with invalid integrity tag";
return;
}
} else {
if (original_connection_id != default_path_.server_connection_id) {
QUIC_DLOG(ERROR) << "Ignoring RETRY with original connection ID "
<< original_connection_id << " not matching expected "
<< default_path_.server_connection_id << " token "
<< absl::BytesToHexString(retry_token);
return;
}
}
framer_.set_drop_incoming_retry_packets(true);
stats_.retry_packet_processed = true;
QUIC_DLOG(INFO) << "Received RETRY, replacing connection ID "
<< default_path_.server_connection_id << " with "
<< new_connection_id << ", received token "
<< absl::BytesToHexString(retry_token);
if (!original_destination_connection_id_.has_value()) {
original_destination_connection_id_ = default_path_.server_connection_id;
}
QUICHE_DCHECK(!retry_source_connection_id_.has_value())
<< retry_source_connection_id_.value();
retry_source_connection_id_ = new_connection_id;
ReplaceInitialServerConnectionId(new_connection_id);
packet_creator_.SetRetryToken(retry_token);
// Reinstall initial crypters because the connection ID changed.
InstallInitialCrypters(default_path_.server_connection_id);
sent_packet_manager_.MarkInitialPacketsForRetransmission();
}
void QuicConnection::SetOriginalDestinationConnectionId(
const QuicConnectionId& original_destination_connection_id) {
QUIC_DLOG(INFO) << "Setting original_destination_connection_id to "
<< original_destination_connection_id
<< " on connection with server_connection_id "
<< default_path_.server_connection_id;
QUICHE_DCHECK_NE(original_destination_connection_id,
default_path_.server_connection_id);
InstallInitialCrypters(original_destination_connection_id);
QUICHE_DCHECK(!original_destination_connection_id_.has_value())
<< original_destination_connection_id_.value();
original_destination_connection_id_ = original_destination_connection_id;
original_destination_connection_id_replacement_ =
default_path_.server_connection_id;
}
QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() {
if (original_destination_connection_id_.has_value()) {
return original_destination_connection_id_.value();
}
return default_path_.server_connection_id;
}
bool QuicConnection::ValidateServerConnectionId(
const QuicPacketHeader& header) const {
if (perspective_ == Perspective::IS_CLIENT &&
header.form == IETF_QUIC_SHORT_HEADER_PACKET) {
return true;
}
QuicConnectionId server_connection_id =
GetServerConnectionIdAsRecipient(header, perspective_);
if (server_connection_id == default_path_.server_connection_id ||
server_connection_id == original_destination_connection_id_) {
return true;
}
if (PacketCanReplaceServerConnectionId(header, perspective_)) {
QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID "
<< server_connection_id << " instead of "
<< default_path_.server_connection_id;
return true;
}
if (connection_migration_use_new_cid_ &&
perspective_ == Perspective::IS_SERVER &&
self_issued_cid_manager_ != nullptr &&
self_issued_cid_manager_->IsConnectionIdInUse(server_connection_id)) {
return true;
}
return false;
}
bool QuicConnection::OnUnauthenticatedPublicHeader(
const QuicPacketHeader& header) {
last_packet_destination_connection_id_ = header.destination_connection_id;
// If last packet destination connection ID is the original server
// connection ID chosen by client, replaces it with the connection ID chosen
// by server.
if (perspective_ == Perspective::IS_SERVER &&
original_destination_connection_id_.has_value() &&
last_packet_destination_connection_id_ ==
*original_destination_connection_id_) {
last_packet_destination_connection_id_ =
original_destination_connection_id_replacement_;
}
// As soon as we receive an initial we start ignoring subsequent retries.
if (header.version_flag && header.long_packet_type == INITIAL) {
framer_.set_drop_incoming_retry_packets(true);
}
if (!ValidateServerConnectionId(header)) {
++stats_.packets_dropped;
QuicConnectionId server_connection_id =
GetServerConnectionIdAsRecipient(header, perspective_);
QUIC_DLOG(INFO) << ENDPOINT
<< "Ignoring packet from unexpected server connection ID "
<< server_connection_id << " instead of "
<< default_path_.server_connection_id;
if (debug_visitor_ != nullptr) {
debug_visitor_->OnIncorrectConnectionId(server_connection_id);
}
// 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;
}
if (perspective_ == Perspective::IS_SERVER &&
header.form == IETF_QUIC_SHORT_HEADER_PACKET) {
return true;
}
QuicConnectionId client_connection_id =
GetClientConnectionIdAsRecipient(header, perspective_);
if (client_connection_id == default_path_.client_connection_id) {
return true;
}
if (!client_connection_id_is_set_ && perspective_ == Perspective::IS_SERVER) {
QUIC_DLOG(INFO) << ENDPOINT
<< "Setting client connection ID from first packet to "
<< client_connection_id;
set_client_connection_id(client_connection_id);
return true;
}
if (connection_migration_use_new_cid_ &&
perspective_ == Perspective::IS_CLIENT &&
self_issued_cid_manager_ != nullptr &&
self_issued_cid_manager_->IsConnectionIdInUse(client_connection_id)) {
return true;
}
++stats_.packets_dropped;
QUIC_DLOG(INFO) << ENDPOINT
<< "Ignoring packet from unexpected client connection ID "
<< client_connection_id << " instead of "
<< default_path_.client_connection_id;
return false;
}
bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnUnauthenticatedHeader(header);
}
// Sanity check on the server connection ID in header.
QUICHE_DCHECK(ValidateServerConnectionId(header));
if (packet_creator_.HasPendingFrames()) {
// Incoming packets may change a queued ACK frame.
const std::string error_details =
"Pending frames must be serialized before incoming packets are "
"processed.";
QUIC_BUG(quic_pending_frames_not_serialized)
<< error_details << ", received header: " << header;
CloseConnection(QUIC_INTERNAL_ERROR, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
return true;
}
void QuicConnection::OnSuccessfulVersionNegotiation() {
visitor_->OnSuccessfulVersionNegotiation(version());
if (debug_visitor_ != nullptr) {
debug_visitor_->OnSuccessfulVersionNegotiation(version());
}
}
void QuicConnection::OnSuccessfulMigration(bool is_port_change) {
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
if (IsPathDegrading()) {
// If path was previously degrading, and migration is successful after
// probing, restart the path degrading and blackhole detection.
OnForwardProgressMade();
}
if (IsAlternativePath(default_path_.self_address,
default_path_.peer_address)) {
// Reset alternative path state even if it is still under validation.
alternative_path_.Clear();
}
// TODO(b/159074035): notify SentPacketManger with RTT sample from probing.
if (version().HasIetfQuicFrames() && !is_port_change) {
sent_packet_manager_.OnConnectionMigration(/*reset_send_algorithm=*/true);
}
}
void QuicConnection::OnTransportParametersSent(
const TransportParameters& transport_parameters) const {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnTransportParametersSent(transport_parameters);
}
}
void QuicConnection::OnTransportParametersReceived(
const TransportParameters& transport_parameters) const {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnTransportParametersReceived(transport_parameters);
}
}
void QuicConnection::OnTransportParametersResumed(
const TransportParameters& transport_parameters) const {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnTransportParametersResumed(transport_parameters);
}
}
bool QuicConnection::HasPendingAcks() const {
return ack_alarm_->IsSet();
}
void QuicConnection::OnUserAgentIdKnown(const std::string& /*user_agent_id*/) {
sent_packet_manager_.OnUserAgentIdKnown();
}
void QuicConnection::OnDecryptedPacket(size_t /*length*/,
EncryptionLevel level) {
last_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(
last_received_packet_info_.receipt_time);
visitor_->OnPacketDecrypted(level);
}
QuicSocketAddress QuicConnection::GetEffectivePeerAddressFromCurrentPacket()
const {
// By default, the connection is not proxied, and the effective peer address
// is the packet's source address, i.e. the direct peer address.
return last_received_packet_info_.source_address;
}
bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPacketHeader(header, clock_->ApproximateNow(),
last_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_received_packet_info_.source_address);
default_path_.peer_address = GetEffectivePeerAddressFromCurrentPacket();
}
} else {
// At server, remember the address change type of effective_peer_address
// in current_effective_peer_migration_type_. But this variable alone
// doesn't necessarily starts a migration. A migration will be started
// later, once the current packet is confirmed to meet the following
// conditions:
// 1) current_effective_peer_migration_type_ is not NO_CHANGE.
// 2) The current packet is not a connectivity probing.
// 3) The current packet is not reordered, i.e. its packet number is the
// largest of this connection so far.
// Once the above conditions are confirmed, a new migration will start
// even if there is an active migration underway.
current_effective_peer_migration_type_ =
QuicUtils::DetermineAddressChangeType(
default_path_.peer_address,
GetEffectivePeerAddressFromCurrentPacket());
if (connection_migration_use_new_cid_) {
auto effective_peer_address = GetEffectivePeerAddressFromCurrentPacket();
// Since server does not send new connection ID to client before handshake
// completion and source connection ID is omitted in short header packet,
// the server_connection_id on PathState on the server side does not
// affect the packets server writes after handshake completion. On the
// other hand, it is still desirable to have the "correct" server
// connection ID set on path.
// 1) If client uses 1 unique server connection ID per path and the packet
// is received from an existing path, then
// last_packet_destination_connection_id_ will always be the same as the
// server connection ID on path. Server side will maintain the 1-to-1
// mapping from server connection ID to path.
// 2) If client uses multiple server connection IDs on the same path,
// compared to the server_connection_id on path,
// last_packet_destination_connection_id_ has the advantage that it is
// still present in the session map since the packet can be routed here
// regardless of packet reordering.
if (IsDefaultPath(last_received_packet_info_.destination_address,
effective_peer_address)) {
default_path_.server_connection_id =
last_packet_destination_connection_id_;
} else if (IsAlternativePath(
last_received_packet_info_.destination_address,
effective_peer_address)) {
alternative_path_.server_connection_id =
last_packet_destination_connection_id_;
}
}
if (last_packet_destination_connection_id_ !=
default_path_.server_connection_id &&
(!original_destination_connection_id_.has_value() ||
last_packet_destination_connection_id_ !=
*original_destination_connection_id_)) {
QUIC_CODE_COUNT(quic_connection_id_change);
}
QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE)
<< ENDPOINT << "Effective peer's ip:port changed from "
<< default_path_.peer_address.ToString() << " to "
<< GetEffectivePeerAddressFromCurrentPacket().ToString()
<< ", active_effective_peer_migration_type is "
<< active_effective_peer_migration_type_;
}
--stats_.packets_dropped;
QUIC_DVLOG(1) << ENDPOINT << "Received packet header: " << header;
last_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.
QuicTime receipt_time = idle_network_detector_.time_of_last_received_packet();
if (reset_per_packet_state_for_undecryptable_packets_ &&
SupportsMultiplePacketNumberSpaces()) {
QUIC_RELOADABLE_FLAG_COUNT_N(
quic_reset_per_packet_state_for_undecryptable_packets, 2, 2);
receipt_time = last_received_packet_info_.receipt_time;
}
uber_received_packet_manager_.RecordPacketReceived(
last_decrypted_packet_level_, last_header_, receipt_time);
if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() &&
!header.retry_token.empty() &&
visitor_->ValidateToken(header.retry_token)) {
QUIC_DLOG(INFO) << ENDPOINT << "Address validated via token.";
QUIC_CODE_COUNT(quic_address_validated_via_token);
default_path_.validated = true;
stats_.address_validated_via_token = true;
}
QUICHE_DCHECK(connected_);
return true;
}
bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_3, !connected_)
<< "Processing STREAM frame when connection is closed. 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(quic_peer_bug_10511_6)
<< 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;
}
// TODO(fayang): Consider moving UpdatePacketContent and
// MaybeUpdateAckTimeout to a stand-alone function instead of calling them for
// all frames.
MaybeUpdateAckTimeout();
visitor_->OnStreamFrame(frame);
stats_.stream_bytes_received += frame.data_length;
consecutive_retransmittable_on_wire_ping_count_ = 0;
return connected_;
}
bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_4, !connected_)
<< "Processing CRYPTO frame when connection is closed. 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(quic_bug_12714_5, !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(quic_bug_12714_6, !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(quic_bug_10511_7, !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(quic_bug_12714_7, !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(quic_bug_12714_8, !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(quic_bug_12714_9, !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(quic_bug_12714_10, !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(quic_bug_12714_11, !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(quic_bug_12714_12, !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;
MaybeUpdateAckTimeout();
visitor_->OnStopSendingFrame(frame);
return connected_;
}
class ReversePathValidationContext : public QuicPathValidationContext {
public:
ReversePathValidationContext(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicSocketAddress& effective_peer_address,
QuicConnection* connection)
: QuicPathValidationContext(self_address,
peer_address,
effective_peer_address),
connection_(connection) {}
QuicPacketWriter* WriterToUse() override { return connection_->writer(); }
private:
QuicConnection* connection_;
};
bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_8, !connected_)
<< "Processing PATH_CHALLENGE frame when connection "
"is closed. 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_response2, 2, 5);
// Only respond to the 1st PATH_CHALLENGE in the packet.
return true;
}
if (!validate_client_addresses_) {
return OnPathChallengeFrameInternal(frame);
}
QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 1, 6);
{
// TODO(danzh) inline OnPathChallengeFrameInternal() once
// validate_client_addresses_ is deprecated.
if (!OnPathChallengeFrameInternal(frame)) {
return false;
}
}
return connected_;
}
bool QuicConnection::OnPathChallengeFrameInternal(
const QuicPathChallengeFrame& frame) {
should_proactively_validate_peer_address_on_path_challenge_ = false;
// UpdatePacketContent() may start reverse path validation.
if (!UpdatePacketContent(PATH_CHALLENGE_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPathChallengeFrame(frame);
}
const QuicSocketAddress current_effective_peer_address =
GetEffectivePeerAddressFromCurrentPacket();
QuicConnectionId client_cid, server_cid;
FindOnPathConnectionIds(last_received_packet_info_.destination_address,
current_effective_peer_address, &client_cid,
&server_cid);
QuicPacketCreator::ScopedPeerAddressContext context(
&packet_creator_, last_received_packet_info_.source_address, client_cid,
server_cid, connection_migration_use_new_cid_);
if (should_proactively_validate_peer_address_on_path_challenge_) {
// Conditions to proactively validate peer address:
// The perspective is server
// The PATH_CHALLENGE is received on an unvalidated alternative path.
// The connection isn't validating migrated peer address, which is of
// higher prority.
QUIC_DVLOG(1) << "Proactively validate the effective peer address "
<< current_effective_peer_address;
QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 2, 6);
ValidatePath(std::make_unique<ReversePathValidationContext>(
default_path_.self_address,
last_received_packet_info_.source_address,
current_effective_peer_address, this),
std::make_unique<ReversePathValidationResultDelegate>(
this, peer_address()));
}
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_response2, 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.
const bool success = SendPathResponse(
frame.data_buffer, last_received_packet_info_.source_address,
current_effective_peer_address);
if (GetQuicReloadableFlag(quic_drop_unsent_path_response)) {
QUIC_RELOADABLE_FLAG_COUNT(quic_drop_unsent_path_response);
}
if (!success) {
QUIC_CODE_COUNT(quic_failed_to_send_path_response);
if (!GetQuicReloadableFlag(quic_drop_unsent_path_response)) {
// Queue the payloads to re-try later.
pending_path_challenge_payloads_.push_back(
{frame.data_buffer, last_received_packet_info_.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(quic_bug_10511_9, !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_) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 1, 4);
path_validator_.OnPathResponse(
frame.data_buffer, last_received_packet_info_.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(quic_bug_10511_10, !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: "
<< QuicIetfTransportErrorCodeString(
static_cast<QuicIetfTransportErrorCodes>(
frame.wire_error_code))
<< ", error frame type: "
<< frame.transport_close_frame_type;
break;
case IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
QUIC_DLOG(INFO) << ENDPOINT
<< "Received Application ConnectionClose for connection: "
<< connection_id() << ", with error: "
<< QuicErrorCodeToString(frame.quic_error_code) << " ("
<< frame.error_details << ")"
<< ", application error code: " << frame.wire_error_code;
break;
}
if (frame.quic_error_code == QUIC_BAD_MULTIPATH_FLAG) {
QUIC_LOG_FIRST_N(ERROR, 10) << "Unexpected QUIC_BAD_MULTIPATH_FLAG error."
<< " last_received_header: " << last_header_
<< " encryption_level: " << encryption_level_;
}
TearDownLocalConnectionState(frame, ConnectionCloseSource::FROM_PEER);
return connected_;
}
bool QuicConnection::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_13, !connected_)
<< "Processing MAX_STREAMS frame when connection is closed. Last frame: "
<< most_recent_frame_type_;
if (!UpdatePacketContent(MAX_STREAMS_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnMaxStreamsFrame(frame);
}
MaybeUpdateAckTimeout();
return visitor_->OnMaxStreamsFrame(frame) && connected_;
}
bool QuicConnection::OnStreamsBlockedFrame(
const QuicStreamsBlockedFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_11, !connected_)
<< "Processing STREAMS_BLOCKED frame when "
"connection is closed. Last frame: "
<< most_recent_frame_type_;
if (!UpdatePacketContent(STREAMS_BLOCKED_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnStreamsBlockedFrame(frame);
}
MaybeUpdateAckTimeout();
return visitor_->OnStreamsBlockedFrame(frame) && connected_;
}
bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_14, !connected_)
<< "Processing GOAWAY frame when connection is closed. 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(quic_bug_10511_12, !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_;
}
void QuicConnection::OnClientConnectionIdAvailable() {
QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
if (!peer_issued_cid_manager_->HasUnusedConnectionId()) {
return;
}
if (default_path_.client_connection_id.IsEmpty()) {
// Count client connection ID patched onto the default path.
QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 3,
6);
const QuicConnectionIdData* unused_cid_data =
peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID "
<< unused_cid_data->connection_id << " to default path";
default_path_.client_connection_id = unused_cid_data->connection_id;
default_path_.stateless_reset_token =
unused_cid_data->stateless_reset_token;
QUICHE_DCHECK(!packet_creator_.HasPendingFrames());
QUICHE_DCHECK(packet_creator_.GetDestinationConnectionId().IsEmpty());
packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
return;
}
if (alternative_path_.peer_address.IsInitialized() &&
alternative_path_.client_connection_id.IsEmpty()) {
// Count client connection ID patched onto the alternative path.
QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 4,
6);
const QuicConnectionIdData* unused_cid_data =
peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
QUIC_DVLOG(1) << ENDPOINT << "Patch connection ID "
<< unused_cid_data->connection_id << " to alternative path";
alternative_path_.client_connection_id = unused_cid_data->connection_id;
alternative_path_.stateless_reset_token =
unused_cid_data->stateless_reset_token;
}
}
bool QuicConnection::ShouldSetRetransmissionAlarmOnPacketSent(
bool in_flight, EncryptionLevel level) const {
if (!retransmission_alarm_->IsSet()) {
return true;
}
if (!in_flight) {
return false;
}
if (!SupportsMultiplePacketNumberSpaces()) {
return true;
}
// Before handshake gets confirmed, do not re-arm PTO timer on application
// data. Think about this scenario: on the client side, the CHLO gets
// acknowledged and the SHLO is not received yet. The PTO alarm is set when
// the CHLO acknowledge is received (and there is no in flight INITIAL
// packet). Re-arming PTO alarm on 0-RTT packet would keep postponing the PTO
// alarm.
return IsHandshakeConfirmed() || level == ENCRYPTION_INITIAL ||
level == ENCRYPTION_HANDSHAKE;
}
bool QuicConnection::OnNewConnectionIdFrameInner(
const QuicNewConnectionIdFrame& frame) {
QUICHE_DCHECK(support_multiple_connection_ids_);
if (peer_issued_cid_manager_ == nullptr) {
CloseConnection(
IETF_QUIC_PROTOCOL_VIOLATION,
"Receives NEW_CONNECTION_ID while peer uses zero length connection ID",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
std::string error_detail;
QuicErrorCode error =
peer_issued_cid_manager_->OnNewConnectionIdFrame(frame, &error_detail);
if (error != QUIC_NO_ERROR) {
CloseConnection(error, error_detail,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
if (perspective_ == Perspective::IS_SERVER) {
OnClientConnectionIdAvailable();
}
MaybeUpdateAckTimeout();
return true;
}
bool QuicConnection::OnNewConnectionIdFrame(
const QuicNewConnectionIdFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_13, !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);
}
if (!support_multiple_connection_ids_) {
return true;
}
return OnNewConnectionIdFrameInner(frame);
}
bool QuicConnection::OnRetireConnectionIdFrame(
const QuicRetireConnectionIdFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_14, !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);
}
if (!connection_migration_use_new_cid_) {
// Do not respond to RetireConnectionId frame.
return true;
}
if (!support_multiple_connection_ids_) {
return true;
}
if (self_issued_cid_manager_ == nullptr) {
CloseConnection(
IETF_QUIC_PROTOCOL_VIOLATION,
"Receives RETIRE_CONNECTION_ID while new connection ID is never issued",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
std::string error_detail;
QuicErrorCode error = self_issued_cid_manager_->OnRetireConnectionIdFrame(
frame, sent_packet_manager_.GetPtoDelay(), &error_detail);
if (error != QUIC_NO_ERROR) {
CloseConnection(error, error_detail,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// Count successfully received RETIRE_CONNECTION_ID frames.
QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid_v2, 5, 6);
MaybeUpdateAckTimeout();
return true;
}
bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_15, !connected_)
<< "Processing NEW_TOKEN frame when connection is closed. Last frame: "
<< most_recent_frame_type_;
if (!UpdatePacketContent(NEW_TOKEN_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnNewTokenFrame(frame);
}
if (perspective_ == Perspective::IS_SERVER) {
CloseConnection(QUIC_INVALID_NEW_TOKEN, "Server received new token frame.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// NEW_TOKEN frame should insitgate ACKs.
MaybeUpdateAckTimeout();
visitor_->OnNewTokenReceived(frame.token);
return true;
}
bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_16, !connected_)
<< "Processing MESSAGE frame when connection is closed. 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(quic_bug_10511_15, !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(quic_bug_10511_16, !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(quic_bug_12714_17, !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_received_packet_info_.destination_address,
last_received_packet_info_.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_received_packet_info_.source_address,
/* is_response=*/true);
return;
}
} else {
if (IsCurrentPacketConnectivityProbing()) {
visitor_->OnPacketReceived(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address,
/*is_connectivity_probe=*/true);
return;
}
if (perspective_ == Perspective::IS_CLIENT) {
// This node is a client, notify that a speculative connectivity probing
// packet has been received anyway.
QUIC_DVLOG(1)
<< ENDPOINT
<< "Received a speculative connectivity probing packet for "
<< GetServerConnectionIdAsRecipient(last_header_, perspective_)
<< " from ip:port: "
<< last_received_packet_info_.source_address.ToString()
<< " to ip:port: "
<< last_received_packet_info_.destination_address.ToString();
visitor_->OnPacketReceived(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address,
/*is_connectivity_probe=*/false);
return;
}
}
}
bool QuicConnection::IsValidStatelessResetToken(
const StatelessResetToken& token) const {
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
return default_path_.stateless_reset_token.has_value() &&
QuicUtils::AreStatelessResetTokensEqual(
token, *default_path_.stateless_reset_token);
}
void QuicConnection::OnAuthenticatedIetfStatelessResetPacket(
const QuicIetfStatelessResetPacket& /*packet*/) {
// TODO(fayang): Add OnAuthenticatedIetfStatelessResetPacket to
// debug_visitor_.
QUICHE_DCHECK(version().HasIetfInvariantHeader());
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
if (use_path_validator_) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_pass_path_response_to_validator, 4, 4);
if (!IsDefaultPath(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address)) {
// This packet is received on a probing path. Do not close connection.
if (IsAlternativePath(last_received_packet_info_.destination_address,
GetEffectivePeerAddressFromCurrentPacket())) {
QUIC_BUG_IF(quic_bug_12714_18, alternative_path_.validated)
<< "STATELESS_RESET received on alternate path after it's "
"validated.";
path_validator_.CancelPathValidation();
} else {
QUIC_BUG(quic_bug_10511_17)
<< "Received Stateless Reset on unknown socket.";
}
return;
}
} else if (!visitor_->ValidateStatelessReset(
last_received_packet_info_.destination_address,
last_received_packet_info_.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() {
// This occurs if we don't discard old packets we've seen fast enough. It's
// possible largest observed is less than leaset unacked.
const bool should_close =
sent_packet_manager_.GetLargestSentPacket().IsInitialized() &&
sent_packet_manager_.GetLargestSentPacket() >
sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_;
if (should_close) {
CloseConnection(
QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS,
absl::StrCat("More than ", max_tracked_packets_,
" outstanding, least_unacked: ",
sent_packet_manager_.GetLeastUnacked().ToUint64(),
", packets_processed: ", stats_.packets_processed,
", last_decrypted_packet_level: ",
EncryptionLevelToString(last_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 {
WriteIfNotBlocked();
}
}
void QuicConnection::MaybeActivateLegacyVersionEncapsulation() {
if (!legacy_version_encapsulation_enabled_) {
return;
}
QUICHE_DCHECK(!legacy_version_encapsulation_in_progress_);
QUIC_BUG_IF(quic_bug_12714_19, !packet_creator_.CanSetMaxPacketLength())
<< "Cannot activate Legacy Version Encapsulation mid-packet";
QUIC_BUG_IF(quic_bug_12714_20, coalesced_packet_.length() != 0u)
<< "Cannot activate Legacy Version Encapsulation mid-coalesced-packet";
legacy_version_encapsulation_in_progress_ = true;
MaybeUpdatePacketCreatorMaxPacketLengthAndPadding();
}
void QuicConnection::MaybeDisactivateLegacyVersionEncapsulation() {
if (!legacy_version_encapsulation_in_progress_) {
return;
}
// Flush any remaining packet before disactivating encapsulation.
packet_creator_.FlushCurrentPacket();
QUICHE_DCHECK(legacy_version_encapsulation_enabled_);
legacy_version_encapsulation_in_progress_ = false;
MaybeUpdatePacketCreatorMaxPacketLengthAndPadding();
}
size_t QuicConnection::SendCryptoData(EncryptionLevel level,
size_t write_length,
QuicStreamOffset offset) {
if (write_length == 0) {
QUIC_BUG(quic_bug_10511_18) << "Attempt to send empty crypto frame";
return 0;
}
if (level == ENCRYPTION_INITIAL) {
MaybeActivateLegacyVersionEncapsulation();
}
size_t consumed_length;
{
ScopedPacketFlusher flusher(this);
consumed_length =
packet_creator_.ConsumeCryptoData(level, write_length, offset);
} // Added scope ensures packets are flushed before continuing.
MaybeDisactivateLegacyVersionEncapsulation();
return consumed_length;
}
QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
size_t write_length,
QuicStreamOffset offset,
StreamSendingState state) {
if (state == NO_FIN && write_length == 0) {
QUIC_BUG(quic_bug_10511_19) << "Attempt to send empty stream frame";
return QuicConsumedData(0, false);
}
if (packet_creator_.encryption_level() == ENCRYPTION_INITIAL &&
QuicUtils::IsCryptoStreamId(transport_version(), id)) {
MaybeActivateLegacyVersionEncapsulation();
}
if (perspective_ == Perspective::IS_SERVER &&
version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) {
if (in_on_retransmission_time_out_ &&
coalesced_packet_.NumberOfPackets() == 0u) {
// PTO fires while handshake is not confirmed. Do not preempt handshake
// data with stream data.
QUIC_CODE_COUNT(quic_try_to_send_half_rtt_data_when_pto_fires);
return QuicConsumedData(0, false);
}
if (coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) &&
coalesced_packet_.NumberOfPackets() == 1u) {
// Handshake is not confirmed yet, if there is only an initial packet in
// the coalescer, try to bundle an ENCRYPTION_HANDSHAKE packet before
// sending stream data.
sent_packet_manager_.RetransmitDataOfSpaceIfAny(HANDSHAKE_DATA);
}
}
QuicConsumedData consumed_data(0, false);
{
// Opportunistically bundle an ack with every outgoing packet.
// Particularly, we want to bundle with handshake packets since we don't
// know which decrypter will be used on an ack packet following a handshake
// packet (a handshake packet from client to server could result in a REJ or
// a SHLO from the server, leading to two different decrypters at the
// server.)
ScopedPacketFlusher flusher(this);
consumed_data =
packet_creator_.ConsumeData(id, write_length, offset, state);
} // Added scope ensures packets are flushed before continuing.
MaybeDisactivateLegacyVersionEncapsulation();
return consumed_data;
}
bool QuicConnection::SendControlFrame(const QuicFrame& frame) {
if (SupportsMultiplePacketNumberSpaces() &&
(encryption_level_ == ENCRYPTION_INITIAL ||
encryption_level_ == ENCRYPTION_HANDSHAKE) &&
frame.type != PING_FRAME) {
// Allow PING frame to be sent without APPLICATION key. For example, when
// anti-amplification limit is used, client needs to send something to avoid
// handshake deadlock.
QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame
<< " at encryption level: " << encryption_level_;
return false;
}
ScopedPacketFlusher flusher(this);
const bool consumed =
packet_creator_.ConsumeRetransmittableControlFrame(frame);
if (!consumed) {
QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame;
return false;
}
if (frame.type == PING_FRAME) {
// Flush PING frame immediately.
packet_creator_.FlushCurrentPacket();
stats_.ping_frames_sent++;
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPingSent();
}
}
if (frame.type == BLOCKED_FRAME) {
stats_.blocked_frames_sent++;
}
return true;
}
void QuicConnection::OnStreamReset(QuicStreamId id,
QuicRstStreamErrorCode error) {
if (error == QUIC_STREAM_NO_ERROR) {
// All data for streams which are reset with QUIC_STREAM_NO_ERROR must
// be received by the peer.
return;
}
// Flush stream frames of reset stream.
if (packet_creator_.HasPendingStreamFramesOfStream(id)) {
ScopedPacketFlusher flusher(this);
packet_creator_.FlushCurrentPacket();
}
// TODO(ianswett): Consider checking for 3 RTOs when the last stream is
// cancelled as well.
}
const QuicConnectionStats& QuicConnection::GetStats() {
const RttStats* rtt_stats = sent_packet_manager_.GetRttStats();
// Update rtt and estimated bandwidth.
QuicTime::Delta min_rtt = rtt_stats->min_rtt();
if (min_rtt.IsZero()) {
// If min RTT has not been set, use initial RTT instead.
min_rtt = rtt_stats->initial_rtt();
}
stats_.min_rtt_us = min_rtt.ToMicroseconds();
QuicTime::Delta srtt = rtt_stats->SmoothedOrInitialRtt();
stats_.srtt_us = srtt.ToMicroseconds();
stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate();
sent_packet_manager_.GetSendAlgorithm()->PopulateConnectionStats(&stats_);
stats_.egress_mtu = long_term_mtu_;
stats_.ingress_mtu = largest_received_packet_size_;
return stats_;
}
void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) {
QueueCoalescedPacket(packet);
}
void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet,
EncryptionLevel decryption_level,
bool has_decryption_key) {
QUIC_DVLOG(1) << ENDPOINT << "Received undecryptable packet of length "
<< packet.length() << " with"
<< (has_decryption_key ? "" : "out") << " key at level "
<< decryption_level
<< " while connection is at encryption level "
<< encryption_level_;
QUICHE_DCHECK(EncryptionLevelIsValid(decryption_level));
if (encryption_level_ != ENCRYPTION_FORWARD_SECURE) {
++stats_.undecryptable_packets_received_before_handshake_complete;
}
const bool should_enqueue =
ShouldEnqueueUnDecryptablePacket(decryption_level, has_decryption_key);
if (should_enqueue) {
QueueUndecryptablePacket(packet, decryption_level);
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnUndecryptablePacket(decryption_level,
/*dropped=*/!should_enqueue);
}
if (has_decryption_key) {
stats_.num_failed_authentication_packets_received++;
if (version().UsesTls()) {
// Should always be non-null if has_decryption_key is true.
QUICHE_DCHECK(framer_.GetDecrypter(decryption_level));
const QuicPacketCount integrity_limit =
framer_.GetDecrypter(decryption_level)->GetIntegrityLimit();
QUIC_DVLOG(2) << ENDPOINT << "Checking AEAD integrity limits:"
<< " num_failed_authentication_packets_received="
<< stats_.num_failed_authentication_packets_received
<< " integrity_limit=" << integrity_limit;
if (stats_.num_failed_authentication_packets_received >=
integrity_limit) {
const std::string error_details = absl::StrCat(
"decrypter integrity limit reached:"
" num_failed_authentication_packets_received=",
stats_.num_failed_authentication_packets_received,
" integrity_limit=", integrity_limit);
CloseConnection(QUIC_AEAD_LIMIT_REACHED, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
}
}
if (version().UsesTls() && perspective_ == Perspective::IS_SERVER &&
decryption_level == ENCRYPTION_ZERO_RTT && !has_decryption_key &&
had_zero_rtt_decrypter_) {
QUIC_CODE_COUNT_N(
quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 1,
3);
stats_
.num_tls_server_zero_rtt_packets_received_after_discarding_decrypter++;
}
}
bool QuicConnection::ShouldEnqueueUnDecryptablePacket(
EncryptionLevel decryption_level, bool has_decryption_key) const {
if (has_decryption_key) {
// We already have the key for this decryption level, therefore no
// future keys will allow it be decrypted.
return false;
}
if (IsHandshakeComplete()) {
// We do not expect to install any further keys.
return false;
}
if (undecryptable_packets_.size() >= max_undecryptable_packets_) {
// We do not queue more than max_undecryptable_packets_ packets.
return false;
}
if (version().KnowsWhichDecrypterToUse() &&
decryption_level == ENCRYPTION_INITIAL) {
// When the corresponding decryption key is not available, all
// non-Initial packets should be buffered until the handshake is complete.
return false;
}
if (perspective_ == Perspective::IS_CLIENT && version().UsesTls() &&
decryption_level == ENCRYPTION_ZERO_RTT) {
// Only clients send Zero RTT packets in IETF QUIC.
QUIC_PEER_BUG(quic_peer_bug_client_received_zero_rtt)
<< "Client received a Zero RTT packet, not buffering.";
return false;
}
return true;
}
std::string QuicConnection::UndecryptablePacketsInfo() const {
std::string info = absl::StrCat(
"num_undecryptable_packets: ", undecryptable_packets_.size(), " {");
for (const auto& packet : undecryptable_packets_) {
absl::StrAppend(&info, "[",
EncryptionLevelToString(packet.encryption_level), ", ",
packet.packet->length(), "]");
}
absl::StrAppend(&info, "}");
return info;
}
void QuicConnection::MaybeUpdatePacketCreatorMaxPacketLengthAndPadding() {
QuicByteCount max_packet_length = GetLimitedMaxPacketSize(long_term_mtu_);
if (legacy_version_encapsulation_in_progress_) {
QUICHE_DCHECK(legacy_version_encapsulation_enabled_);
const QuicByteCount minimum_overhead =
QuicLegacyVersionEncapsulator::GetMinimumOverhead(
legacy_version_encapsulation_sni_);
if (max_packet_length < minimum_overhead) {
QUIC_BUG(quic_bug_10511_20)
<< "Cannot apply Legacy Version Encapsulation overhead because "
<< "max_packet_length " << max_packet_length << " < minimum_overhead "
<< minimum_overhead;
legacy_version_encapsulation_in_progress_ = false;
legacy_version_encapsulation_enabled_ = false;
MaybeUpdatePacketCreatorMaxPacketLengthAndPadding();
return;
}
max_packet_length -= minimum_overhead;
}
packet_creator_.SetMaxPacketLength(max_packet_length);
}
void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
if (!connected_) {
return;
}
QUIC_DVLOG(2) << ENDPOINT << "Received encrypted " << packet.length()
<< " bytes:" << std::endl
<< quiche::QuicheTextUtils::HexDump(
absl::string_view(packet.data(), packet.length()));
QUIC_BUG_IF(quic_bug_12714_21, current_packet_data_ != nullptr)
<< "ProcessUdpPacket must not be called while processing a packet.";
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPacketReceived(self_address, peer_address, packet);
}
last_received_packet_info_ =
ReceivedPacketInfo(self_address, peer_address, packet.receipt_time());
last_size_ = packet.length();
current_packet_data_ = packet.data();
if (!default_path_.self_address.IsInitialized()) {
default_path_.self_address = last_received_packet_info_.destination_address;
}
if (!direct_peer_address_.IsInitialized()) {
UpdatePeerAddress(last_received_packet_info_.source_address);
}
if (!default_path_.peer_address.IsInitialized()) {
const QuicSocketAddress effective_peer_addr =
GetEffectivePeerAddressFromCurrentPacket();
// The default path peer_address must be initialized at the beginning of the
// first packet processed(here). If effective_peer_addr is uninitialized,
// just set effective_peer_address_ to the direct peer address.
default_path_.peer_address = effective_peer_addr.IsInitialized()
? effective_peer_addr
: direct_peer_address_;
}
stats_.bytes_received += packet.length();
++stats_.packets_received;
if (!count_bytes_on_alternative_path_separately_) {
if (EnforceAntiAmplificationLimit()) {
default_path_.bytes_received_before_address_validation += last_size_;
}
} else if (IsDefaultPath(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address) &&
EnforceAntiAmplificationLimit()) {
QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 1, 5);
last_received_packet_info_.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(quic_bug_10511_21)
<< "Packet receipt time:" << packet.receipt_time().ToDebuggingValue()
<< " too far from current time:"
<< clock_->ApproximateNow().ToDebuggingValue();
}
QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: "
<< packet.receipt_time().ToDebuggingValue() << " from peer "
<< last_received_packet_info_.source_address;
ScopedPacketFlusher flusher(this);
if (!framer_.ProcessPacket(packet)) {
// If we are unable to decrypt this packet, it might be
// because the CHLO or SHLO packet was lost.
QUIC_DVLOG(1) << ENDPOINT
<< "Unable to process packet. Last packet processed: "
<< last_header_.packet_number;
current_packet_data_ = nullptr;
is_current_packet_connectivity_probing_ = false;
MaybeProcessCoalescedPackets();
return;
}
++stats_.packets_processed;
QUIC_DLOG_IF(INFO, active_effective_peer_migration_type_ != NO_CHANGE)
<< "sent_packet_manager_.GetLargestObserved() = "
<< sent_packet_manager_.GetLargestObserved()
<< ", highest_packet_sent_before_effective_peer_migration_ = "
<< highest_packet_sent_before_effective_peer_migration_;
if (!validate_client_addresses_ &&
active_effective_peer_migration_type_ != NO_CHANGE &&
sent_packet_manager_.GetLargestObserved().IsInitialized() &&
(!highest_packet_sent_before_effective_peer_migration_.IsInitialized() ||
sent_packet_manager_.GetLargestObserved() >
highest_packet_sent_before_effective_peer_migration_)) {
if (perspective_ == Perspective::IS_SERVER) {
OnEffectivePeerMigrationValidated();
}
}
if (!MaybeProcessCoalescedPackets()) {
MaybeProcessUndecryptablePackets();
MaybeSendInResponseToPacket();
}
SetPingAlarm();
RetirePeerIssuedConnectionIdsNoLongerOnPath();
current_packet_data_ = nullptr;
is_current_packet_connectivity_probing_ = false;
}
void QuicConnection::OnBlockedWriterCanWrite() {
writer_->SetWritable();
OnCanWrite();
}
void QuicConnection::OnCanWrite() {
if (!connected_) {
return;
}
if (writer_->IsWriteBlocked()) {
const std::string error_details =
"Writer is blocked while calling OnCanWrite.";
QUIC_BUG(quic_bug_10511_22) << ENDPOINT << error_details;
CloseConnection(QUIC_INTERNAL_ERROR, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return;
}
ScopedPacketFlusher flusher(this);
WriteQueuedPackets();
const QuicTime ack_timeout =
uber_received_packet_manager_.GetEarliestAckTimeout();
if (ack_timeout.IsInitialized() && ack_timeout <= clock_->ApproximateNow()) {
// Send an ACK now because either 1) we were write blocked when we last
// tried to send an ACK, or 2) both ack alarm and send alarm were set to
// go off together.
if (SupportsMultiplePacketNumberSpaces()) {
SendAllPendingAcks();
} else {
SendAck();
}
}
// 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_response2, 4, 5);
const PendingPathChallenge& pending_path_challenge =
pending_path_challenge_payloads_.front();
// Note connection_migration_use_cid_ will depends on
// quic_drop_unsent_path_response flag eventually, and hence the empty
// effective_peer_address here will not be used.
if (!SendPathResponse(pending_path_challenge.received_path_challenge,
pending_path_challenge.peer_address,
/*effective_peer_address=*/QuicSocketAddress())) {
break;
}
pending_path_challenge_payloads_.pop_front();
}
// Sending queued packets may have caused the socket to become write blocked,
// or the congestion manager to prohibit sending.
if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
return;
}
// Tell the session it can write.
visitor_->OnCanWrite();
// After the visitor writes, it may have caused the socket to become write
// blocked or the congestion manager to prohibit sending, so check again.
if (visitor_->WillingAndAbleToWrite() && !send_alarm_->IsSet() &&
CanWrite(HAS_RETRANSMITTABLE_DATA)) {
// We're not write blocked, but some data wasn't written. Register for
// 'immediate' resumption so we'll keep writing after other connections.
send_alarm_->Set(clock_->ApproximateNow());
}
}
void QuicConnection::WriteIfNotBlocked() {
if (framer().is_processing_packet()) {
QUIC_BUG(connection_write_mid_packet_processing)
<< ENDPOINT << "Tried to write in mid of packet processing";
return;
}
if (!HandleWriteBlocked()) {
OnCanWrite();
}
}
void QuicConnection::MaybeClearQueuedPacketsOnPathChange() {
if (connection_migration_use_new_cid_ &&
peer_issued_cid_manager_ != nullptr && HasQueuedPackets()) {
// Discard packets serialized with the connection ID on the old code path.
// It is possible to clear queued packets only if connection ID changes.
// However, the case where connection ID is unchanged and queued packets are
// non-empty is quite rare.
ClearQueuedPackets();
}
}
void QuicConnection::ReplaceInitialServerConnectionId(
const QuicConnectionId& new_server_connection_id) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
if (support_multiple_connection_ids_) {
if (new_server_connection_id.IsEmpty()) {
peer_issued_cid_manager_ = nullptr;
} else {
if (peer_issued_cid_manager_ != nullptr) {
QUIC_BUG_IF(quic_bug_12714_22,
!peer_issued_cid_manager_->IsConnectionIdActive(
default_path_.server_connection_id))
<< "Connection ID replaced header is no longer active. old id: "
<< default_path_.server_connection_id
<< " new_id: " << new_server_connection_id;
peer_issued_cid_manager_->ReplaceConnectionId(
default_path_.server_connection_id, new_server_connection_id);
} else {
peer_issued_cid_manager_ =
std::make_unique<QuicPeerIssuedConnectionIdManager>(
kMinNumOfActiveConnectionIds, new_server_connection_id, clock_,
alarm_factory_, this, context());
}
}
}
default_path_.