Commit 5be4e96e authored by Renjie Tang's avatar Renjie Tang Committed by Chromium LUCI CQ

Use QUIC core path validator for IETF QUIC connection migration probing.

Change-Id: I5fbffa18e477d5e24fe3a772f1ed97f92d9b985e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2573678
Commit-Queue: Renjie Tang <renjietang@chromium.org>
Reviewed-by: default avatarDavid Schinazi <dschinazi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#844183}
parent 4445f174
......@@ -762,6 +762,120 @@ int QuicChromiumClientSession::StreamRequest::DoRequestStreamComplete(int rv) {
return rv;
}
QuicChromiumClientSession::QuicChromiumPathValidationContext::
QuicChromiumPathValidationContext(
const quic::QuicSocketAddress& self_address,
const quic::QuicSocketAddress& peer_address,
NetworkChangeNotifier::NetworkHandle network,
std::unique_ptr<DatagramClientSocket> socket,
std::unique_ptr<QuicChromiumPacketWriter> writer,
std::unique_ptr<QuicChromiumPacketReader> reader)
: QuicPathValidationContext(self_address, peer_address),
network_handle_(network),
socket_(std::move(socket)),
writer_(std::move(writer)),
reader_(std::move(reader)) {}
QuicChromiumClientSession::QuicChromiumPathValidationContext::
~QuicChromiumPathValidationContext() = default;
NetworkChangeNotifier::NetworkHandle
QuicChromiumClientSession::QuicChromiumPathValidationContext::network() {
return network_handle_;
}
quic::QuicPacketWriter*
QuicChromiumClientSession::QuicChromiumPathValidationContext::WriterToUse() {
return writer_.get();
}
std::unique_ptr<QuicChromiumPacketWriter>
QuicChromiumClientSession::QuicChromiumPathValidationContext::ReleaseWriter() {
return std::move(writer_);
}
std::unique_ptr<DatagramClientSocket>
QuicChromiumClientSession::QuicChromiumPathValidationContext::ReleaseSocket() {
return std::move(socket_);
}
std::unique_ptr<QuicChromiumPacketReader>
QuicChromiumClientSession::QuicChromiumPathValidationContext::ReleaseReader() {
return std::move(reader_);
}
QuicChromiumClientSession::ConnectionMigrationValidationResultDelegate::
ConnectionMigrationValidationResultDelegate(
QuicChromiumClientSession* session)
: session_(session) {}
void QuicChromiumClientSession::ConnectionMigrationValidationResultDelegate::
OnPathValidationSuccess(
std::unique_ptr<quic::QuicPathValidationContext> context) {
auto* chrome_context =
static_cast<QuicChromiumPathValidationContext*>(context.get());
session_->OnConnectionMigrationProbeSucceeded(
chrome_context->network(), chrome_context->peer_address(),
chrome_context->self_address(), chrome_context->ReleaseSocket(),
chrome_context->ReleaseWriter(), chrome_context->ReleaseReader());
}
void QuicChromiumClientSession::ConnectionMigrationValidationResultDelegate::
OnPathValidationFailure(
std::unique_ptr<quic::QuicPathValidationContext> context) {
// Note that socket, packet writer, and packet reader in |context| will be
// discarded.
auto* chrome_context =
static_cast<QuicChromiumPathValidationContext*>(context.get());
session_->OnProbeFailed(chrome_context->network(),
chrome_context->peer_address());
}
QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
QuicChromiumPathValidationWriterDelegate(
QuicChromiumClientSession* session,
base::SequencedTaskRunner* task_runner)
: session_(session),
task_runner_(task_runner),
network_(NetworkChangeNotifier::kInvalidNetworkHandle) {}
QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
~QuicChromiumPathValidationWriterDelegate() = default;
int QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
HandleWriteError(
int error_code,
scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> last_packet) {
// Write error on the probing network is not recoverable.
DVLOG(1) << "Probing packet encounters write error " << error_code;
// Post a task to notify |session_| that this probe failed and cancel
// undergoing probing, which will delete the packet writer.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&QuicChromiumPathValidationWriterDelegate::NotifySessionProbeFailed,
weak_factory_.GetWeakPtr(), network_));
return error_code;
}
void QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
OnWriteError(int error_code) {
NotifySessionProbeFailed(network_);
}
void QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
OnWriteUnblocked() {}
void QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
NotifySessionProbeFailed(NetworkChangeNotifier::NetworkHandle network) {
session_->OnProbeFailed(network, peer_address_);
}
void QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
set_peer_address(const quic::QuicSocketAddress& peer_address) {
peer_address_ = peer_address;
}
void QuicChromiumClientSession::QuicChromiumPathValidationWriterDelegate::
set_network(NetworkChangeNotifier::NetworkHandle network) {
network_ = network;
}
QuicChromiumClientSession::QuicChromiumClientSession(
quic::QuicConnection* connection,
std::unique_ptr<DatagramClientSocket> socket,
......@@ -865,7 +979,8 @@ QuicChromiumClientSession::QuicChromiumClientSession(
attempted_zero_rtt_(false),
num_migrations_(0),
last_key_update_reason_(quic::KeyUpdateReason::kInvalid),
push_promise_index_(std::move(push_promise_index)) {
push_promise_index_(std::move(push_promise_index)),
path_validation_writer_delegate_(this, task_runner_) {
// Make sure connection migration and goaway on path degrading are not turned
// on at the same time.
DCHECK(!(migrate_session_early_v2_ && go_away_on_path_degrading_));
......@@ -2352,6 +2467,19 @@ void QuicChromiumClientSession::OnProbeFailed(
/*is_success=*/false);
});
if (version().HasIetfQuicFrames()) {
auto* context = static_cast<QuicChromiumPathValidationContext*>(
connection()->GetPathValidationContext());
if (!context)
return;
if (context->network() == network &&
context->peer_address() == peer_address) {
connection()->CancelPathValidation();
}
}
LogProbeResultToHistogram(current_migration_cause_, false);
if (network != NetworkChangeNotifier::kInvalidNetworkHandle) {
......@@ -2418,7 +2546,16 @@ void QuicChromiumClientSession::OnNetworkDisconnectedV2(
"disconnected_network", disconnected_network);
// Stop probing the disconnected network if there is one.
if (version().HasIetfQuicFrames()) {
auto* context = static_cast<QuicChromiumPathValidationContext*>(
connection()->GetPathValidationContext());
if (context && context->network() == disconnected_network &&
context->peer_address() == peer_address()) {
connection()->CancelPathValidation();
}
} else {
probing_manager_.CancelProbing(disconnected_network, peer_address());
}
if (disconnected_network == default_network_) {
DVLOG(1) << "Default network: " << default_network_ << " is disconnected.";
default_network_ = NetworkChangeNotifier::kInvalidNetworkHandle;
......@@ -2535,7 +2672,16 @@ void QuicChromiumClientSession::MigrateNetworkImmediately(
}
// Cancel probing on |network| if there is any.
if (version().HasIetfQuicFrames()) {
auto* context = static_cast<QuicChromiumPathValidationContext*>(
connection()->GetPathValidationContext());
if (context && context->network() == network &&
context->peer_address() == peer_address()) {
connection()->CancelPathValidation();
}
} else {
probing_manager_.CancelProbing(network, peer_address());
}
MigrationResult result =
Migrate(network, ToIPEndPoint(connection()->peer_address()),
......@@ -2854,7 +3000,8 @@ ProbingResult QuicChromiumClientSession::MaybeStartProbing(
return ProbingResult::DISABLED_WITH_IDLE_SESSION;
// Abort probing if connection migration is disabled by config.
if (config()->DisableConnectionMigration()) {
if (config()->DisableConnectionMigration() ||
(version().HasIetfQuicFrames() && !connection()->use_path_validator())) {
DVLOG(1) << "Client disables probing network with connection migration "
<< "disabled by config";
HistogramAndLogMigrationFailure(MIGRATION_STATUS_DISABLED_BY_CONFIG,
......@@ -2870,8 +3017,16 @@ ProbingResult QuicChromiumClientSession::StartProbing(
NetworkChangeNotifier::NetworkHandle network,
const quic::QuicSocketAddress& peer_address) {
// Check if probing manager is probing the same path.
if (probing_manager_.IsUnderProbing(network, peer_address))
if (version().HasIetfQuicFrames()) {
auto* context = static_cast<QuicChromiumPathValidationContext*>(
connection()->GetPathValidationContext());
if (context && context->network() == network &&
context->peer_address() == peer_address) {
return ProbingResult::PENDING;
}
} else if (probing_manager_.IsUnderProbing(network, peer_address)) {
return ProbingResult::PENDING;
}
// Create and configure socket on |network|.
std::unique_ptr<DatagramClientSocket> probing_socket =
......@@ -2902,6 +3057,24 @@ ProbingResult QuicChromiumClientSession::StartProbing(
rtt_ms = kDefaultRTTMilliSecs;
int timeout_ms = rtt_ms * 2;
if (version().HasIetfQuicFrames() &&
current_migration_cause_ != CHANGE_PORT_ON_PATH_DEGRADING) {
probing_reader->StartReading();
path_validation_writer_delegate_.set_network(network);
path_validation_writer_delegate_.set_peer_address(peer_address);
probing_writer->set_delegate(&path_validation_writer_delegate_);
IPEndPoint local_address;
probing_socket->GetLocalAddress(&local_address);
auto context = std::make_unique<QuicChromiumPathValidationContext>(
ToQuicSocketAddress(local_address), peer_address, network,
std::move(probing_socket), std::move(probing_writer),
std::move(probing_reader));
ValidatePath(
std::move(context),
std::make_unique<ConnectionMigrationValidationResultDelegate>(this));
return ProbingResult::PENDING;
}
probing_manager_.StartProbing(
network, peer_address, std::move(probing_socket),
std::move(probing_writer), std::move(probing_reader),
......
......@@ -44,7 +44,9 @@
#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h"
#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
#include "net/third_party/quiche/src/quic/core/quic_packets.h"
#include "net/third_party/quiche/src/quic/core/quic_path_validator.h"
#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
......@@ -409,6 +411,89 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession
DISALLOW_COPY_AND_ASSIGN(StreamRequest);
};
// This class contains all the context needed for path validation and
// migration.
class NET_EXPORT_PRIVATE QuicChromiumPathValidationContext
: public quic::QuicPathValidationContext {
public:
QuicChromiumPathValidationContext(
const quic::QuicSocketAddress& self_address,
const quic::QuicSocketAddress& peer_address,
NetworkChangeNotifier::NetworkHandle network,
std::unique_ptr<DatagramClientSocket> socket,
std::unique_ptr<QuicChromiumPacketWriter> writer,
std::unique_ptr<QuicChromiumPacketReader> reader);
~QuicChromiumPathValidationContext() override;
NetworkChangeNotifier::NetworkHandle network();
quic::QuicPacketWriter* WriterToUse() override;
// Transfer the ownership from |this| to the caller.
std::unique_ptr<QuicChromiumPacketWriter> ReleaseWriter();
std::unique_ptr<DatagramClientSocket> ReleaseSocket();
std::unique_ptr<QuicChromiumPacketReader> ReleaseReader();
private:
NetworkChangeNotifier::NetworkHandle network_handle_;
std::unique_ptr<DatagramClientSocket> socket_;
std::unique_ptr<QuicChromiumPacketWriter> writer_;
std::unique_ptr<QuicChromiumPacketReader> reader_;
};
// This class implements Chrome logic for path validation events associated
// with connection migration.
class NET_EXPORT_PRIVATE ConnectionMigrationValidationResultDelegate
: public quic::QuicPathValidator::ResultDelegate {
public:
explicit ConnectionMigrationValidationResultDelegate(
QuicChromiumClientSession* session);
void OnPathValidationSuccess(
std::unique_ptr<quic::QuicPathValidationContext> context) override;
void OnPathValidationFailure(
std::unique_ptr<quic::QuicPathValidationContext> context) override;
private:
// |session_| owns |this| and should out live |this|.
QuicChromiumClientSession* session_;
};
// This class is used to handle writer events that occur on the probing path.
class NET_EXPORT_PRIVATE QuicChromiumPathValidationWriterDelegate
: public QuicChromiumPacketWriter::Delegate {
public:
QuicChromiumPathValidationWriterDelegate(
QuicChromiumClientSession* session,
base::SequencedTaskRunner* task_runner);
~QuicChromiumPathValidationWriterDelegate();
// QuicChromiumPacketWriter::Delegate interface.
int HandleWriteError(
int error_code,
scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> last_packet)
override;
void OnWriteError(int error_code) override;
void OnWriteUnblocked() override;
void set_peer_address(const quic::QuicSocketAddress& peer_address);
void set_network(NetworkChangeNotifier::NetworkHandle network);
private:
void NotifySessionProbeFailed(NetworkChangeNotifier::NetworkHandle network);
// |session_| owns |this| and should out live |this|.
QuicChromiumClientSession* session_;
// |task_owner_| should out live |this|.
base::SequencedTaskRunner* task_runner_;
// The path validation context of the most recent probing.
NetworkChangeNotifier::NetworkHandle network_;
quic::QuicSocketAddress peer_address_;
base::WeakPtrFactory<QuicChromiumPathValidationWriterDelegate>
weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(QuicChromiumPathValidationWriterDelegate);
};
// Constructs a new session which will own |connection|, but not
// |stream_factory|, which must outlive this session.
// TODO(rch): decouple the factory from the session via a Delegate interface.
......@@ -952,6 +1037,8 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession
std::unique_ptr<quic::QuicClientPushPromiseIndex> push_promise_index_;
QuicChromiumPathValidationWriterDelegate path_validation_writer_delegate_;
base::WeakPtrFactory<QuicChromiumClientSession> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(QuicChromiumClientSession);
......
This diff is collapsed.
......@@ -494,6 +494,35 @@ QuicTestPacketMaker::MakeDataRstAckAndConnectionClosePacket(
return BuildPacket();
}
std::unique_ptr<quic::QuicReceivedPacket>
QuicTestPacketMaker::MakeDataRstAckAndConnectionClosePacket(
uint64_t num,
bool include_version,
quic::QuicStreamId data_stream_id,
absl::string_view data,
quic::QuicStreamId rst_stream_id,
quic::QuicRstStreamErrorCode error_code,
uint64_t largest_received,
uint64_t smallest_received,
quic::QuicErrorCode quic_error,
const std::string& quic_error_details,
uint64_t frame_type) {
InitializeHeader(num, include_version);
AddQuicAckFrame(largest_received, smallest_received);
AddQuicStreamFrame(data_stream_id, /* fin = */ false, data);
if (version_.HasIetfQuicFrames()) {
AddQuicStopSendingFrame(rst_stream_id, error_code);
}
AddQuicRstStreamFrame(rst_stream_id, error_code);
AddQuicAckFrame(largest_received, smallest_received);
AddQuicConnectionCloseFrame(quic_error, quic_error_details, frame_type);
return BuildPacket();
}
std::unique_ptr<quic::QuicReceivedPacket>
QuicTestPacketMaker::MakeAckAndConnectionClosePacket(
uint64_t num,
......
......@@ -205,6 +205,20 @@ class QuicTestPacketMaker {
quic::QuicErrorCode quic_error,
const std::string& quic_error_details);
std::unique_ptr<quic::QuicReceivedPacket>
MakeDataRstAckAndConnectionClosePacket(
uint64_t num,
bool include_version,
quic::QuicStreamId data_stream_id,
absl::string_view data,
quic::QuicStreamId rst_stream_id,
quic::QuicRstStreamErrorCode error_code,
uint64_t largest_received,
uint64_t smallest_received,
quic::QuicErrorCode quic_error,
const std::string& quic_error_details,
uint64_t frame_type);
std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndConnectionClosePacket(
uint64_t num,
bool include_version,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment