Refactoring server preferred address (SPA) validation interfaces. And add sanity checks in QuicConnection client code to prevent client from kicking off connection migration while validating the SPA.
Change QuicConnection::ValidateServerPreferredAddress() to an abstract interface in QuicSession.
The change is client-side-only and behind connection option "SPAD".
PiperOrigin-RevId: 500024246
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index a929e3a..e340897 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -242,43 +242,6 @@
return false;
}
-// Client migrates to server preferred address on path validation suceeds.
-// Otherwise, client cleans up alternative path.
-class ServerPreferredAddressResultDelegate
- : public QuicPathValidator::ResultDelegate {
- public:
- explicit ServerPreferredAddressResultDelegate(QuicConnection* connection)
- : connection_(connection) {}
- void OnPathValidationSuccess(
- std::unique_ptr<QuicPathValidationContext> context,
- QuicTime /*start_time*/) override {
- QUIC_DLOG(INFO) << "Server preferred address: " << context->peer_address()
- << " validated. Migrating path, self_address: "
- << context->self_address()
- << ", peer_address: " << context->peer_address();
- connection_->mutable_stats().server_preferred_address_validated = true;
- const bool success = connection_->MigratePath(context->self_address(),
- context->peer_address(),
- context->WriterToUse(),
- /*owns_writer*/ false);
- QUIC_BUG_IF(failed to migrate to server preferred address, !success)
- << "Failed to migrate to server preferred address: "
- << context->peer_address() << " after successful validation";
- }
-
- void OnPathValidationFailure(
- std::unique_ptr<QuicPathValidationContext> context) override {
- QUIC_DLOG(INFO) << "Failed to validate server preferred address : "
- << context->peer_address();
- connection_->mutable_stats().failed_to_validate_server_preferred_address =
- true;
- connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false);
- }
-
- private:
- QuicConnection* connection_;
-};
-
} // namespace
#define ENDPOINT \
@@ -706,13 +669,12 @@
config.HasReceivedIPv6AlternateServerAddress()) {
server_preferred_address_ = config.ReceivedIPv6AlternateServerAddress();
}
- AddKnownServerAddress(server_preferred_address_);
if (server_preferred_address_.IsInitialized()) {
QUICHE_DLOG(INFO) << ENDPOINT << "Received server preferred address: "
<< server_preferred_address_;
if (config.HasClientRequestedIndependentOption(kSPA2, perspective_)) {
accelerated_server_preferred_address_ = true;
- ValidateServerPreferredAddress();
+ visitor_->OnServerPreferredAddressAvailable(server_preferred_address_);
}
}
}
@@ -4005,7 +3967,7 @@
if (!accelerated_server_preferred_address_ &&
server_preferred_address_.IsInitialized()) {
QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
- ValidateServerPreferredAddress();
+ visitor_->OnServerPreferredAddressAvailable(server_preferred_address_);
}
}
@@ -6428,21 +6390,6 @@
->MaybeIssueNewConnectionIdForPreferredAddress();
}
-void QuicConnection::ValidateServerPreferredAddress() {
- QUICHE_DCHECK(server_preferred_address_.IsInitialized());
- // Validate received server preferred address.
- auto context = visitor_->CreatePathValidationContextForServerPreferredAddress(
- server_preferred_address_);
- if (context == nullptr) {
- return;
- }
- QUICHE_DLOG(INFO) << ENDPOINT << "Start validating server preferred address: "
- << server_preferred_address_;
- auto result_delegate =
- std::make_unique<ServerPreferredAddressResultDelegate>(this);
- ValidatePath(std::move(context), std::move(result_delegate));
-}
-
bool QuicConnection::ShouldDetectBlackhole() const {
if (!connected_ || blackhole_detection_disabled_) {
return false;
@@ -6564,6 +6511,13 @@
default_path_.stateless_reset_token);
}
if (path_validator_.HasPendingPathValidation()) {
+ if (perspective_ == Perspective::IS_CLIENT &&
+ IsValidatingServerPreferredAddress()) {
+ QUIC_CLIENT_HISTOGRAM_BOOL(
+ "QuicSession.ServerPreferredAddressValidationCancelled", true,
+ "How often the caller kicked off another validation while there is "
+ "an on-going server preferred address validation.");
+ }
// Cancel and fail any earlier validation.
path_validator_.CancelPathValidation();
}
@@ -6603,6 +6557,10 @@
}
path_validator_.StartPathValidation(std::move(context),
std::move(result_delegate));
+ if (perspective_ == Perspective::IS_CLIENT &&
+ IsValidatingServerPreferredAddress()) {
+ AddKnownServerAddress(server_preferred_address_);
+ }
}
bool QuicConnection::SendPathResponse(
@@ -6682,7 +6640,7 @@
path_validator_.CancelPathValidation();
}
-bool QuicConnection::UpdateConnectionIdsOnClientMigration(
+bool QuicConnection::UpdateConnectionIdsOnMigration(
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
@@ -6765,7 +6723,7 @@
QUICHE_DCHECK(!version().UsesHttp3() || IsHandshakeConfirmed());
if (connection_migration_use_new_cid_) {
- if (!UpdateConnectionIdsOnClientMigration(self_address, peer_address)) {
+ if (!UpdateConnectionIdsOnMigration(self_address, peer_address)) {
if (owns_writer) {
delete writer;
}
@@ -6800,7 +6758,8 @@
return true;
}
-void QuicConnection::OnPathValidationFailureAtClient(bool is_multi_port) {
+void QuicConnection::OnPathValidationFailureAtClient(
+ bool is_multi_port, const QuicPathValidationContext& context) {
if (connection_migration_use_new_cid_) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
alternative_path_.Clear();
@@ -6815,6 +6774,13 @@
}
}
+ if (context.peer_address() == server_preferred_address_ &&
+ server_preferred_address_ != default_path_.peer_address) {
+ QUIC_DLOG(INFO) << "Failed to validate server preferred address : "
+ << server_preferred_address_;
+ mutable_stats().failed_to_validate_server_preferred_address = true;
+ }
+
RetirePeerIssuedConnectionIdsOnPathValidationFailure();
}
@@ -7072,8 +7038,9 @@
void QuicConnection::MultiPortPathValidationResultDelegate::
OnPathValidationFailure(
- std::unique_ptr<QuicPathValidationContext> /*context*/) {
- connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/true);
+ std::unique_ptr<QuicPathValidationContext> context) {
+ connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/true,
+ *context);
}
QuicConnection::ReversePathValidationResultDelegate::
@@ -7231,5 +7198,29 @@
retransmittable_on_wire_timeout);
}
+bool QuicConnection::IsValidatingServerPreferredAddress() const {
+ QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
+ return server_preferred_address_.IsInitialized() &&
+ server_preferred_address_ != default_path_.peer_address &&
+ path_validator_.HasPendingPathValidation() &&
+ path_validator_.GetContext()->peer_address() ==
+ server_preferred_address_;
+}
+
+void QuicConnection::OnServerPreferredAddressValidated(
+ QuicPathValidationContext& context, bool owns_writer) {
+ QUIC_DLOG(INFO) << "Server preferred address: " << context.peer_address()
+ << " validated. Migrating path, self_address: "
+ << context.self_address()
+ << ", peer_address: " << context.peer_address();
+ mutable_stats().server_preferred_address_validated = true;
+ const bool success =
+ MigratePath(context.self_address(), context.peer_address(),
+ context.WriterToUse(), owns_writer);
+ QUIC_BUG_IF(failed to migrate to server preferred address, !success)
+ << "Failed to migrate to server preferred address: "
+ << context.peer_address() << " after successful validation";
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index ca25805..5c06949 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -241,9 +241,8 @@
virtual std::unique_ptr<QuicPathValidationContext>
CreateContextForMultiPortPath() = 0;
- // Creates context to validate server preferred address.
- virtual std::unique_ptr<QuicPathValidationContext>
- CreatePathValidationContextForServerPreferredAddress(
+ // Called when the client receives a preferred address from its peer.
+ virtual void OnServerPreferredAddressAvailable(
const QuicSocketAddress& server_preferred_address) = 0;
};
@@ -1219,7 +1218,8 @@
// Called to clear the alternative_path_ when path validation failed on the
// client side.
- void OnPathValidationFailureAtClient(bool is_multi_port);
+ void OnPathValidationFailureAtClient(
+ bool is_multi_port, const QuicPathValidationContext& context);
void SetSourceAddressTokenToSend(absl::string_view token);
@@ -1275,6 +1275,16 @@
// Kicks off validation of received server preferred address.
void ValidateServerPreferredAddress();
+ // Returns true if the client is validating the server preferred address which
+ // hasn't been used before.
+ bool IsValidatingServerPreferredAddress() const;
+
+ // Called by client to start sending packets to the preferred address.
+ // If |owns_writer| is true, the ownership of the writer in the |context| is
+ // also passed in.
+ void OnServerPreferredAddressValidated(QuicPathValidationContext& context,
+ bool owns_writer);
+
protected:
// Calls cancel() on all the alarms owned by this connection.
void CancelAllAlarms();
@@ -1604,11 +1614,11 @@
// Returns true if header contains valid server connection ID.
bool ValidateServerConnectionId(const QuicPacketHeader& header) const;
- // Update the connection IDs when client migrates with/without validation.
+ // Update the connection IDs when client migrates its own address
+ // (with/without validation) or switches to server preferred address.
// Returns false if required connection ID is not available.
- bool UpdateConnectionIdsOnClientMigration(
- const QuicSocketAddress& self_address,
- const QuicSocketAddress& peer_address);
+ bool UpdateConnectionIdsOnMigration(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address);
// Retire active peer issued connection IDs after they are no longer used on
// any path.
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 874eeb1..e1e318b 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -102,6 +102,13 @@
const QuicSocketAddress kSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(),
/*port=*/443);
+const QuicSocketAddress kServerPreferredAddress = QuicSocketAddress(
+ []() {
+ QuicIpAddress address;
+ address.FromString("2604:31c0::");
+ return address;
+ }(),
+ /*port=*/443);
QuicStreamId GetNthClientInitiatedStreamId(int n,
QuicTransportVersion version) {
@@ -1468,9 +1475,6 @@
ASSERT_TRUE(version().HasIetfQuicFrames());
ASSERT_TRUE(connection_.self_address().host().IsIPv6());
SetQuicReloadableFlag(quic_connection_migration_use_new_cid_v2, true);
- QuicIpAddress host;
- host.FromString("2604:31c0::");
- QuicSocketAddress server_preferred_address(host, 443);
const QuicConnectionId connection_id = TestConnectionId(17);
const StatelessResetToken reset_token =
QuicUtils::GenerateStatelessResetToken(connection_id);
@@ -1491,7 +1495,7 @@
QuicConfigPeer::SetReceivedStatelessResetToken(&config,
kTestStatelessResetToken);
QuicConfigPeer::SetReceivedAlternateServerAddress(&config,
- server_preferred_address);
+ kServerPreferredAddress);
QuicConfigPeer::SetPreferredAddressConnectionIdAndToken(
&config, connection_id, reset_token);
EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
@@ -1499,7 +1503,7 @@
ASSERT_TRUE(QuicConnectionPeer::GetServerPreferredAddress(&connection_)
.IsInitialized());
- EXPECT_EQ(server_preferred_address,
+ EXPECT_EQ(kServerPreferredAddress,
QuicConnectionPeer::GetServerPreferredAddress(&connection_));
}
@@ -2514,7 +2518,8 @@
EXPECT_EQ(expected_self_address_, context->self_address());
EXPECT_EQ(expected_peer_address_, context->peer_address());
if (connection_->perspective() == Perspective::IS_CLIENT) {
- connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false);
+ connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false,
+ *context);
}
*success_ = false;
}
@@ -2526,6 +2531,32 @@
bool* success_;
};
+// A test implementation which migrates to server preferred address
+// on path validation suceeds. Otherwise, client cleans up alternative path.
+class QUICHE_EXPORT ServerPreferredAddressTestResultDelegate
+ : public QuicPathValidator::ResultDelegate {
+ public:
+ explicit ServerPreferredAddressTestResultDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ void OnPathValidationSuccess(
+ std::unique_ptr<QuicPathValidationContext> context,
+ QuicTime /*start_time*/) override {
+ connection_->OnServerPreferredAddressValidated(*context, false);
+ }
+
+ void OnPathValidationFailure(
+ std::unique_ptr<QuicPathValidationContext> context) override {
+ connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false,
+ *context);
+ }
+
+ protected:
+ QuicConnection* connection() { return connection_; }
+
+ private:
+ QuicConnection* connection_;
+};
+
// Receive a path probe request at the server side, i.e.,
// in non-IETF version: receive a padded PING packet with a peer addess change;
// in IETF version: receive a packet contains PATH CHALLENGE with peer address
@@ -2946,6 +2977,53 @@
EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
}
+TEST_P(QuicConnectionTest,
+ PeerAddressChangesToPreferredAddressBeforeClientInitiates) {
+ if (!version().HasIetfQuicFrames()) {
+ return;
+ }
+ ASSERT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+ ASSERT_TRUE(connection_.self_address().host().IsIPv6());
+ SetQuicReloadableFlag(quic_connection_migration_use_new_cid_v2, true);
+ const QuicConnectionId connection_id = TestConnectionId(17);
+ const StatelessResetToken reset_token =
+ QuicUtils::GenerateStatelessResetToken(connection_id);
+
+ connection_.CreateConnectionIdManager();
+
+ connection_.SendCryptoStreamData();
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1);
+ // Received ACK for packet 1.
+ ProcessFramePacketAtLevel(1, QuicFrame(&frame), ENCRYPTION_INITIAL);
+ // Discard INITIAL key.
+ connection_.RemoveEncrypter(ENCRYPTION_INITIAL);
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+
+ QuicConfig config;
+ config.SetClientConnectionOptions(QuicTagVector{kSPAD});
+ config.SetConnectionOptionsToSend(QuicTagVector{kRVCM});
+ QuicConfigPeer::SetReceivedStatelessResetToken(&config,
+ kTestStatelessResetToken);
+ QuicConfigPeer::SetReceivedAlternateServerAddress(&config,
+ kServerPreferredAddress);
+ QuicConfigPeer::SetPreferredAddressConnectionIdAndToken(
+ &config, connection_id, reset_token);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+ ASSERT_TRUE(QuicConnectionPeer::GetServerPreferredAddress(&connection_)
+ .IsInitialized());
+ EXPECT_EQ(kServerPreferredAddress,
+ QuicConnectionPeer::GetServerPreferredAddress(&connection_));
+
+ EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(0);
+ ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress,
+ kServerPreferredAddress, ENCRYPTION_INITIAL);
+}
+
TEST_P(QuicConnectionTest, MaxPacketSize) {
EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
EXPECT_EQ(1250u, connection_.max_packet_length());
@@ -15989,8 +16067,6 @@
QuicConfig config;
config.SetClientConnectionOptions(QuicTagVector{kSPAD});
ServerPreferredAddressInit(config);
- const QuicSocketAddress kServerPreferredAddress =
- QuicConnectionPeer::GetServerPreferredAddress(&connection_);
const QuicSocketAddress kNewSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
@@ -16001,11 +16077,15 @@
.WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
// Kick off path validation of server preferred address on handshake
// confirmed.
- EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress(
- kServerPreferredAddress))
- .WillOnce(Return(
- testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
- kNewSelfAddress, kServerPreferredAddress, &new_writer))));
+ EXPECT_CALL(visitor_,
+ OnServerPreferredAddressAvailable(kServerPreferredAddress))
+ .WillOnce(Invoke([&]() {
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, kServerPreferredAddress, &new_writer),
+ std::make_unique<ServerPreferredAddressTestResultDelegate>(
+ &connection_));
+ }));
connection_.OnHandshakeComplete();
EXPECT_TRUE(connection_.HasPendingPathValidation());
EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -16027,7 +16107,7 @@
EXPECT_TRUE(connection_.IsValidStatelessResetToken(kTestStatelessResetToken));
EXPECT_FALSE(connection_.IsValidStatelessResetToken(kNewStatelessResetToken));
- // Receive path challenge from server preferred address.
+ // Receive path response from server preferred address.
QuicFrames frames;
frames.push_back(QuicFrame(QuicPathResponseFrame(99, payload)));
// Verify send_algorithm gets reset after migration (new sent packet is not
@@ -16069,8 +16149,6 @@
QuicConfig config;
config.SetClientConnectionOptions(QuicTagVector{kSPAD});
ServerPreferredAddressInit(config);
- const QuicSocketAddress kServerPreferredAddress =
- QuicConnectionPeer::GetServerPreferredAddress(&connection_);
const QuicSocketAddress kNewSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
@@ -16079,11 +16157,15 @@
.WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
// Kick off path validation of server preferred address on handshake
// confirmed.
- EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress(
- kServerPreferredAddress))
- .WillOnce(Return(
- testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
- kNewSelfAddress, kServerPreferredAddress, &new_writer))));
+ EXPECT_CALL(visitor_,
+ OnServerPreferredAddressAvailable(kServerPreferredAddress))
+ .WillOnce(Invoke([&]() {
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, kServerPreferredAddress, &new_writer),
+ std::make_unique<ServerPreferredAddressTestResultDelegate>(
+ &connection_));
+ }));
connection_.OnHandshakeComplete();
EXPECT_TRUE(connection_.HasPendingPathValidation());
ASSERT_FALSE(new_writer.path_challenge_frames().empty());
@@ -16096,7 +16178,7 @@
writer_->last_packet_header().destination_connection_id);
EXPECT_EQ(kPeerAddress, writer_->last_write_peer_address());
- // Receive path challenge from original server address.
+ // Receive path response from original server address.
QuicFrames frames;
frames.push_back(QuicFrame(QuicPathResponseFrame(99, payload)));
ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, kPeerAddress,
@@ -16135,8 +16217,6 @@
QuicConfig config;
config.SetClientConnectionOptions(QuicTagVector{kSPAD});
ServerPreferredAddressInit(config);
- const QuicSocketAddress kServerPreferredAddress =
- QuicConnectionPeer::GetServerPreferredAddress(&connection_);
const QuicSocketAddress kNewSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
@@ -16145,13 +16225,17 @@
.WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
// Kick off path validation of server preferred address on handshake
// confirmed.
- EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress(
- kServerPreferredAddress))
- .WillOnce(Return(
- testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
- kNewSelfAddress, kServerPreferredAddress, &new_writer))));
+ EXPECT_CALL(visitor_,
+ OnServerPreferredAddressAvailable(kServerPreferredAddress))
+ .WillOnce(Invoke([&]() {
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, kServerPreferredAddress, &new_writer),
+ std::make_unique<ServerPreferredAddressTestResultDelegate>(
+ &connection_));
+ }));
connection_.OnHandshakeComplete();
- EXPECT_TRUE(connection_.HasPendingPathValidation());
+ EXPECT_TRUE(connection_.IsValidatingServerPreferredAddress());
EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
&connection_, kNewSelfAddress, kServerPreferredAddress));
ASSERT_FALSE(new_writer.path_challenge_frames().empty());
@@ -16200,17 +16284,18 @@
if (!connection_.version().HasIetfQuicFrames()) {
return;
}
- QuicIpAddress host;
- host.FromString("2604:31c0::");
- const QuicSocketAddress kServerPreferredAddress(host, 443);
const QuicSocketAddress kNewSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
- EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress(
- kServerPreferredAddress))
- .WillOnce(Return(
- testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
- kNewSelfAddress, kServerPreferredAddress, &new_writer))));
+ EXPECT_CALL(visitor_,
+ OnServerPreferredAddressAvailable(kServerPreferredAddress))
+ .WillOnce(Invoke([&]() {
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, kServerPreferredAddress, &new_writer),
+ std::make_unique<ServerPreferredAddressTestResultDelegate>(
+ &connection_));
+ }));
QuicConfig config;
config.SetClientConnectionOptions(QuicTagVector{kSPAD, kSPA2});
ServerPreferredAddressInit(config);
@@ -16236,17 +16321,18 @@
if (!connection_.version().HasIetfQuicFrames()) {
return;
}
- QuicIpAddress host;
- host.FromString("2604:31c0::");
- const QuicSocketAddress kServerPreferredAddress(host, 443);
const QuicSocketAddress kNewSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
- EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress(
- kServerPreferredAddress))
- .WillOnce(Return(
- testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
- kNewSelfAddress, kServerPreferredAddress, &new_writer))));
+ EXPECT_CALL(visitor_,
+ OnServerPreferredAddressAvailable(kServerPreferredAddress))
+ .WillOnce(Invoke([&]() {
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, kServerPreferredAddress, &new_writer),
+ std::make_unique<ServerPreferredAddressTestResultDelegate>(
+ &connection_));
+ }));
QuicConfig config;
config.SetClientConnectionOptions(QuicTagVector{kSPAD, kSPA2});
ServerPreferredAddressInit(config);
@@ -16278,17 +16364,18 @@
if (!connection_.version().HasIetfQuicFrames()) {
return;
}
- QuicIpAddress host;
- host.FromString("2604:31c0::");
- const QuicSocketAddress kServerPreferredAddress(host, 443);
const QuicSocketAddress kNewSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
- EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress(
- kServerPreferredAddress))
- .WillOnce(Return(
- testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
- kNewSelfAddress, kServerPreferredAddress, &new_writer))));
+ EXPECT_CALL(visitor_,
+ OnServerPreferredAddressAvailable(kServerPreferredAddress))
+ .WillOnce(Invoke([&]() {
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, kServerPreferredAddress, &new_writer),
+ std::make_unique<ServerPreferredAddressTestResultDelegate>(
+ &connection_));
+ }));
QuicConfig config;
config.SetClientConnectionOptions(QuicTagVector{kSPAD, kSPA2});
ServerPreferredAddressInit(config);
@@ -16318,6 +16405,96 @@
EXPECT_TRUE(new_writer.ping_frames().empty());
}
+TEST_P(QuicConnectionTest, MultiPortCreationAfterServerMigration) {
+ if (!GetParam().version.HasIetfQuicFrames()) {
+ return;
+ }
+ QuicConfig config;
+ config.SetClientConnectionOptions(QuicTagVector{kMPQC, kSPAD});
+ ServerPreferredAddressInit(config);
+ if (!connection_.connection_migration_use_new_cid()) {
+ return;
+ }
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicConnectionId cid_for_preferred_address = TestConnectionId(17);
+ const QuicSocketAddress kNewSelfAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+ EXPECT_CALL(visitor_,
+ OnServerPreferredAddressAvailable(kServerPreferredAddress))
+ .WillOnce(Invoke([&]() {
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, kServerPreferredAddress, &new_writer),
+ std::make_unique<ServerPreferredAddressTestResultDelegate>(
+ &connection_));
+ }));
+ // The connection should start probing the preferred address after handshake
+ // confirmed.
+ QuicPathFrameBuffer payload;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(testing::AtLeast(1u))
+ .WillOnce(Invoke([&]() {
+ EXPECT_EQ(1u, new_writer.path_challenge_frames().size());
+ payload = new_writer.path_challenge_frames().front().data_buffer;
+ EXPECT_EQ(kServerPreferredAddress,
+ new_writer.last_write_peer_address());
+ }));
+ EXPECT_CALL(visitor_, GetHandshakeState())
+ .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
+ connection_.OnHandshakeComplete();
+ EXPECT_TRUE(connection_.IsValidatingServerPreferredAddress());
+
+ // Receiving PATH_RESPONSE should cause the connection to migrate to the
+ // preferred address.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(QuicPathResponseFrame(99, payload)));
+ ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, kPeerAddress,
+ ENCRYPTION_FORWARD_SECURE);
+ EXPECT_FALSE(connection_.IsValidatingServerPreferredAddress());
+ EXPECT_EQ(kServerPreferredAddress, connection_.effective_peer_address());
+ EXPECT_EQ(kNewSelfAddress, connection_.self_address());
+ EXPECT_EQ(connection_.connection_id(), cid_for_preferred_address);
+
+ // As the default path changed, the server issued CID 1 should be retired.
+ auto* retire_peer_issued_cid_alarm =
+ connection_.GetRetirePeerIssuedConnectionIdAlarm();
+ ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet());
+ EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u));
+ retire_peer_issued_cid_alarm->Fire();
+
+ const QuicSocketAddress kNewSelfAddress2(kNewSelfAddress.host(),
+ kNewSelfAddress.port() + 1);
+ EXPECT_NE(kNewSelfAddress2, kNewSelfAddress);
+ TestPacketWriter new_writer2(version(), &clock_, Perspective::IS_CLIENT);
+ QuicNewConnectionIdFrame frame;
+ frame.connection_id = TestConnectionId(789);
+ ASSERT_NE(frame.connection_id, connection_.connection_id());
+ frame.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+ frame.retire_prior_to = 0u;
+ frame.sequence_number = 2u;
+ EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
+ .WillOnce(Return(
+ testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress2, connection_.peer_address(), &new_writer2))));
+ connection_.OnNewConnectionIdFrame(frame);
+ EXPECT_TRUE(connection_.HasPendingPathValidation());
+ EXPECT_EQ(1u, new_writer.path_challenge_frames().size());
+ payload = new_writer.path_challenge_frames().front().data_buffer;
+ EXPECT_EQ(kServerPreferredAddress, new_writer.last_write_peer_address());
+ EXPECT_EQ(kNewSelfAddress2.host(), new_writer.last_write_source_address());
+ EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress2, connection_.peer_address()));
+ auto* alt_path = QuicConnectionPeer::GetAlternativePath(&connection_);
+ EXPECT_FALSE(alt_path->validated);
+ QuicFrames frames2;
+ frames2.push_back(QuicFrame(QuicPathResponseFrame(99, payload)));
+ ProcessFramesPacketWithAddresses(frames2, kNewSelfAddress2, kPeerAddress,
+ ENCRYPTION_FORWARD_SECURE);
+ EXPECT_TRUE(alt_path->validated);
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h
index 6576325..bf9531a 100644
--- a/quiche/quic/core/quic_session.h
+++ b/quiche/quic/core/quic_session.h
@@ -179,12 +179,8 @@
override {
return nullptr;
}
-
- std::unique_ptr<QuicPathValidationContext>
- CreatePathValidationContextForServerPreferredAddress(
- const QuicSocketAddress& /*server_preferred_address*/) override {
- return nullptr;
- }
+ void OnServerPreferredAddressAvailable(
+ const QuicSocketAddress& /*server_preferred_address*/) override {}
// QuicStreamFrameDataProducer
WriteStreamDataResult WriteStreamData(QuicStreamId id,
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index f2e38f5..d8271e1 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -502,10 +502,8 @@
MOCK_METHOD(bool, MaybeSendAddressToken, (), (override));
MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>,
CreateContextForMultiPortPath, (), (override));
- MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>,
- CreatePathValidationContextForServerPreferredAddress,
+ MOCK_METHOD(void, OnServerPreferredAddressAvailable,
(const QuicSocketAddress&), (override));
-
void OnBandwidthUpdateTimeout() override {}
};
diff --git a/quiche/quic/test_tools/simulator/quic_endpoint.h b/quiche/quic/test_tools/simulator/quic_endpoint.h
index d41bef3..1bb9ce0 100644
--- a/quiche/quic/test_tools/simulator/quic_endpoint.h
+++ b/quiche/quic/test_tools/simulator/quic_endpoint.h
@@ -109,11 +109,8 @@
override {
return nullptr;
}
- std::unique_ptr<QuicPathValidationContext>
- CreatePathValidationContextForServerPreferredAddress(
- const QuicSocketAddress& /*server_preferred_address*/) override {
- return nullptr;
- }
+ void OnServerPreferredAddressAvailable(
+ const QuicSocketAddress& /*server_preferred_address*/) override {}
// End QuicConnectionVisitorInterface implementation.
diff --git a/quiche/quic/tools/quic_client_base.cc b/quiche/quic/tools/quic_client_base.cc
index 948707a..de77089 100644
--- a/quiche/quic/tools/quic_client_base.cc
+++ b/quiche/quic/tools/quic_client_base.cc
@@ -50,7 +50,7 @@
QUIC_LOG(WARNING) << "Fail to validate path " << *context
<< ", stop migrating.";
client_->session()->connection()->OnPathValidationFailureAtClient(
- /*is_multi_port=*/false);
+ /*is_multi_port=*/false, *context);
}
private:
@@ -469,7 +469,7 @@
QUIC_LOG(WARNING) << "Fail to validate path " << *context
<< ", stop migrating.";
client_->session()->connection()->OnPathValidationFailureAtClient(
- /*is_multi_port=*/false);
+ /*is_multi_port=*/false, *context);
}
private:
diff --git a/quiche/quic/tools/quic_simple_client_session.cc b/quiche/quic/tools/quic_simple_client_session.cc
index c1e1b2a..9de95d8 100644
--- a/quiche/quic/tools/quic_simple_client_session.cc
+++ b/quiche/quic/tools/quic_simple_client_session.cc
@@ -6,8 +6,39 @@
#include <utility>
+#include "quiche/quic/core/quic_path_validator.h"
+
namespace quic {
+namespace {
+
+class QUICHE_EXPORT ServerPreferredAddressResultDelegateWithWriter
+ : public QuicPathValidator::ResultDelegate {
+ public:
+ explicit ServerPreferredAddressResultDelegateWithWriter(
+ QuicConnection* connection)
+ : connection_(connection) {}
+
+ // Overridden to transfer the ownership of the new writer.
+ void OnPathValidationSuccess(
+ std::unique_ptr<QuicPathValidationContext> /*context*/,
+ QuicTime /*start_time*/) override {
+ // TODO(danzh) hand over the ownership of the released writer to client and
+ // call the connection to migrate.
+ }
+
+ void OnPathValidationFailure(
+ std::unique_ptr<QuicPathValidationContext> context) override {
+ connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false,
+ *context);
+ }
+
+ private:
+ QuicConnection* connection_ = nullptr;
+};
+
+} // namespace
+
QuicSimpleClientSession::QuicSimpleClientSession(
const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
QuicConnection* connection, QuicClientBase::NetworkHelper* network_helper,
@@ -56,22 +87,24 @@
network_helper_->GetLatestClientAddress(), peer_address());
}
-std::unique_ptr<QuicPathValidationContext>
-QuicSimpleClientSession::CreatePathValidationContextForServerPreferredAddress(
+void QuicSimpleClientSession::OnServerPreferredAddressAvailable(
const QuicSocketAddress& server_preferred_address) {
const auto self_address = connection()->self_address();
if (network_helper_ == nullptr ||
!network_helper_->CreateUDPSocketAndBind(server_preferred_address,
self_address.host(), 0)) {
- return nullptr;
+ return;
}
QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
if (writer == nullptr) {
- return nullptr;
+ return;
}
- return std::make_unique<PathMigrationContext>(
- std::unique_ptr<QuicPacketWriter>(writer),
- network_helper_->GetLatestClientAddress(), server_preferred_address);
+ connection()->ValidatePath(
+ std::make_unique<PathMigrationContext>(
+ std::unique_ptr<QuicPacketWriter>(writer),
+ network_helper_->GetLatestClientAddress(), server_preferred_address),
+ std::make_unique<ServerPreferredAddressResultDelegateWithWriter>(
+ connection()));
}
} // namespace quic
diff --git a/quiche/quic/tools/quic_simple_client_session.h b/quiche/quic/tools/quic_simple_client_session.h
index 631b36b..4e06cc2 100644
--- a/quiche/quic/tools/quic_simple_client_session.h
+++ b/quiche/quic/tools/quic_simple_client_session.h
@@ -27,8 +27,7 @@
HttpDatagramSupport LocalHttpDatagramSupport() override;
std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
override;
- std::unique_ptr<QuicPathValidationContext>
- CreatePathValidationContextForServerPreferredAddress(
+ void OnServerPreferredAddressAvailable(
const QuicSocketAddress& server_preferred_address) override;
bool drop_response_body() const { return drop_response_body_; }