blob: 9be867eb21da63e2da107facd4bdc456be2766f9 [file] [log] [blame] [edit]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/quic/core/quic_connection.h"
#include <string.h>
#include <sys/types.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <memory>
#include <optional>
#include <ostream>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "quiche/quic/core/congestion_control/rtt_stats.h"
#include "quiche/quic/core/congestion_control/send_algorithm_interface.h"
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/crypto/crypto_utils.h"
#include "quiche/quic/core/crypto/quic_decrypter.h"
#include "quiche/quic/core/crypto/quic_encrypter.h"
#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_bandwidth.h"
#include "quiche/quic/core/quic_config.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_packet_creator.h"
#include "quiche/quic/core/quic_packet_writer.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_path_validator.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/quic/platform/api/quic_client_stats.h"
#include "quiche/quic/platform/api/quic_exported_stats.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/common/platform/api/quiche_flag_utils.h"
#include "quiche/common/platform/api/quiche_testvalue.h"
#include "quiche/common/quiche_text_utils.h"
namespace quic {
class QuicDecrypter;
class QuicEncrypter;
namespace {
// Maximum number of consecutive sent nonretransmittable packets.
const QuicPacketCount kMaxConsecutiveNonRetransmittablePackets = 19;
// The minimum release time into future in ms.
const int kMinReleaseTimeIntoFutureMs = 1;
// The maximum number of recorded client addresses.
const size_t kMaxReceivedClientAddressSize = 20;
// An arbitrary limit on the number of PTOs before giving up on ECN, if no ECN-
// marked packet is acked. Avoids abandoning ECN because of one burst loss,
// but doesn't allow multiple RTTs of user delay in the hope of using ECN.
const uint8_t kEcnPtoLimit = 2;
// When the clearer goes out of scope, the coalesced packet gets cleared.
class ScopedCoalescedPacketClearer {
public:
explicit ScopedCoalescedPacketClearer(QuicCoalescedPacket* coalesced)
: coalesced_(coalesced) {}
~ScopedCoalescedPacketClearer() { coalesced_->Clear(); }
private:
QuicCoalescedPacket* coalesced_; // Unowned.
};
// Whether this incoming packet is allowed to replace our connection ID.
bool PacketCanReplaceServerConnectionId(const QuicPacketHeader& header,
Perspective perspective) {
return perspective == Perspective::IS_CLIENT &&
header.form == IETF_QUIC_LONG_HEADER_PACKET &&
header.version.IsKnown() &&
header.version.AllowsVariableLengthConnectionIds() &&
(header.long_packet_type == INITIAL ||
header.long_packet_type == RETRY);
}
// Due to a lost Initial packet, a Handshake packet might use a new connection
// ID we haven't seen before. We shouldn't update the connection ID based on
// this, but should buffer the packet in case it works out.
bool NewServerConnectionIdMightBeValid(const QuicPacketHeader& header,
Perspective perspective,
bool connection_id_already_replaced) {
return perspective == Perspective::IS_CLIENT &&
header.form == IETF_QUIC_LONG_HEADER_PACKET &&
header.version.IsKnown() &&
header.version.AllowsVariableLengthConnectionIds() &&
header.long_packet_type == HANDSHAKE &&
!connection_id_already_replaced;
}
CongestionControlType GetDefaultCongestionControlType() {
if (GetQuicReloadableFlag(quic_default_to_bbr_v2)) {
return kBBRv2;
}
if (GetQuicReloadableFlag(quic_default_to_bbr)) {
return kBBR;
}
return kCubicBytes;
}
bool ContainsNonProbingFrame(const SerializedPacket& packet) {
for (const QuicFrame& frame : packet.nonretransmittable_frames) {
if (!QuicUtils::IsProbingFrame(frame.type)) {
return true;
}
}
for (const QuicFrame& frame : packet.retransmittable_frames) {
if (!QuicUtils::IsProbingFrame(frame.type)) {
return true;
}
}
return false;
}
} // namespace
#define ENDPOINT \
(perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
QuicConnection::QuicConnection(
QuicConnectionId server_connection_id,
QuicSocketAddress initial_self_address,
QuicSocketAddress initial_peer_address,
QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
QuicPacketWriter* writer, bool owns_writer, Perspective perspective,
const ParsedQuicVersionVector& supported_versions,
ConnectionIdGeneratorInterface& generator)
: framer_(supported_versions, helper->GetClock()->ApproximateNow(),
perspective, server_connection_id.length()),
current_packet_content_(NO_FRAMES_RECEIVED),
is_current_packet_connectivity_probing_(false),
has_path_challenge_in_current_packet_(false),
current_effective_peer_migration_type_(NO_CHANGE),
helper_(helper),
alarm_factory_(alarm_factory),
per_packet_options_(nullptr),
writer_(writer),
owns_writer_(owns_writer),
encryption_level_(ENCRYPTION_INITIAL),
clock_(helper->GetClock()),
random_generator_(helper->GetRandomGenerator()),
client_connection_id_is_set_(false),
direct_peer_address_(initial_peer_address),
default_path_(initial_self_address, QuicSocketAddress(),
/*client_connection_id=*/EmptyQuicConnectionId(),
server_connection_id,
/*stateless_reset_token=*/std::nullopt),
active_effective_peer_migration_type_(NO_CHANGE),
support_key_update_for_connection_(false),
current_packet_data_(nullptr),
should_last_packet_instigate_acks_(false),
max_undecryptable_packets_(0),
max_tracked_packets_(GetQuicFlag(quic_max_tracked_packet_count)),
idle_timeout_connection_close_behavior_(
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET),
num_rtos_for_blackhole_detection_(0),
uber_received_packet_manager_(&stats_),
pending_retransmission_alarm_(false),
defer_send_in_response_to_packets_(false),
arena_(),
alarms_(this, *alarm_factory_, 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),
consecutive_num_packets_with_no_retransmittable_frames_(0),
max_consecutive_num_packets_with_no_retransmittable_frames_(
kMaxConsecutiveNonRetransmittablePackets),
bundle_retransmittable_with_pto_ack_(false),
last_control_frame_id_(kInvalidControlFrameId),
is_path_degrading_(false),
processing_ack_frame_(false),
supports_release_time_(false),
release_time_into_future_(QuicTime::Delta::Zero()),
blackhole_detector_(this, &alarms_.network_blackhole_detector_alarm()),
idle_network_detector_(this, clock_->ApproximateNow(),
&alarms_.idle_network_detector_alarm()),
path_validator_(alarm_factory_, &arena_, this, random_generator_, clock_,
&context_),
ping_manager_(perspective, this, &alarms_.ping_alarm()),
multi_port_probing_interval_(kDefaultMultiPortProbingInterval),
connection_id_generator_(generator),
received_client_addresses_cache_(kMaxReceivedClientAddressSize) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
default_path_.self_address.IsInitialized());
QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID "
<< server_connection_id
<< " and version: " << ParsedQuicVersionToString(version());
QUIC_BUG_IF(quic_bug_12714_2, !QuicUtils::IsConnectionIdValidForVersion(
server_connection_id, transport_version()))
<< "QuicConnection: attempted to use server connection ID "
<< server_connection_id << " which is invalid with version " << version();
framer_.set_visitor(this);
stats_.connection_creation_time = clock_->ApproximateNow();
// TODO(ianswett): Supply the NetworkChangeVisitor as a constructor argument
// and make it required non-null, because it's always used.
sent_packet_manager_.SetNetworkChangeVisitor(this);
// 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) {
version_negotiated_ = true;
}
if (default_enable_5rto_blackhole_detection_) {
num_rtos_for_blackhole_detection_ = 5;
if (GetQuicReloadableFlag(quic_disable_server_blackhole_detection) &&
perspective_ == Perspective::IS_SERVER) {
QUIC_RELOADABLE_FLAG_COUNT(quic_disable_server_blackhole_detection);
blackhole_detection_disabled_ = true;
}
}
if (perspective_ == Perspective::IS_CLIENT) {
AddKnownServerAddress(initial_peer_address);
}
packet_creator_.SetDefaultPeerAddress(initial_peer_address);
}
void QuicConnection::InstallInitialCrypters(QuicConnectionId connection_id) {
CrypterPair crypters;
CryptoUtils::CreateInitialObfuscators(perspective_, version(), connection_id,
&crypters);
SetEncrypter(ENCRYPTION_INITIAL, std::move(crypters.encrypter));
if (version().KnowsWhichDecrypterToUse()) {
InstallDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter));
} else {
SetDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter));
}
}
QuicConnection::~QuicConnection() {
QUICHE_DCHECK_GE(stats_.max_egress_mtu, long_term_mtu_);
if (owns_writer_) {
delete writer_;
}
ClearQueuedPackets();
if (stats_
.num_tls_server_zero_rtt_packets_received_after_discarding_decrypter >
0) {
QUIC_CODE_COUNT_N(
quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 2,
3);
} else {
QUIC_CODE_COUNT_N(
quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 3,
3);
}
}
void QuicConnection::ClearQueuedPackets() { buffered_packets_.clear(); }
bool QuicConnection::ValidateConfigConnectionIds(const QuicConfig& config) {
QUICHE_DCHECK(config.negotiated());
if (!version().UsesTls()) {
// QUIC+TLS is required to transmit connection ID transport parameters.
return true;
}
// This function validates connection IDs as defined in IETF draft-28 and
// later.
// Validate initial_source_connection_id.
QuicConnectionId expected_initial_source_connection_id;
if (perspective_ == Perspective::IS_CLIENT) {
expected_initial_source_connection_id = default_path_.server_connection_id;
} else {
expected_initial_source_connection_id = default_path_.client_connection_id;
}
if (!config.HasReceivedInitialSourceConnectionId() ||
config.ReceivedInitialSourceConnectionId() !=
expected_initial_source_connection_id) {
std::string received_value;
if (config.HasReceivedInitialSourceConnectionId()) {
received_value = config.ReceivedInitialSourceConnectionId().ToString();
} else {
received_value = "none";
}
std::string error_details =
absl::StrCat("Bad initial_source_connection_id: expected ",
expected_initial_source_connection_id.ToString(),
", received ", received_value);
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
if (perspective_ == Perspective::IS_CLIENT) {
// Validate original_destination_connection_id.
if (!config.HasReceivedOriginalConnectionId() ||
config.ReceivedOriginalConnectionId() !=
GetOriginalDestinationConnectionId()) {
std::string received_value;
if (config.HasReceivedOriginalConnectionId()) {
received_value = config.ReceivedOriginalConnectionId().ToString();
} else {
received_value = "none";
}
std::string error_details =
absl::StrCat("Bad original_destination_connection_id: expected ",
GetOriginalDestinationConnectionId().ToString(),
", received ", received_value);
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// Validate retry_source_connection_id.
if (retry_source_connection_id_.has_value()) {
// We received a RETRY packet, validate that the retry source
// connection ID from the config matches the one from the RETRY.
if (!config.HasReceivedRetrySourceConnectionId() ||
config.ReceivedRetrySourceConnectionId() !=
*retry_source_connection_id_) {
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_->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()) {
if (ShouldFixTimeouts(config)) {
if (!IsHandshakeComplete()) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_timeouts, 1, 2);
SetNetworkTimeouts(config.max_time_before_crypto_handshake(),
config.max_idle_time_before_crypto_handshake());
} else {
QUIC_BUG(set_from_config_after_handshake_complete)
<< "SetFromConfig is called after Handshake complete";
// Network timeouts has been set by session on handshake complete.
}
} else {
// Handshake complete, set handshake timeout to Infinite.
SetNetworkTimeouts(QuicTime::Delta::Infinite(),
config.IdleNetworkTimeout());
}
idle_timeout_connection_close_behavior_ =
ConnectionCloseBehavior::SILENT_CLOSE;
if (perspective_ == Perspective::IS_SERVER) {
idle_timeout_connection_close_behavior_ = ConnectionCloseBehavior::
SILENT_CLOSE_WITH_CONNECTION_CLOSE_PACKET_SERIALIZED;
}
if (config.HasClientRequestedIndependentOption(kNSLC, perspective_)) {
idle_timeout_connection_close_behavior_ =
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET;
}
if (!ValidateConfigConnectionIds(config)) {
return;
}
support_key_update_for_connection_ = version().UsesTls();
framer_.SetKeyUpdateSupportForConnection(
support_key_update_for_connection_);
} else {
SetNetworkTimeouts(config.max_time_before_crypto_handshake(),
config.max_idle_time_before_crypto_handshake());
}
if (version().HasIetfQuicFrames() &&
config.HasReceivedPreferredAddressConnectionIdAndToken()) {
QuicNewConnectionIdFrame frame;
std::tie(frame.connection_id, frame.stateless_reset_token) =
config.ReceivedPreferredAddressConnectionIdAndToken();
frame.sequence_number = 1u;
frame.retire_prior_to = 0u;
OnNewConnectionIdFrameInner(frame);
}
if (config.DisableConnectionMigration()) {
active_migration_disabled_ = true;
}
sent_packet_manager_.SetFromConfig(config);
if (perspective_ == Perspective::IS_SERVER &&
config.HasClientSentConnectionOption(kAFF2, perspective_)) {
send_ack_frequency_on_handshake_completion_ = true;
}
if (config.HasReceivedBytesForConnectionId() &&
can_truncate_connection_ids_) {
packet_creator_.SetServerConnectionIdLength(
config.ReceivedBytesForConnectionId());
}
max_undecryptable_packets_ = config.max_undecryptable_packets();
if (!GetQuicReloadableFlag(quic_enable_mtu_discovery_at_server)) {
if (config.HasClientRequestedIndependentOption(kMTUH, perspective_)) {
SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh);
}
}
if (config.HasClientRequestedIndependentOption(kMTUL, perspective_)) {
SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeLow);
}
if (default_enable_5rto_blackhole_detection_) {
if (config.HasClientRequestedIndependentOption(kCBHD, perspective_)) {
QUIC_CODE_COUNT(quic_client_only_blackhole_detection);
blackhole_detection_disabled_ = true;
}
if (config.HasClientSentConnectionOption(kNBHD, perspective_)) {
blackhole_detection_disabled_ = true;
}
}
if (config.HasClientRequestedIndependentOption(kFIDT, perspective_)) {
idle_network_detector_.enable_shorter_idle_timeout_on_sent_packet();
}
if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames()) {
// Only conduct those experiments in IETF QUIC because random packets may
// elicit reset and gQUIC PUBLIC_RESET will cause connection close.
if (config.HasClientRequestedIndependentOption(kROWF, perspective_)) {
retransmittable_on_wire_behavior_ = SEND_FIRST_FORWARD_SECURE_PACKET;
}
if (config.HasClientRequestedIndependentOption(kROWR, perspective_)) {
retransmittable_on_wire_behavior_ = SEND_RANDOM_BYTES;
}
}
if (config.HasClientRequestedIndependentOption(k3AFF, perspective_)) {
anti_amplification_factor_ = 3;
}
if (config.HasClientRequestedIndependentOption(k10AF, perspective_)) {
anti_amplification_factor_ = 10;
}
if (GetQuicReloadableFlag(quic_enable_server_on_wire_ping) &&
perspective_ == Perspective::IS_SERVER &&
config.HasClientSentConnectionOption(kSRWP, perspective_)) {
QUIC_RELOADABLE_FLAG_COUNT(quic_enable_server_on_wire_ping);
set_initial_retransmittable_on_wire_timeout(
QuicTime::Delta::FromMilliseconds(200));
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnSetFromConfig(config);
}
uber_received_packet_manager_.SetFromConfig(config, perspective_);
if (config.HasClientSentConnectionOption(k5RTO, perspective_)) {
num_rtos_for_blackhole_detection_ = 5;
}
if (config.HasClientSentConnectionOption(k6PTO, perspective_) ||
config.HasClientSentConnectionOption(k7PTO, perspective_) ||
config.HasClientSentConnectionOption(k8PTO, perspective_)) {
num_rtos_for_blackhole_detection_ = 5;
}
if (config.HasReceivedStatelessResetToken()) {
default_path_.stateless_reset_token = config.ReceivedStatelessResetToken();
}
if (config.HasReceivedAckDelayExponent()) {
framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent());
}
if (config.HasClientSentConnectionOption(kEACK, perspective_)) {
bundle_retransmittable_with_pto_ack_ = true;
}
if (config.HasClientSentConnectionOption(kDFER, perspective_)) {
defer_send_in_response_to_packets_ = false;
}
if (perspective_ == Perspective::IS_CLIENT &&
config.HasClientSentConnectionOption(kCDFR, perspective_)) {
defer_send_in_response_to_packets_ = true;
}
if (config.HasClientRequestedIndependentOption(kINVC, perspective_)) {
send_connection_close_for_invalid_version_ = true;
}
if (version().HasIetfQuicFrames() &&
config.HasReceivedPreferredAddressConnectionIdAndToken() &&
config.SupportsServerPreferredAddress(perspective_)) {
if (self_address().host().IsIPv4() &&
config.HasReceivedIPv4AlternateServerAddress()) {
received_server_preferred_address_ =
config.ReceivedIPv4AlternateServerAddress();
} else if (self_address().host().IsIPv6() &&
config.HasReceivedIPv6AlternateServerAddress()) {
received_server_preferred_address_ =
config.ReceivedIPv6AlternateServerAddress();
}
if (received_server_preferred_address_.IsInitialized()) {
QUICHE_DLOG(INFO) << ENDPOINT << "Received server preferred address: "
<< received_server_preferred_address_;
if (config.HasClientRequestedIndependentOption(kSPA2, perspective_)) {
accelerated_server_preferred_address_ = true;
visitor_->OnServerPreferredAddressAvailable(
received_server_preferred_address_);
}
}
}
if (config.HasReceivedMaxPacketSize()) {
peer_max_packet_size_ = config.ReceivedMaxPacketSize();
packet_creator_.SetMaxPacketLength(
GetLimitedMaxPacketSize(packet_creator_.max_packet_length()));
}
if (config.HasReceivedMaxDatagramFrameSize()) {
packet_creator_.SetMaxDatagramFrameSize(
config.ReceivedMaxDatagramFrameSize());
}
supports_release_time_ =
writer_ != nullptr && writer_->SupportsReleaseTime() &&
!config.HasClientSentConnectionOption(kNPCO, perspective_);
if (supports_release_time_) {
UpdateReleaseTimeIntoFuture();
}
if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames() &&
config.HasClientRequestedIndependentOption(kMPQC, perspective_)) {
multi_port_stats_ = std::make_unique<MultiPortStats>();
if (config.HasClientRequestedIndependentOption(kMPQM, perspective_)) {
multi_port_migration_enabled_ = true;
}
}
reliable_stream_reset_ = config.SupportsReliableStreamReset();
framer_.set_process_reset_stream_at(reliable_stream_reset_);
}
void QuicConnection::AddDispatcherSentPackets(
absl::Span<const DispatcherSentPacket> dispatcher_sent_packets) {
QUICHE_DCHECK_EQ(stats_.packets_sent, 0u);
QUICHE_DCHECK_EQ(stats_.packets_sent_by_dispatcher, 0u);
QUICHE_DCHECK(!sent_packet_manager_.GetLargestSentPacket().IsInitialized());
if (dispatcher_sent_packets.empty()) {
return;
}
stats_.packets_sent_by_dispatcher = dispatcher_sent_packets.size();
for (const DispatcherSentPacket& packet : dispatcher_sent_packets) {
const QuicTransmissionInfo& info =
sent_packet_manager_.AddDispatcherSentPacket(packet);
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPacketSent(
packet.packet_number, info.bytes_sent, info.has_crypto_handshake,
info.transmission_type, info.encryption_level,
info.retransmittable_frames,
/*nonretransmittable_frames=*/{}, info.sent_time,
/*batch_id=*/0);
}
}
packet_creator_.set_packet_number(
dispatcher_sent_packets.back().packet_number);
}
bool QuicConnection::MaybeTestLiveness() {
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
if (liveness_testing_disabled_ ||
encryption_level_ != ENCRYPTION_FORWARD_SECURE) {
return false;
}
const QuicTime idle_network_deadline =
idle_network_detector_.GetIdleNetworkDeadline();
if (!idle_network_deadline.IsInitialized()) {
return false;
}
const QuicTime now = clock_->ApproximateNow();
if (now > idle_network_deadline) {
QUIC_DLOG(WARNING) << "Idle network deadline has passed";
return false;
}
const QuicTime::Delta timeout = idle_network_deadline - now;
if (2 * timeout > idle_network_detector_.idle_network_timeout()) {
// Do not test liveness if timeout is > half timeout. This is used to
// prevent an infinite loop for short idle timeout.
return false;
}
if (!sent_packet_manager_.IsLessThanThreePTOs(timeout)) {
return false;
}
QUIC_LOG_EVERY_N_SEC(INFO, 60)
<< "Testing liveness, idle_network_timeout: "
<< idle_network_detector_.idle_network_timeout()
<< ", timeout: " << timeout
<< ", Pto delay: " << sent_packet_manager_.GetPtoDelay()
<< ", smoothed_rtt: "
<< sent_packet_manager_.GetRttStats()->smoothed_rtt()
<< ", mean deviation: "
<< sent_packet_manager_.GetRttStats()->mean_deviation();
SendConnectivityProbingPacket(writer_, peer_address());
return true;
}
void QuicConnection::ApplyConnectionOptions(
const QuicTagVector& connection_options) {
sent_packet_manager_.ApplyConnectionOptions(connection_options);
}
void QuicConnection::OnSendConnectionState(
const CachedNetworkParameters& cached_network_params) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnSendConnectionState(cached_network_params);
}
}
void QuicConnection::OnReceiveConnectionState(
const CachedNetworkParameters& cached_network_params) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnReceiveConnectionState(cached_network_params);
}
}
void QuicConnection::ResumeConnectionState(
const CachedNetworkParameters& cached_network_params,
bool max_bandwidth_resumption) {
sent_packet_manager_.ResumeConnectionState(cached_network_params,
max_bandwidth_resumption);
}
void QuicConnection::SetMaxPacingRate(QuicBandwidth max_pacing_rate) {
sent_packet_manager_.SetMaxPacingRate(max_pacing_rate);
}
void QuicConnection::SetApplicationDrivenPacingRate(
QuicBandwidth application_driven_pacing_rate) {
QUIC_CODE_COUNT(quic_connection_set_app_driven_pacing_rate);
sent_packet_manager_.SetApplicationDrivenPacingRate(
application_driven_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();
}
QuicBandwidth QuicConnection::ApplicationDrivenPacingRate() const {
return sent_packet_manager_.ApplicationDrivenPacingRate();
}
bool QuicConnection::SelectMutualVersion(
const ParsedQuicVersionVector& available_versions) {
// Try to find the highest mutual version by iterating over supported
// versions, starting with the highest, and breaking out of the loop once we
// find a matching version in the provided available_versions vector.
const ParsedQuicVersionVector& supported_versions =
framer_.supported_versions();
for (size_t i = 0; i < supported_versions.size(); ++i) {
const ParsedQuicVersion& version = supported_versions[i];
if (std::find(available_versions.begin(), available_versions.end(),
version) != available_versions.end()) {
framer_.set_version(version);
return true;
}
}
return false;
}
void QuicConnection::OnError(QuicFramer* framer) {
// Packets that we can not or have not decrypted are dropped.
// TODO(rch): add stats to measure this.
if (!connected_ || !last_received_packet_info_.decrypted) {
return;
}
CloseConnection(framer->error(), framer->detailed_error(),
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
void QuicConnection::OnPacket() {
last_received_packet_info_.decrypted = false;
}
bool QuicConnection::OnProtocolVersionMismatch(
ParsedQuicVersion received_version) {
QUIC_DLOG(INFO) << ENDPOINT << "Received packet with mismatched version "
<< ParsedQuicVersionToString(received_version);
if (perspective_ == Perspective::IS_CLIENT) {
const std::string error_details = "Protocol version mismatch.";
QUIC_BUG(quic_bug_10511_3) << ENDPOINT << error_details;
CloseConnection(QUIC_INTERNAL_ERROR, error_details,
ConnectionCloseBehavior::SILENT_CLOSE);
}
// Server drops old packets that were sent by the client before the version
// was negotiated.
return false;
}
// Handles version negotiation for client connection.
void QuicConnection::OnVersionNegotiationPacket(
const QuicVersionNegotiationPacket& packet) {
// Check that any public reset packet with a different connection ID that was
// routed to this QuicConnection has been redirected before control reaches
// here. (Check for a bug regression.)
QUICHE_DCHECK_EQ(default_path_.server_connection_id, packet.connection_id);
if (perspective_ == Perspective::IS_SERVER) {
const std::string error_details =
"Server received version negotiation packet.";
QUIC_BUG(quic_bug_10511_4) << error_details;
QUIC_CODE_COUNT(quic_tear_down_local_connection_on_version_negotiation);
CloseConnection(QUIC_INTERNAL_ERROR, error_details,
ConnectionCloseBehavior::SILENT_CLOSE);
return;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnVersionNegotiationPacket(packet);
}
if (version_negotiated_) {
// Possibly a duplicate version negotiation packet.
return;
}
if (std::find(packet.versions.begin(), packet.versions.end(), version()) !=
packet.versions.end()) {
const std::string error_details = absl::StrCat(
"Server already supports client's version ",
ParsedQuicVersionToString(version()),
" and should have accepted the connection instead of sending {",
ParsedQuicVersionVectorToString(packet.versions), "}.");
QUIC_DLOG(WARNING) << error_details;
CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, error_details,
ConnectionCloseBehavior::SILENT_CLOSE);
return;
}
server_supported_versions_ = packet.versions;
CloseConnection(
QUIC_INVALID_VERSION,
absl::StrCat(
"Client may support one of the versions in the server's list, but "
"it's going to close the connection anyway. Supported versions: {",
ParsedQuicVersionVectorToString(framer_.supported_versions()),
"}, peer supported versions: {",
ParsedQuicVersionVectorToString(packet.versions), "}"),
send_connection_close_for_invalid_version_
? ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET
: ConnectionCloseBehavior::SILENT_CLOSE);
}
// Handles retry for client connection.
void QuicConnection::OnRetryPacket(QuicConnectionId original_connection_id,
QuicConnectionId new_connection_id,
absl::string_view retry_token,
absl::string_view retry_integrity_tag,
absl::string_view retry_without_tag) {
QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
if (version().UsesTls()) {
if (!CryptoUtils::ValidateRetryIntegrityTag(
version(), default_path_.server_connection_id, retry_without_tag,
retry_integrity_tag)) {
QUIC_DLOG(ERROR) << "Ignoring RETRY with invalid integrity tag";
return;
}
} else {
if (original_connection_id != default_path_.server_connection_id) {
QUIC_DLOG(ERROR) << "Ignoring RETRY with original connection ID "
<< original_connection_id << " not matching expected "
<< default_path_.server_connection_id << " token "
<< absl::BytesToHexString(retry_token);
return;
}
}
framer_.set_drop_incoming_retry_packets(true);
stats_.retry_packet_processed = true;
QUIC_DLOG(INFO) << "Received RETRY, replacing connection ID "
<< default_path_.server_connection_id << " with "
<< new_connection_id << ", received token "
<< absl::BytesToHexString(retry_token);
if (!original_destination_connection_id_.has_value()) {
original_destination_connection_id_ = default_path_.server_connection_id;
}
QUICHE_DCHECK(!retry_source_connection_id_.has_value())
<< *retry_source_connection_id_;
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::SetMultiPacketClientHello() {
if (debug_visitor_ != nullptr) {
debug_visitor_->SetMultiPacketClientHello();
}
}
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_;
original_destination_connection_id_ = original_destination_connection_id;
original_destination_connection_id_replacement_ =
default_path_.server_connection_id;
}
QuicConnectionId QuicConnection::GetOriginalDestinationConnectionId() const {
if (original_destination_connection_id_.has_value()) {
return *original_destination_connection_id_;
}
return default_path_.server_connection_id;
}
void QuicConnection::RetireOriginalDestinationConnectionId() {
if (original_destination_connection_id_.has_value()) {
visitor_->OnServerConnectionIdRetired(*original_destination_connection_id_);
original_destination_connection_id_.reset();
}
}
void QuicConnection::OnDiscardZeroRttDecryptionKeysAlarm() {
QUICHE_DCHECK(connected());
QUIC_DLOG(INFO) << "0-RTT discard alarm fired";
RemoveDecrypter(ENCRYPTION_ZERO_RTT);
RetireOriginalDestinationConnectionId();
}
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 (version().HasIetfQuicFrames() && perspective_ == Perspective::IS_SERVER &&
self_issued_cid_manager_ != nullptr &&
self_issued_cid_manager_->IsConnectionIdInUse(server_connection_id)) {
return true;
}
if (NewServerConnectionIdMightBeValid(
header, perspective_, server_connection_id_replaced_by_initial_)) {
return true;
}
return false;
}
bool QuicConnection::OnUnauthenticatedPublicHeader(
const QuicPacketHeader& header) {
last_received_packet_info_.destination_connection_id =
header.destination_connection_id;
// If last packet destination connection ID is the original server
// connection ID chosen by client, replaces it with the connection ID chosen
// by server.
if (perspective_ == Perspective::IS_SERVER &&
original_destination_connection_id_.has_value() &&
last_received_packet_info_.destination_connection_id ==
*original_destination_connection_id_) {
last_received_packet_info_.destination_connection_id =
original_destination_connection_id_replacement_;
}
// As soon as we receive an initial we start ignoring subsequent retries.
if (header.version_flag && header.long_packet_type == INITIAL) {
framer_.set_drop_incoming_retry_packets(true);
}
if (!ValidateServerConnectionId(header)) {
++stats_.packets_dropped;
QuicConnectionId server_connection_id =
GetServerConnectionIdAsRecipient(header, perspective_);
QUIC_DLOG(INFO) << ENDPOINT
<< "Ignoring packet from unexpected server connection ID "
<< server_connection_id << " instead of "
<< default_path_.server_connection_id;
if (debug_visitor_ != nullptr) {
debug_visitor_->OnIncorrectConnectionId(server_connection_id);
}
QUICHE_DCHECK_NE(Perspective::IS_SERVER, perspective_);
return false;
}
if (!version().SupportsClientConnectionIds()) {
return true;
}
if (perspective_ == Perspective::IS_SERVER &&
header.form == IETF_QUIC_SHORT_HEADER_PACKET) {
return true;
}
QuicConnectionId client_connection_id =
GetClientConnectionIdAsRecipient(header, perspective_);
if (client_connection_id == default_path_.client_connection_id) {
return true;
}
if (!client_connection_id_is_set_ && perspective_ == Perspective::IS_SERVER) {
QUIC_DLOG(INFO) << ENDPOINT
<< "Setting client connection ID from first packet to "
<< client_connection_id;
set_client_connection_id(client_connection_id);
return true;
}
if (version().HasIetfQuicFrames() && 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() && !multi_port_stats_) {
// If path was previously degrading, and migration is successful after
// probing, restart the path degrading and blackhole detection.
// In the case of multi-port, since the alt-path state is inferred from
// historical data, we can't trust it until we receive data on the new path.
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);
}
}
void QuicConnection::OnEncryptedClientHelloSent(
absl::string_view client_hello) const {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnEncryptedClientHelloSent(client_hello);
}
}
void QuicConnection::OnEncryptedClientHelloReceived(
absl::string_view client_hello) const {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnEncryptedClientHelloReceived(client_hello);
}
}
void QuicConnection::OnParsedClientHelloInfo(
const ParsedClientHello& client_hello) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnParsedClientHelloInfo(client_hello);
}
}
bool QuicConnection::HasPendingAcks() const { return ack_alarm().IsSet(); }
void QuicConnection::OnUserAgentIdKnown(const std::string& /*user_agent_id*/) {
sent_packet_manager_.OnUserAgentIdKnown();
}
void QuicConnection::OnDecryptedPacket(size_t /*length*/,
EncryptionLevel level) {
last_received_packet_info_.decrypted_level = level;
last_received_packet_info_.decrypted = true;
if (level == ENCRYPTION_FORWARD_SECURE &&
!have_decrypted_first_one_rtt_packet_) {
have_decrypted_first_one_rtt_packet_ = true;
if (version().UsesTls() && perspective_ == Perspective::IS_SERVER) {
// Servers MAY temporarily retain 0-RTT keys to allow decrypting reordered
// packets without requiring their contents to be retransmitted with 1-RTT
// keys. After receiving a 1-RTT packet, servers MUST discard 0-RTT keys
// within a short time; the RECOMMENDED time period is three times the
// Probe Timeout.
// https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-discarding-0-rtt-keys
discard_zero_rtt_decryption_keys_alarm().Set(
clock_->ApproximateNow() + sent_packet_manager_.GetPtoDelay() * 3);
}
}
if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() &&
(level == ENCRYPTION_HANDSHAKE || level == ENCRYPTION_FORWARD_SECURE)) {
// Address is validated by successfully processing a HANDSHAKE or 1-RTT
// packet.
default_path_.validated = true;
stats_.address_validated_via_decrypting_packet = true;
}
idle_network_detector_.OnPacketReceived(
last_received_packet_info_.receipt_time);
visitor_->OnPacketDecrypted(level);
}
QuicSocketAddress QuicConnection::GetEffectivePeerAddressFromCurrentPacket()
const {
// By default, the connection is not proxied, and the effective peer address
// is the packet's source address, i.e. the direct peer address.
return last_received_packet_info_.source_address;
}
bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPacketHeader(header, clock_->ApproximateNow(),
last_received_packet_info_.decrypted_level);
}
// Will be decremented below if we fall through to return true.
++stats_.packets_dropped;
if (!ProcessValidatedPacket(header)) {
return false;
}
// Initialize the current packet content state.
current_packet_content_ = NO_FRAMES_RECEIVED;
is_current_packet_connectivity_probing_ = false;
has_path_challenge_in_current_packet_ = false;
current_effective_peer_migration_type_ = NO_CHANGE;
if (perspective_ == Perspective::IS_CLIENT) {
if (!GetLargestReceivedPacket().IsInitialized() ||
header.packet_number > GetLargestReceivedPacket()) {
if (version().HasIetfQuicFrames()) {
// Client processes packets from any known server address, but only
// updates peer address on initialization and/or to validated server
// preferred address.
} else {
// Update direct_peer_address_ and default path peer_address immediately
// for client connections.
// TODO(fayang): only change peer addresses in application data packet
// number space.
UpdatePeerAddress(last_received_packet_info_.source_address);
default_path_.peer_address = GetEffectivePeerAddressFromCurrentPacket();
}
}
} else {
// At server, remember the address change type of effective_peer_address
// in current_effective_peer_migration_type_. But this variable alone
// doesn't necessarily starts a migration. A migration will be started
// later, once the current packet is confirmed to meet the following
// conditions:
// 1) current_effective_peer_migration_type_ is not NO_CHANGE.
// 2) The current packet is not a connectivity probing.
// 3) The current packet is not reordered, i.e. its packet number is the
// largest of this connection so far.
// Once the above conditions are confirmed, a new migration will start
// even if there is an active migration underway.
current_effective_peer_migration_type_ =
QuicUtils::DetermineAddressChangeType(
default_path_.peer_address,
GetEffectivePeerAddressFromCurrentPacket());
if (version().HasIetfQuicFrames()) {
auto effective_peer_address = GetEffectivePeerAddressFromCurrentPacket();
// Since server does not send new connection ID to client before handshake
// completion and source connection ID is omitted in short header packet,
// the server_connection_id on PathState on the server side does not
// affect the packets server writes after handshake completion. On the
// other hand, it is still desirable to have the "correct" server
// connection ID set on path.
// 1) If client uses 1 unique server connection ID per path and the packet
// is received from an existing path, then
// last_received_packet_info_.destination_connection_id will always be the
// same as the server connection ID on path. Server side will maintain the
// 1-to-1 mapping from server connection ID to path. 2) If client uses
// multiple server connection IDs on the same path, compared to the
// server_connection_id on path,
// last_received_packet_info_.destination_connection_id has the advantage
// that it is still present in the session map since the packet can be
// routed here regardless of packet reordering.
if (IsDefaultPath(last_received_packet_info_.destination_address,
effective_peer_address)) {
default_path_.server_connection_id =
last_received_packet_info_.destination_connection_id;
} else if (IsAlternativePath(
last_received_packet_info_.destination_address,
effective_peer_address)) {
alternative_path_.server_connection_id =
last_received_packet_info_.destination_connection_id;
}
}
if (last_received_packet_info_.destination_connection_id !=
default_path_.server_connection_id &&
(!original_destination_connection_id_.has_value() ||
last_received_packet_info_.destination_connection_id !=
*original_destination_connection_id_)) {
QUIC_CODE_COUNT(quic_connection_id_change);
}
QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE)
<< ENDPOINT << "Effective peer's ip:port changed from "
<< default_path_.peer_address.ToString() << " to "
<< GetEffectivePeerAddressFromCurrentPacket().ToString()
<< ", active_effective_peer_migration_type is "
<< active_effective_peer_migration_type_;
}
--stats_.packets_dropped;
QUIC_DVLOG(1) << ENDPOINT << "Received packet header: " << header;
last_received_packet_info_.header = header;
if (!stats_.first_decrypted_packet.IsInitialized()) {
stats_.first_decrypted_packet =
last_received_packet_info_.header.packet_number;
}
switch (last_received_packet_info_.ecn_codepoint) {
case ECN_NOT_ECT:
break;
case ECN_ECT0:
stats_.num_ecn_marks_received.ect0++;
break;
case ECN_ECT1:
stats_.num_ecn_marks_received.ect1++;
break;
case ECN_CE:
stats_.num_ecn_marks_received.ce++;
break;
}
// Record packet receipt to populate ack info before processing stream
// frames, since the processing may result in sending a bundled ack.
QuicTime receipt_time = idle_network_detector_.time_of_last_received_packet();
if (SupportsMultiplePacketNumberSpaces()) {
receipt_time = last_received_packet_info_.receipt_time;
}
uber_received_packet_manager_.RecordPacketReceived(
last_received_packet_info_.decrypted_level,
last_received_packet_info_.header, receipt_time,
last_received_packet_info_.ecn_codepoint);
if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() &&
!header.retry_token.empty() &&
visitor_->ValidateToken(header.retry_token)) {
QUIC_DLOG(INFO) << ENDPOINT << "Address validated via token.";
QUIC_CODE_COUNT(quic_address_validated_via_token);
default_path_.validated = true;
stats_.address_validated_via_token = true;
}
QUICHE_DCHECK(connected_);
return true;
}
bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_3, !connected_)
<< "Processing STREAM frame when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
// Since a stream frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(STREAM_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnStreamFrame(frame);
}
if (!QuicUtils::IsCryptoStreamId(transport_version(), frame.stream_id) &&
last_received_packet_info_.decrypted_level == ENCRYPTION_INITIAL) {
if (MaybeConsiderAsMemoryCorruption(frame)) {
CloseConnection(QUIC_MAYBE_CORRUPTED_MEMORY,
"Received crypto frame on non crypto stream.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
QUIC_PEER_BUG(quic_peer_bug_10511_6)
<< ENDPOINT << "Received an unencrypted data frame: closing connection"
<< " packet_number:" << last_received_packet_info_.header.packet_number
<< " stream_id:" << frame.stream_id
<< " received_packets:" << ack_frame();
CloseConnection(QUIC_UNENCRYPTED_STREAM_DATA,
"Unencrypted stream data seen.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// TODO(fayang): Consider moving UpdatePacketContent and
// MaybeUpdateAckTimeout to a stand-alone function instead of calling them for
// all frames.
MaybeUpdateAckTimeout();
visitor_->OnStreamFrame(frame);
stats_.stream_bytes_received += frame.data_length;
ping_manager_.reset_consecutive_retransmittable_on_wire_count();
return connected_;
}
bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_4, !connected_)
<< "Processing CRYPTO frame when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
// Since a CRYPTO frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(CRYPTO_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnCryptoFrame(frame);
}
MaybeUpdateAckTimeout();
visitor_->OnCryptoFrame(frame);
return connected_;
}
bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked,
QuicTime::Delta ack_delay_time) {
QUIC_BUG_IF(quic_bug_12714_5, !connected_)
<< "Processing ACK frame start when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
if (processing_ack_frame_) {
CloseConnection(QUIC_INVALID_ACK_DATA,
"Received a new ack while processing an ack frame.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// Since an ack frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(ACK_FRAME)) {
return false;
}
QUIC_DVLOG(1) << ENDPOINT
<< "OnAckFrameStart, largest_acked: " << largest_acked;
if (GetLargestReceivedPacketWithAck().IsInitialized() &&
last_received_packet_info_.header.packet_number <=
GetLargestReceivedPacketWithAck()) {
QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
return true;
}
if (!sent_packet_manager_.GetLargestSentPacket().IsInitialized() ||
largest_acked > sent_packet_manager_.GetLargestSentPacket()) {
QUIC_DLOG(WARNING) << ENDPOINT
<< "Peer's observed unsent packet:" << largest_acked
<< " vs " << sent_packet_manager_.GetLargestSentPacket()
<< ". SupportsMultiplePacketNumberSpaces():"
<< SupportsMultiplePacketNumberSpaces()
<< ", last_received_packet_info_.decrypted_level:"
<< last_received_packet_info_.decrypted_level;
// We got an ack for data we have not sent.
CloseConnection(QUIC_INVALID_ACK_DATA, "Largest observed too high.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
processing_ack_frame_ = true;
sent_packet_manager_.OnAckFrameStart(
largest_acked, ack_delay_time,
idle_network_detector_.time_of_last_received_packet());
return true;
}
bool QuicConnection::OnAckRange(QuicPacketNumber start, QuicPacketNumber end) {
QUIC_BUG_IF(quic_bug_12714_6, !connected_)
<< "Processing ACK frame range when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")";
if (GetLargestReceivedPacketWithAck().IsInitialized() &&
last_received_packet_info_.header.packet_number <=
GetLargestReceivedPacketWithAck()) {
QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
return true;
}
sent_packet_manager_.OnAckRange(start, end);
return true;
}
bool QuicConnection::OnAckTimestamp(QuicPacketNumber packet_number,
QuicTime timestamp) {
QUIC_BUG_IF(quic_bug_10511_7, !connected_)
<< "Processing ACK frame time stamp when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
QUIC_DVLOG(1) << ENDPOINT << "OnAckTimestamp: [" << packet_number << ", "
<< timestamp.ToDebuggingValue() << ")";
if (GetLargestReceivedPacketWithAck().IsInitialized() &&
last_received_packet_info_.header.packet_number <=
GetLargestReceivedPacketWithAck()) {
QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
return true;
}
sent_packet_manager_.OnAckTimestamp(packet_number, timestamp);
return true;
}
bool QuicConnection::OnAckFrameEnd(
QuicPacketNumber start, const std::optional<QuicEcnCounts>& ecn_counts) {
QUIC_BUG_IF(quic_bug_12714_7, !connected_)
<< "Processing ACK frame end when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start;
if (GetLargestReceivedPacketWithAck().IsInitialized() &&
last_received_packet_info_.header.packet_number <=
GetLargestReceivedPacketWithAck()) {
QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
return true;
}
const bool one_rtt_packet_was_acked =
sent_packet_manager_.one_rtt_packet_acked();
const bool zero_rtt_packet_was_acked =
sent_packet_manager_.zero_rtt_packet_acked();
const AckResult ack_result = sent_packet_manager_.OnAckFrameEnd(
idle_network_detector_.time_of_last_received_packet(),
last_received_packet_info_.header.packet_number,
last_received_packet_info_.decrypted_level, ecn_counts);
if (ack_result != PACKETS_NEWLY_ACKED &&
ack_result != NO_PACKETS_NEWLY_ACKED) {
// Error occurred (e.g., this ACK tries to ack packets in wrong packet
// number space), and this would cause the connection to be closed.
QUIC_DLOG(ERROR) << ENDPOINT
<< "Error occurred when processing an ACK frame: "
<< QuicUtils::AckResultToString(ack_result);
return false;
}
if (SupportsMultiplePacketNumberSpaces() && !one_rtt_packet_was_acked &&
sent_packet_manager_.one_rtt_packet_acked()) {
visitor_->OnOneRttPacketAcknowledged();
}
if (debug_visitor_ != nullptr && version().UsesTls() &&
!zero_rtt_packet_was_acked &&
sent_packet_manager_.zero_rtt_packet_acked()) {
debug_visitor_->OnZeroRttPacketAcked();
}
// Cancel the send alarm because new packets likely have been acked, which
// may change the congestion window and/or pacing rate. Canceling the alarm
// causes CanWrite to recalculate the next send time.
if (send_alarm().IsSet()) {
send_alarm().Cancel();
}
if (supports_release_time_) {
// Update pace time into future because smoothed RTT is likely updated.
UpdateReleaseTimeIntoFuture();
}
SetLargestReceivedPacketWithAck(
last_received_packet_info_.header.packet_number);
PostProcessAfterAckFrame(ack_result == PACKETS_NEWLY_ACKED);
processing_ack_frame_ = false;
return connected_;
}
bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& /*frame*/) {
QUIC_BUG_IF(quic_bug_12714_8, !connected_)
<< "Processing STOP_WAITING frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
// Since a stop waiting frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(STOP_WAITING_FRAME)) {
return false;
}
return connected_;
}
bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_9, !connected_)
<< "Processing PADDING frame when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
if (!UpdatePacketContent(PADDING_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPaddingFrame(frame);
}
return true;
}
bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_10, !connected_)
<< "Processing PING frame when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
if (!UpdatePacketContent(PING_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
QuicTime::Delta ping_received_delay = QuicTime::Delta::Zero();
const QuicTime now = clock_->ApproximateNow();
if (now > stats_.connection_creation_time) {
ping_received_delay = now - stats_.connection_creation_time;
}
debug_visitor_->OnPingFrame(frame, ping_received_delay);
}
MaybeUpdateAckTimeout();
return true;
}
bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_11, !connected_)
<< "Processing RST_STREAM frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
// Since a reset stream frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(RST_STREAM_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnRstStreamFrame(frame);
}
QUIC_DLOG(INFO) << ENDPOINT
<< "RST_STREAM_FRAME received for stream: " << frame.stream_id
<< " with error: "
<< QuicRstStreamErrorCodeToString(frame.error_code);
MaybeUpdateAckTimeout();
visitor_->OnRstStream(frame);
return connected_;
}
bool QuicConnection::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_12, !connected_)
<< "Processing STOP_SENDING frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
// Since a reset stream frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(STOP_SENDING_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnStopSendingFrame(frame);
}
QUIC_DLOG(INFO) << ENDPOINT << "STOP_SENDING frame received for stream: "
<< frame.stream_id
<< " with error: " << frame.ietf_error_code;
MaybeUpdateAckTimeout();
visitor_->OnStopSendingFrame(frame);
return connected_;
}
class ReversePathValidationContext : public QuicPathValidationContext {
public:
ReversePathValidationContext(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicSocketAddress& effective_peer_address,
QuicConnection* connection)
: QuicPathValidationContext(self_address, peer_address,
effective_peer_address),
connection_(connection) {}
QuicPacketWriter* WriterToUse() override { return connection_->writer(); }
private:
QuicConnection* connection_;
};
bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_8, !connected_)
<< "Processing PATH_CHALLENGE frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
if (has_path_challenge_in_current_packet_) {
// Only respond to the 1st PATH_CHALLENGE in the packet.
return true;
}
should_proactively_validate_peer_address_on_path_challenge_ = false;
// UpdatePacketContent() may start reverse path validation.
if (!UpdatePacketContent(PATH_CHALLENGE_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPathChallengeFrame(frame);
}
// On the server side, send response to the source address of the current
// incoming packet according to RFC9000.
// On the client side, send response to the default peer address which
// should be on an existing path with a pre-assigned a destination CID.
const QuicSocketAddress effective_peer_address_to_respond =
perspective_ == Perspective::IS_CLIENT
? effective_peer_address()
: GetEffectivePeerAddressFromCurrentPacket();
const QuicSocketAddress direct_peer_address_to_respond =
perspective_ == Perspective::IS_CLIENT
? direct_peer_address_
: last_received_packet_info_.source_address;
QuicConnectionId client_cid, server_cid;
FindOnPathConnectionIds(last_received_packet_info_.destination_address,
effective_peer_address_to_respond, &client_cid,
&server_cid);
{
QuicPacketCreator::ScopedPeerAddressContext context(
&packet_creator_, direct_peer_address_to_respond, client_cid,
server_cid);
if (should_proactively_validate_peer_address_on_path_challenge_) {
// Conditions to proactively validate peer address:
// The perspective is server
// The PATH_CHALLENGE is received on an unvalidated alternative path.
// The connection isn't validating migrated peer address, which is of
// higher prority.
QUIC_DVLOG(1) << "Proactively validate the effective peer address "
<< effective_peer_address_to_respond;
QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 2, 6);
ValidatePath(
std::make_unique<ReversePathValidationContext>(
default_path_.self_address, direct_peer_address_to_respond,
effective_peer_address_to_respond, this),
std::make_unique<ReversePathValidationResultDelegate>(this,
peer_address()),
PathValidationReason::kReversePathValidation);
}
has_path_challenge_in_current_packet_ = true;
MaybeUpdateAckTimeout();
// Queue or send PATH_RESPONSE.
if (!SendPathResponse(frame.data_buffer, direct_peer_address_to_respond,
effective_peer_address_to_respond)) {
QUIC_CODE_COUNT(quic_failed_to_send_path_response);
}
// TODO(b/150095588): change the stats to
// num_valid_path_challenge_received.
++stats_.num_connectivity_probing_received;
// Flushing packet creator might cause connection to be closed.
}
return connected_;
}
bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_9, !connected_)
<< "Processing PATH_RESPONSE frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
++stats_.num_path_response_received;
if (!UpdatePacketContent(PATH_RESPONSE_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPathResponseFrame(frame);
}
MaybeUpdateAckTimeout();
path_validator_.OnPathResponse(
frame.data_buffer, last_received_packet_info_.destination_address);
return connected_;
}
bool QuicConnection::OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_10, !connected_)
<< "Processing CONNECTION_CLOSE frame when connection is closed. "
"Received packet info: "
<< last_received_packet_info_;
// Since a connection close frame was received, this is not a connectivity
// probe. A probe only contains a PING and full padding.
if (!UpdatePacketContent(CONNECTION_CLOSE_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnConnectionCloseFrame(frame);
}
switch (frame.close_type) {
case GOOGLE_QUIC_CONNECTION_CLOSE:
QUIC_DLOG(INFO) << ENDPOINT << "Received ConnectionClose for connection: "
<< connection_id() << ", with error: "
<< QuicErrorCodeToString(frame.quic_error_code) << " ("
<< frame.error_details << ")";
break;
case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE:
QUIC_DLOG(INFO) << ENDPOINT
<< "Received Transport ConnectionClose for connection: "
<< connection_id() << ", with error: "
<< QuicErrorCodeToString(frame.quic_error_code) << " ("
<< frame.error_details << ")"
<< ", transport error code: "
<< QuicIetfTransportErrorCodeString(
static_cast<QuicIetfTransportErrorCodes>(
frame.wire_error_code))
<< ", error frame type: "
<< frame.transport_close_frame_type;
break;
case IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
QUIC_DLOG(INFO) << ENDPOINT
<< "Received Application ConnectionClose for connection: "
<< connection_id() << ", with error: "
<< QuicErrorCodeToString(frame.quic_error_code) << " ("
<< frame.error_details << ")"
<< ", application error code: " << frame.wire_error_code;
break;
}
if (frame.quic_error_code == QUIC_BAD_MULTIPATH_FLAG) {
QUIC_LOG_FIRST_N(ERROR, 10)
<< "Unexpected QUIC_BAD_MULTIPATH_FLAG error."
<< " last_received_header: " << last_received_packet_info_.header
<< " encryption_level: " << encryption_level_;
}
TearDownLocalConnectionState(frame, ConnectionCloseSource::FROM_PEER);
return connected_;
}
bool QuicConnection::OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_13, !connected_)
<< "Processing MAX_STREAMS frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
if (!UpdatePacketContent(MAX_STREAMS_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnMaxStreamsFrame(frame);
}
MaybeUpdateAckTimeout();
return visitor_->OnMaxStreamsFrame(frame) && connected_;
}
bool QuicConnection::OnStreamsBlockedFrame(
const QuicStreamsBlockedFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_11, !connected_)
<< "Processing STREAMS_BLOCKED frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
if (!UpdatePacketContent(STREAMS_BLOCKED_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnStreamsBlockedFrame(frame);
}
MaybeUpdateAckTimeout();
return visitor_->OnStreamsBlockedFrame(frame) && connected_;
}
bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_14, !connected_)
<< "Processing GOAWAY frame when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
// Since a go away frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(GOAWAY_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnGoAwayFrame(frame);
}
QUIC_DLOG(INFO) << ENDPOINT << "GOAWAY_FRAME received with last good stream: "
<< frame.last_good_stream_id
<< " and error: " << QuicErrorCodeToString(frame.error_code)
<< " and reason: " << frame.reason_phrase;
MaybeUpdateAckTimeout();
visitor_->OnGoAway(frame);
return connected_;
}
bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_12, !connected_)
<< "Processing WINDOW_UPDATE frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
// Since a window update frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(WINDOW_UPDATE_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnWindowUpdateFrame(
frame, idle_network_detector_.time_of_last_received_packet());
}
QUIC_DVLOG(1) << ENDPOINT << "WINDOW_UPDATE_FRAME received " << frame;
MaybeUpdateAckTimeout();
visitor_->OnWindowUpdateFrame(frame);
return connected_;
}
void QuicConnection::OnClientConnectionIdAvailable() {
QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
if (!peer_issued_cid_manager_->HasUnusedConnectionId()) {
return;
}
if (default_path_.client_connection_id.IsEmpty()) {
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()) {
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;
}
}
NewConnectionIdResult QuicConnection::OnNewConnectionIdFrameInner(
const QuicNewConnectionIdFrame& frame) {
if (peer_issued_cid_manager_ == nullptr) {
CloseConnection(
IETF_QUIC_PROTOCOL_VIOLATION,
"Receives NEW_CONNECTION_ID while peer uses zero length connection ID",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return NewConnectionIdResult::kProtocolViolation;
}
std::string error_detail;
bool duplicate_new_connection_id = false;
QuicErrorCode error = peer_issued_cid_manager_->OnNewConnectionIdFrame(
frame, &error_detail, &duplicate_new_connection_id);
if (error != QUIC_NO_ERROR) {
CloseConnection(error, error_detail,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return NewConnectionIdResult::kProtocolViolation;
}
if (duplicate_new_connection_id) {
return NewConnectionIdResult::kDuplicateFrame;
}
if (perspective_ == Perspective::IS_SERVER) {
OnClientConnectionIdAvailable();
}
MaybeUpdateAckTimeout();
return NewConnectionIdResult::kOk;
}
bool QuicConnection::OnNewConnectionIdFrame(
const QuicNewConnectionIdFrame& frame) {
QUICHE_DCHECK(version().HasIetfQuicFrames());
QUIC_BUG_IF(quic_bug_10511_13, !connected_)
<< "Processing NEW_CONNECTION_ID frame when connection is closed. "
"Received packet info: "
<< last_received_packet_info_;
if (!UpdatePacketContent(NEW_CONNECTION_ID_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnNewConnectionIdFrame(frame);
}
NewConnectionIdResult result = OnNewConnectionIdFrameInner(frame);
switch (result) {
case NewConnectionIdResult::kOk:
if (multi_port_stats_ != nullptr) {
MaybeCreateMultiPortPath();
}
break;
case NewConnectionIdResult::kProtocolViolation:
return false;
case NewConnectionIdResult::kDuplicateFrame:
break;
}
return true;
}
bool QuicConnection::OnRetireConnectionIdFrame(
const QuicRetireConnectionIdFrame& frame) {
QUICHE_DCHECK(version().HasIetfQuicFrames());
QUIC_BUG_IF(quic_bug_10511_14, !connected_)
<< "Processing RETIRE_CONNECTION_ID frame when connection is closed. "
"Received packet info: "
<< last_received_packet_info_;
if (!UpdatePacketContent(RETIRE_CONNECTION_ID_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnRetireConnectionIdFrame(frame);
}
if (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.
MaybeUpdateAckTimeout();
return true;
}
bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_15, !connected_)
<< "Processing NEW_TOKEN frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
if (!UpdatePacketContent(NEW_TOKEN_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnNewTokenFrame(frame);
}
if (perspective_ == Perspective::IS_SERVER) {
CloseConnection(QUIC_INVALID_NEW_TOKEN, "Server received new token frame.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// NEW_TOKEN frame should insitgate ACKs.
MaybeUpdateAckTimeout();
visitor_->OnNewTokenReceived(frame.token);
return true;
}
bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_16, !connected_)
<< "Processing MESSAGE frame when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
// Since a message frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(MESSAGE_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnMessageFrame(frame);
}
MaybeUpdateAckTimeout();
visitor_->OnMessageReceived(
absl::string_view(frame.data, frame.message_length));
return connected_;
}
bool QuicConnection::OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_15, !connected_)
<< "Processing HANDSHAKE_DONE frame when connection "
"is closed. Received packet "
"info: "
<< last_received_packet_info_;
if (!version().UsesTls()) {
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
"Handshake done frame is unsupported",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
if (perspective_ == Perspective::IS_SERVER) {
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
"Server received handshake done frame.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
// Since a handshake done frame was received, this is not a connectivity
// probe. A probe only contains a PING and full padding.
if (!UpdatePacketContent(HANDSHAKE_DONE_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnHandshakeDoneFrame(frame);
}
MaybeUpdateAckTimeout();
visitor_->OnHandshakeDoneReceived();
return connected_;
}
bool QuicConnection::OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) {
QUIC_BUG_IF(quic_bug_10511_16, !connected_)
<< "Processing ACK_FREQUENCY frame when connection "
"is closed. Received packet "
"info: "
<< last_received_packet_info_;
if (debug_visitor_ != nullptr) {
debug_visitor_->OnAckFrequencyFrame(frame);
}
if (!UpdatePacketContent(ACK_FREQUENCY_FRAME)) {
return false;
}
if (!can_receive_ack_frequency_frame_) {
QUIC_LOG_EVERY_N_SEC(ERROR, 120) << "Get unexpected AckFrequencyFrame.";
return false;
}
if (auto packet_number_space =
QuicUtils::GetPacketNumberSpace(
last_received_packet_info_.decrypted_level) == APPLICATION_DATA) {
uber_received_packet_manager_.OnAckFrequencyFrame(frame);
} else {
QUIC_LOG_EVERY_N_SEC(ERROR, 120)
<< "Get AckFrequencyFrame in packet number space "
<< packet_number_space;
}
MaybeUpdateAckTimeout();
return true;
}
bool QuicConnection::OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) {
QUIC_BUG_IF(OnResetStreamAtFrame_connection_closed, !connected_)
<< "Processing RESET_STREAM_AT frame while the connection is closed. "
"Received packet info: "
<< last_received_packet_info_;
if (debug_visitor_ != nullptr) {
debug_visitor_->OnResetStreamAtFrame(frame);
}
if (!UpdatePacketContent(RESET_STREAM_AT_FRAME)) {
return false;
}
if (!reliable_stream_reset_) {
CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
"Received RESET_STREAM_AT while not negotiated.",
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return false;
}
MaybeUpdateAckTimeout();
visitor_->OnResetStreamAt(frame);
return true;
}
bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_17, !connected_)
<< "Processing BLOCKED frame when connection is closed. Received packet "
"info: "
<< last_received_packet_info_;
// Since a blocked frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
if (!UpdatePacketContent(BLOCKED_FRAME)) {
return false;
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnBlockedFrame(frame);
}
QUIC_DLOG(INFO) << ENDPOINT
<< "BLOCKED_FRAME received for stream: " << frame.stream_id;
MaybeUpdateAckTimeout();
visitor_->OnBlockedFrame(frame);
stats_.blocked_frames_received++;
return connected_;
}
void QuicConnection::OnPacketComplete() {
// Don't do anything if this packet closed the connection.
if (!connected_) {
ClearLastFrames();
return;
}
if (IsCurrentPacketConnectivityProbing()) {
QUICHE_DCHECK(!version().HasIetfQuicFrames() && !ignore_gquic_probing_);
++stats_.num_connectivity_probing_received;
}
QUIC_DVLOG(1) << ENDPOINT << "Got"
<< (SupportsMultiplePacketNumberSpaces()
? (" " +
EncryptionLevelToString(
last_received_packet_info_.decrypted_level))
: "")
<< " packet " << last_received_packet_info_.header.packet_number
<< " for "
<< GetServerConnectionIdAsRecipient(
last_received_packet_info_.header, perspective_);
QUIC_DLOG_IF(INFO, current_packet_content_ == SECOND_FRAME_IS_PADDING)
<< ENDPOINT << "Received a padded PING packet. is_probing: "
<< IsCurrentPacketConnectivityProbing();
if (!version().HasIetfQuicFrames() && !ignore_gquic_probing_) {
MaybeRespondToConnectivityProbingOrMigration();
}
current_effective_peer_migration_type_ = NO_CHANGE;
// For IETF QUIC, it is guaranteed that TLS will give connection the
// corresponding write key before read key. In other words, connection should
// never process a packet while an ACK for it cannot be encrypted.
if (!should_last_packet_instigate_acks_) {
uber_received_packet_manager_.MaybeUpdateAckTimeout(
should_last_packet_instigate_acks_,
last_received_packet_info_.decrypted_level,
last_received_packet_info_.header.packet_number,
last_received_packet_info_.receipt_time, clock_->ApproximateNow(),
sent_packet_manager_.GetRttStats());
}
ClearLastFrames();
CloseIfTooManyOutstandingSentPackets();
}
void QuicConnection::MaybeRespondToConnectivityProbingOrMigration() {
QUICHE_DCHECK(!version().HasIetfQuicFrames());
if (IsCurrentPacketConnectivityProbing()) {
visitor_->OnPacketReceived(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address,
/*is_connectivity_probe=*/true);
return;
}
if (perspective_ == Perspective::IS_CLIENT) {
// This node is a client, notify that a speculative connectivity probing
// packet has been received anyway.
QUIC_DVLOG(1) << ENDPOINT
<< "Received a speculative connectivity probing packet for "
<< GetServerConnectionIdAsRecipient(
last_received_packet_info_.header, perspective_)
<< " from ip:port: "
<< last_received_packet_info_.source_address.ToString()
<< " to ip:port: "
<< last_received_packet_info_.destination_address.ToString();
visitor_->OnPacketReceived(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address,
/*is_connectivity_probe=*/false);
return;
}
}
bool QuicConnection::IsValidStatelessResetToken(
const StatelessResetToken& token) const {
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
return default_path_.stateless_reset_token.has_value() &&
QuicUtils::AreStatelessResetTokensEqual(
token, *default_path_.stateless_reset_token);
}
void QuicConnection::OnAuthenticatedIetfStatelessResetPacket(
const QuicIetfStatelessResetPacket& /*packet*/) {
// TODO(fayang): Add OnAuthenticatedIetfStatelessResetPacket to
// debug_visitor_.
QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
if (!IsDefaultPath(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address)) {
// This packet is received on a probing path. Do not close connection.
if (IsAlternativePath(last_received_packet_info_.destination_address,
GetEffectivePeerAddressFromCurrentPacket())) {
QUIC_BUG_IF(quic_bug_12714_18, alternative_path_.validated)
<< "STATELESS_RESET received on alternate path after it's "
"validated.";
path_validator_.CancelPathValidation();
++stats_.num_stateless_resets_on_alternate_path;
} else {
QUIC_BUG(quic_bug_10511_17)
<< "Received Stateless Reset on unknown socket.";
}
return;
}
const std::string error_details = "Received stateless reset.";
QUIC_CODE_COUNT(quic_tear_down_local_connection_on_stateless_reset);
TearDownLocalConnectionState(QUIC_PUBLIC_RESET, NO_IETF_QUIC_ERROR,
error_details, ConnectionCloseSource::FROM_PEER);
}
void QuicConnection::OnKeyUpdate(KeyUpdateReason reason) {
QUICHE_DCHECK(support_key_update_for_connection_);
QUIC_DLOG(INFO) << ENDPOINT << "Key phase updated for " << reason;
lowest_packet_sent_in_current_key_phase_.Clear();
stats_.key_update_count++;
// If another key update triggers while the previous
// discard_previous_one_rtt_keys_alarm_ hasn't fired yet, cancel it since the
// old keys would already be discarded.
discard_previous_one_rtt_keys_alarm().Cancel();
visitor_->OnKeyUpdate(reason);
}
void QuicConnection::OnDecryptedFirstPacketInKeyPhase() {
QUIC_DLOG(INFO) << ENDPOINT << "OnDecryptedFirstPacketInKeyPhase";
// An endpoint SHOULD retain old read keys for no more than three times the
// PTO after having received a packet protected using the new keys. After this
// period, old read keys and their corresponding secrets SHOULD be discarded.
//
// Note that this will cause an unnecessary
// discard_previous_one_rtt_keys_alarm_ on the first packet in the 1RTT
// encryption level, but this is harmless.
discard_previous_one_rtt_keys_alarm().Set(
clock_->ApproximateNow() + sent_packet_manager_.GetPtoDelay() * 3);
}
std::unique_ptr<QuicDecrypter>
QuicConnection::AdvanceKeysAndCreateCurrentOneRttDecrypter() {
QUIC_DLOG(INFO) << ENDPOINT << "AdvanceKeysAndCreateCurrentOneRttDecrypter";
return visitor_->AdvanceKeysAndCreateCurrentOneRttDecrypter();
}
std::unique_ptr<QuicEncrypter> QuicConnection::CreateCurrentOneRttEncrypter() {
QUIC_DLOG(INFO) << ENDPOINT << "CreateCurrentOneRttEncrypter";
return visitor_->CreateCurrentOneRttEncrypter();
}
void QuicConnection::ClearLastFrames() {
should_last_packet_instigate_acks_ = false;
}
void QuicConnection::CloseIfTooManyOutstandingSentPackets() {
// This occurs if we don't discard old packets we've seen fast enough. It's
// possible largest observed is less than leaset unacked.
const bool should_close =
sent_packet_manager_.GetLargestSentPacket().IsInitialized() &&
sent_packet_manager_.GetLargestSentPacket() >
sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_;
if (should_close) {
CloseConnection(
QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS,
absl::StrCat("More than ", max_tracked_packets_,
" outstanding, least_unacked: ",
sent_packet_manager_.GetLeastUnacked().ToUint64(),
", packets_processed: ", stats_.packets_processed,
", last_decrypted_packet_level: ",
EncryptionLevelToString(
last_received_packet_info_.decrypted_level)),
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
}
const QuicFrame QuicConnection::GetUpdatedAckFrame() {
QUICHE_DCHECK(!uber_received_packet_manager_.IsAckFrameEmpty(
QuicUtils::GetPacketNumberSpace(encryption_level_)))
<< "Try to retrieve an empty ACK frame";
return uber_received_packet_manager_.GetUpdatedAckFrame(
QuicUtils::GetPacketNumberSpace(encryption_level_),
clock_->ApproximateNow());
}
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 (IsMissingDestinationConnectionID()) {
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;
}
if (!defer_send_in_response_to_packets_) {
WriteIfNotBlocked();
return;
}
if (!visitor_->WillingAndAbleToWrite()) {
QUIC_DVLOG(1)
<< "No send alarm after processing packet. !WillingAndAbleToWrite.";
return;
}
// If the send alarm is already armed. Record its deadline in |max_deadline|
// and cancel the alarm temporarily. The rest of this function will ensure
// the alarm deadline is no later than |max_deadline| when the function exits.
QuicTime max_deadline = QuicTime::Infinite();
if (send_alarm().IsSet()) {
QUIC_DVLOG(1) << "Send alarm already set to " << send_alarm().deadline();
max_deadline = send_alarm().deadline();
send_alarm().Cancel();
}
if (CanWrite(HAS_RETRANSMITTABLE_DATA)) {
// Some data can be written immediately. Register for immediate resumption
// so we'll keep writing after other connections.
QUIC_BUG_IF(quic_send_alarm_set_with_data_to_send, send_alarm().IsSet());
QUIC_DVLOG(1) << "Immediate send alarm scheduled after processing packet.";
send_alarm().Set(clock_->ApproximateNow() +
sent_packet_manager_.GetDeferredSendAlarmDelay());
return;
}
if (send_alarm().IsSet()) {
// Pacing limited: CanWrite returned false, and it has scheduled a send
// alarm before it returns.
if (send_alarm().deadline() > max_deadline) {
QUIC_DVLOG(1)
<< "Send alarm restored after processing packet. previous deadline:"
<< max_deadline
<< ", deadline from CanWrite:" << send_alarm().deadline();
// Restore to the previous, earlier deadline.
send_alarm().Update(max_deadline, QuicTime::Delta::Zero());
} else {
QUIC_DVLOG(1) << "Future send alarm scheduled after processing packet.";
}
return;
}
if (max_deadline != QuicTime::Infinite()) {
QUIC_DVLOG(1) << "Send alarm restored after processing packet.";
send_alarm().Set(max_deadline);
return;
}
// Can not send data due to other reasons: congestion blocked, anti
// amplification throttled, etc.
QUIC_DVLOG(1) << "No send alarm after processing packet. Other reasons.";
}
size_t QuicConnection::SendCryptoData(EncryptionLevel level,
size_t write_length,
QuicStreamOffset offset) {
if (write_length == 0) {
QUIC_BUG(quic_bug_10511_18) << "Attempt to send empty crypto frame";
return 0;
}
ScopedPacketFlusher flusher(this);
return packet_creator_.ConsumeCryptoData(level, write_length, offset);
}
QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
size_t write_length,
QuicStreamOffset offset,
StreamSendingState state) {
if (state == NO_FIN && write_length == 0) {
QUIC_BUG(quic_bug_10511_19) << "Attempt to send empty stream frame";
return QuicConsumedData(0, false);
}
if (perspective_ == Perspective::IS_SERVER &&
version().CanSendCoalescedPackets() && !IsHandshakeConfirmed()) {
if (in_probe_time_out_ && coalesced_packet_.NumberOfPackets() == 0u) {
// PTO fires while handshake is not confirmed. Do not preempt handshake
// data with stream data.
QUIC_CODE_COUNT(quic_try_to_send_half_rtt_data_when_pto_fires);
return QuicConsumedData(0, false);
}
if (coalesced_packet_.ContainsPacketOfEncryptionLevel(ENCRYPTION_INITIAL) &&
coalesced_packet_.NumberOfPackets() == 1u) {
// Handshake is not confirmed yet, if there is only an initial packet in
// the coalescer, try to bundle an ENCRYPTION_HANDSHAKE packet before
// sending stream data.
sent_packet_manager_.RetransmitDataOfSpaceIfAny(HANDSHAKE_DATA);
}
}
// Opportunistically bundle an ack with every outgoing packet.
// Particularly, we want to bundle with handshake packets since we don't
// know which decrypter will be used on an ack packet following a handshake
// packet (a handshake packet from client to server could result in a REJ or
// a SHLO from the server, leading to two different decrypters at the
// server.)
ScopedPacketFlusher flusher(this);
return packet_creator_.ConsumeData(id, write_length, offset, state);
}
bool QuicConnection::SendControlFrame(const QuicFrame& frame) {
if (SupportsMultiplePacketNumberSpaces() &&
(encryption_level_ == ENCRYPTION_INITIAL ||
encryption_level_ == ENCRYPTION_HANDSHAKE) &&
frame.type != PING_FRAME) {
// Allow PING frame to be sent without APPLICATION key. For example, when
// anti-amplification limit is used, client needs to send something to avoid
// handshake deadlock.
QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame
<< " at encryption level: " << encryption_level_;
return false;
}
ScopedPacketFlusher flusher(this);
const bool consumed =
packet_creator_.ConsumeRetransmittableControlFrame(frame);
if (!consumed) {
QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame;
return false;
}
if (frame.type == PING_FRAME) {
// Flush PING frame immediately.
packet_creator_.FlushCurrentPacket();
stats_.ping_frames_sent++;
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPingSent();
}
}
if (frame.type == BLOCKED_FRAME) {
stats_.blocked_frames_sent++;
}
return true;
}
void QuicConnection::OnStreamReset(QuicStreamId id,
QuicRstStreamErrorCode error) {
if (error == QUIC_STREAM_NO_ERROR) {
// All data for streams which are reset with QUIC_STREAM_NO_ERROR must
// be received by the peer.
return;
}
// Flush stream frames of reset stream.
if (packet_creator_.HasPendingStreamFramesOfStream(id)) {
ScopedPacketFlusher flusher(this);
packet_creator_.FlushCurrentPacket();
}
// TODO(ianswett): Consider checking for 3 RTOs when the last stream is
// cancelled as well.
}
const QuicConnectionStats& QuicConnection::GetStats() {
const RttStats* rtt_stats = sent_packet_manager_.GetRttStats();
// Update rtt and estimated bandwidth.
QuicTime::Delta min_rtt = rtt_stats->min_rtt();
if (min_rtt.IsZero()) {
// If min RTT has not been set, use initial RTT instead.
min_rtt = rtt_stats->initial_rtt();
}
stats_.min_rtt_us = min_rtt.ToMicroseconds();
QuicTime::Delta srtt = rtt_stats->SmoothedOrInitialRtt();
stats_.srtt_us = srtt.ToMicroseconds();
stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate();
sent_packet_manager_.GetSendAlgorithm()->PopulateConnectionStats(&stats_);
stats_.egress_mtu = long_term_mtu_;
stats_.ingress_mtu = largest_received_packet_size_;
return stats_;
}
void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) {
QueueCoalescedPacket(packet);
}
void QuicConnection::OnUndecryptablePacket(const QuicEncryptedPacket& packet,
EncryptionLevel decryption_level,
bool has_decryption_key) {
QUIC_DVLOG(1) << ENDPOINT << "Received undecryptable packet of length "
<< packet.length() << " with"
<< (has_decryption_key ? "" : "out") << " key at level "
<< decryption_level
<< " while connection is at encryption level "
<< encryption_level_;
QUICHE_DCHECK(EncryptionLevelIsValid(decryption_level));
if (encryption_level_ != ENCRYPTION_FORWARD_SECURE) {
++stats_.undecryptable_packets_received_before_handshake_complete;
}
const bool should_enqueue =
ShouldEnqueueUnDecryptablePacket(decryption_level, has_decryption_key);
if (should_enqueue) {
QueueUndecryptablePacket(packet, decryption_level);
}
if (debug_visitor_ != nullptr) {
debug_visitor_->OnUndecryptablePacket(decryption_level,
/*dropped=*/!should_enqueue);
}
if (has_decryption_key) {
stats_.num_failed_authentication_packets_received++;
if (version().UsesTls()) {
// Should always be non-null if has_decryption_key is true.
QUICHE_DCHECK(framer_.GetDecrypter(decryption_level));
const QuicPacketCount integrity_limit =
framer_.GetDecrypter(decryption_level)->GetIntegrityLimit();
QUIC_DVLOG(2) << ENDPOINT << "Checking AEAD integrity limits:"
<< " num_failed_authentication_packets_received="
<< stats_.num_failed_authentication_packets_received
<< " integrity_limit=" << integrity_limit;
if (stats_.num_failed_authentication_packets_received >=
integrity_limit) {
const std::string error_details = absl::StrCat(
"decrypter integrity limit reached:"
" num_failed_authentication_packets_received=",
stats_.num_failed_authentication_packets_received,
" integrity_limit=", integrity_limit);
CloseConnection(QUIC_AEAD_LIMIT_REACHED, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
}
}
if (version().UsesTls() && perspective_ == Perspective::IS_SERVER &&
decryption_level == ENCRYPTION_ZERO_RTT && !has_decryption_key &&
had_zero_rtt_decrypter_) {
QUIC_CODE_COUNT_N(
quic_server_received_tls_zero_rtt_packet_after_discarding_decrypter, 1,
3);
stats_
.num_tls_server_zero_rtt_packets_received_after_discarding_decrypter++;
}
}
bool QuicConnection::ShouldEnqueueUnDecryptablePacket(
EncryptionLevel decryption_level, bool has_decryption_key) const {
if (has_decryption_key) {
// We already have the key for this decryption level, therefore no
// future keys will allow it be decrypted.
return false;
}
if (IsHandshakeComplete()) {
// We do not expect to install any further keys.
return false;
}
if (undecryptable_packets_.size() >= max_undecryptable_packets_) {
// We do not queue more than max_undecryptable_packets_ packets.
return false;
}
if (version().KnowsWhichDecrypterToUse() &&
decryption_level == ENCRYPTION_INITIAL) {
// When the corresponding decryption key is not available, all
// non-Initial packets should be buffered until the handshake is complete.
return false;
}
if (perspective_ == Perspective::IS_CLIENT && version().UsesTls() &&
decryption_level == ENCRYPTION_ZERO_RTT) {
// Only clients send Zero RTT packets in IETF QUIC.
QUIC_PEER_BUG(quic_peer_bug_client_received_zero_rtt)
<< "Client received a Zero RTT packet, not buffering.";
return false;
}
return true;
}
std::string QuicConnection::UndecryptablePacketsInfo() const {
std::string info = absl::StrCat(
"num_undecryptable_packets: ", undecryptable_packets_.size(), " {");
for (const auto& packet : undecryptable_packets_) {
absl::StrAppend(&info, "[",
EncryptionLevelToString(packet.encryption_level), ", ",
packet.packet->length(), "]");
}
absl::StrAppend(&info, "}");
return info;
}
void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
if (!connected_) {
return;
}
QUIC_DVLOG(2) << ENDPOINT << "Received encrypted " << packet.length()
<< " bytes:" << std::endl
<< quiche::QuicheTextUtils::HexDump(
absl::string_view(packet.data(), packet.length()));
QUIC_BUG_IF(quic_bug_12714_21, current_packet_data_ != nullptr)
<< "ProcessUdpPacket must not be called while processing a packet.";
if (debug_visitor_ != nullptr) {
debug_visitor_->OnPacketReceived(self_address, peer_address, packet);
}
last_received_packet_info_ = ReceivedPacketInfo(
self_address, peer_address, packet.receipt_time(), packet.length(),
packet.ecn_codepoint(), packet.ipv6_flow_label());
current_packet_data_ = packet.data();
if (!default_path_.self_address.IsInitialized()) {
default_path_.self_address = last_received_packet_info_.destination_address;
} else if (default_path_.self_address != self_address &&
expected_server_preferred_address_.IsInitialized() &&
self_address.Normalized() ==
expected_server_preferred_address_.Normalized()) {
// If the packet is received at the preferred address, treat it as if it is
// received on the original server address.
last_received_packet_info_.destination_address = default_path_.self_address;
last_received_packet_info_.actual_destination_address = self_address;
}
if (!direct_peer_address_.IsInitialized()) {
if (perspective_ == Perspective::IS_CLIENT) {
AddKnownServerAddress(last_received_packet_info_.source_address);
}
UpdatePeerAddress(last_received_packet_info_.source_address);
}
if (!default_path_.peer_address.IsInitialized()) {
const QuicSocketAddress effective_peer_addr =
GetEffectivePeerAddressFromCurrentPacket();
// The default path peer_address must be initialized at the beginning of the
// first packet processed(here). If effective_peer_addr is uninitialized,
// just set effective_peer_address_ to the direct peer address.
default_path_.peer_address = effective_peer_addr.IsInitialized()
? effective_peer_addr
: direct_peer_address_;
}
stats_.bytes_received += packet.length();
++stats_.packets_received;
if (IsDefaultPath(last_received_packet_info_.destination_address,
last_received_packet_info_.source_address) &&
EnforceAntiAmplificationLimit()) {
last_received_packet_info_.received_bytes_counted = true;
default_path_.bytes_received_before_address_validation +=
last_received_packet_info_.length;
}
// Ensure the time coming from the packet reader is within 2 minutes of now.
if (std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) >
2 * 60) {
QUIC_LOG(WARNING) << "(Formerly quic_bug_10511_21): Packet receipt time: "
<< packet.receipt_time().ToDebuggingValue()
<< " too far from current time: "
<< clock_->ApproximateNow().ToDebuggingValue();
}
QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: "
<< packet.receipt_time().ToDebuggingValue() << " from peer "
<< last_received_packet_info_.source_address << ", to "
<< last_received_packet_info_.destination_address;
ScopedPacketFlusher flusher(this);
if (!framer_.ProcessPacket(packet)) {
// If we are unable to decrypt this packet, it might be
// because the CHLO or SHLO packet was lost.
QUIC_DVLOG(1) << ENDPOINT
<< "Unable to process packet. Last packet processed: "
<< last_received_packet_info_.header.packet_number;
current_packet_data_ = nullptr;
is_current_packet_connectivity_probing_ = false;
MaybeProcessCoalescedPackets();
return;
}
++stats_.packets_processed;
QUIC_DLOG_IF(INFO, active_effective_peer_migration_type_ != NO_CHANGE)
<< "sent_packet_manager_.GetLargestObserved() = "
<< sent_packet_manager_.GetLargestObserved()
<< ", highest_packet_sent_before_effective_peer_migration_ = "
<< highest_packet_sent_before_effective_peer_migration_;
if (!framer_.version().HasIetfQuicFrames() &&
active_effective_peer_migration_type_ != NO_CHANGE &&
sent_packet_manager_.GetLargestObserved().IsInitialized() &&
(!highest_packet_sent_before_effective_peer_migration_.IsInitialized() ||
sent_packet_manager_.GetLargestObserved() >
highest_packet_sent_before_effective_peer_migration_)) {
if (perspective_ == Perspective::IS_SERVER) {
OnEffectivePeerMigrationValidated(/*is_migration_linkable=*/true);
}
}
if (!MaybeProcessCoalescedPackets()) {
MaybeProcessUndecryptablePackets();
MaybeSendInResponseToPacket();
}
SetPingAlarm();
RetirePeerIssuedConnectionIdsNoLongerOnPath();
current_packet_data_ = nullptr;
is_current_packet_connectivity_probing_ = false;
}
void QuicConnection::OnBlockedWriterCanWrite() {
writer_->SetWritable();
OnCanWrite();
}
void QuicConnection::OnCanWrite() {
if (!connected_) {
return;
}
if (writer_->IsWriteBlocked()) {
const std::string error_details =
"Writer is blocked while calling OnCanWrite.";
QUIC_BUG(quic_bug_10511_22) << ENDPOINT << error_details;
CloseConnection(QUIC_INTERNAL_ERROR, error_details,
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
return;
}
ScopedPacketFlusher flusher(this);
WriteQueuedPackets();
const QuicTime ack_timeout =
uber_received_packet_manager_.GetEarliestAckTimeout();
if (ack_timeout.IsInitialized() && ack_timeout <= clock_->ApproximateNow()) {
// Send an ACK now because either 1) we were write blocked when we last
// tried to send an ACK, or 2) both ack alarm and send alarm were set to
// go off together.
if (SupportsMultiplePacketNumberSpaces()) {
SendAllPendingAcks();
} else {
SendAck();
}
}
// Sending queued packets may have caused the socket to become write blocked,
// or the congestion manager to prohibit sending.
if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
return;
}
// Tell the session it can write.
visitor_->OnCanWrite();
// After the visitor writes, it may have caused the socket to become write
// blocked or the congestion manager to prohibit sending, so check again.
if (visitor_->WillingAndAbleToWrite() && !send_alarm().IsSet() &&
CanWrite(HAS_RETRANSMITTABLE_DATA)) {
// We're not write blocked, but some data wasn't written. Register for
// 'immediate' resumption so we'll keep writing after other connections.
send_alarm().Set(clock_->ApproximateNow());
}
}
void QuicConnection::OnSendAlarm() {
QUICHE_DCHECK(connected());
WriteIfNotBlocked();
}
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 (IsMissingDestinationConnectionID()) {
return;
}
if (!HandleWriteBlocked()) {
OnCanWrite();
}
}
void QuicConnection::MaybeClearQueuedPacketsOnPathChange() {
if (version().HasIetfQuicFrames() && peer_issued_cid_manager_ != nullptr &&
HasQueuedPackets()) {
// Discard packets serialized with the connection ID on the old code path.
// It is possible to clear queued packets only if connection ID changes.
// However, the case where connection ID is unchanged and queued packets are
// non-empty is quite rare.
ClearQueuedPackets();
}
}
void QuicConnection::ReplaceInitialServerConnectionId(
const QuicConnectionId& new_server_connection_id) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
if (version().HasIetfQuicFrames()) {
if (new_server_connection_id.IsEmpty()) {
peer_issued_cid_manager_ = nullptr;
} else {
if (peer_issued_cid_manager_ != nullptr) {
QUIC_BUG_IF(quic_bug_12714_22,
!peer_issued_cid_manager_->IsConnectionIdActive(
default_path_.server_connection_id))
<< "Connection ID replaced header is no longer active. old id: "
<< default_path_.server_connection_id
<< " new_id: " << new_server_connection_id;
peer_issued_cid_manager_->ReplaceConnectionId(
default_path_.server_connection_id, new_server_connection_id);
} else {
peer_issued_cid_manager_ =
std::make_unique<QuicPeerIssuedConnectionIdManager>(
kMinNumOfActiveConnectionIds, new_server_connection_id, clock_,
alarm_factory_, this, context());
}
}
}
default_path_.server_connection_id = new_server_connection_id;
packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
}
void QuicConnection::FindMatchingOrNewClientConnectionIdOrToken(
const PathState& default_path, const PathState& alternative_path,
const QuicConnectionId& server_connection_id,
QuicConnectionId* client_connection_id,
std::optional<StatelessResetToken>* stateless_reset_token) {
QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER &&
version().HasIetfQuicFrames());
if (peer_issued_cid_manager_ == nullptr ||
server_connection_id == default_path.server_connection_id) {
*client_connection_id = default_path.client_connection_id;
*stateless_reset_token = default_path.stateless_reset_token;
return;
}
if (server_connection_id == alternative_path_.server_connection_id) {
*client_connection_id = alternative_path.client_connection_id;
*stateless_reset_token = alternative_path.stateless_reset_token;
return;
}
auto* connection_id_data =
peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
if (connection_id_data == nullptr) {
return;
}
*client_connection_id = connection_id_data->connection_id;
*stateless_reset_token = connection_id_data->stateless_reset_token;
}
bool QuicConnection::FindOnPathConnectionIds(
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
QuicConnectionId* client_connection_id,
QuicConnectionId* server_connection_id) const {
if (IsDefaultPath(self_address, peer_address)) {
*client_connection_id = default_path_.client_connection_id,
*server_connection_id = default_path_.server_connection_id;
return true;
}
if (IsAlternativePath(self_address, peer_address)) {
*client_connection_id = alternative_path_.client_connection_id,
*server_connection_id = alternative_path_.server_connection_id;
return true;
}
// Client should only send packets on either default or alternative path, so
// it shouldn't fail here. If the server fail to find CID to use, no packet
// will be generated on this path.
// TODO(danzh) fix SendPathResponse() to respond to probes from a different
// client port with non-Zero client CID.
QUIC_BUG_IF(failed to find on path connection ids,
perspective_ == Perspective::IS_CLIENT)
<< "Fails to find on path connection IDs";
return false;
}
void QuicConnection::SetDefaultPathState(PathState new_path_state) {
QUICHE_DCHECK(version().HasIetfQuicFrames());
default_path_ = std::move(new_path_state);
packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
}
// TODO(wub): Inline this function when deprecating
// --quic_test_peer_addr_change_after_normalize.
bool QuicConnection::PeerAddressChanged() const {
if (quic_test_peer_addr_change_after_normalize_) {
return direct_peer_address_.Normalized() !=
last_received_packet_info_.source_address.Normalized();
}
return direct_peer_address_ != last_received_packet_info_.source_address;
}
bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) {
if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames() &&
direct_peer_address_.IsInitialized() &&
last_received_packet_info_.source_address.IsInitialized() &&
PeerAddressChanged() &&
!IsKnownServerAddress(last_received_packet_info_.source_address)) {
// Discard packets received from unseen server addresses.
return false;
}
if (perspective_ == Perspective::IS_SERVER &&
default_path_.self_address.IsInitialized() &&
last_received_packet_info_.destination_address.IsInitialized() &&
default_path_.self_address !=
last_received_packet_info_.destination_address) {
// Allow change between pure IPv4 and equivalent mapped IPv4 address.
if (default_path_.self_address.port() !=
last_received_packet_info_.destination_address.port() ||
default_path_.self_address.host().Normalized() !=
last_received_packet_info_.destination_address.host()
.Normalized()) {
if (!visitor_->AllowSelfAddressChange()) {
const std::string error_details = absl::StrCat(
"Self address migration is not supported at the server, current "
"address: ",
default_path_.self_address.ToString(),
", expected server preferred address: ",
expected_server_preferred_address_.ToString(),
", received packet address: ",
last_received_packet_info_.destination_address.ToString(),
", size: ", last_received_packet_info_.length,
", packet number: ", header.packet_number.ToString(),
", encryption level: ",
EncryptionLevelToString(
last_received_packet_info_.decrypted_level));
QUIC_LOG_EVERY_N_SEC(INFO, 100) << error_details;
QUIC_CODE_COUNT(quic_dropped_packets_with_changed_server_address);
return false;
}
}
default_path_.self_address = last_received_packet_info_.destination_address;