Move the creation of server preferred address path validation context from connection to session.
Also removed SetIPv(4|6)AlternateServerAddressToSend that sets connection IDs. Note for chromium merge:
Please replace SetIPv(4|6)AlternateServerAddressToSend(address, cid, token) with:
SetIPv(4|6)AlternateServerAddressToSend(address);
SetPreferredAddressConnectionIdAndTokenToSend(cid, token);
PiperOrigin-RevId: 498064870
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index b889402..f5af621 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -506,8 +506,11 @@
server_supported_versions_, &memory_cache_backend_,
expected_server_connection_id_length_);
test_server->SetEventLoopFactory(GetParam().event_loop);
- server_thread_ =
- std::make_unique<ServerThread>(std::move(test_server), server_address_);
+ const QuicSocketAddress server_listening_address =
+ server_listening_address_.has_value() ? *server_listening_address_
+ : server_address_;
+ server_thread_ = std::make_unique<ServerThread>(std::move(test_server),
+ server_listening_address);
if (chlo_multiplier_ != 0) {
server_thread_->server()->SetChloMultiplier(chlo_multiplier_);
}
@@ -885,6 +888,7 @@
// Default is true.
bool connect_to_server_on_initialize_;
QuicSocketAddress server_address_;
+ absl::optional<QuicSocketAddress> server_listening_address_;
std::string server_hostname_;
QuicTestBackend memory_cache_backend_;
std::unique_ptr<ServerThread> server_thread_;
@@ -5468,6 +5472,36 @@
stream->Reset(QuicRstStreamErrorCode::QUIC_STREAM_NO_ERROR);
}
+TEST_P(EndToEndTest, SimpleServerPreferredAddressTest) {
+ const QuicSocketAddress kServerPreferredAddress(TestLoopback(1), 443);
+ server_address_ = QuicSocketAddress(TestLoopback(2), 443);
+ ASSERT_NE(kServerPreferredAddress, server_address_);
+ // Send server preferred address and let server listen on Any.
+ if (kServerPreferredAddress.host().IsIPv4()) {
+ server_listening_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 443);
+ server_config_.SetIPv4AlternateServerAddressToSend(kServerPreferredAddress);
+ } else {
+ server_listening_address_ = QuicSocketAddress(QuicIpAddress::Any6(), 443);
+ server_config_.SetIPv6AlternateServerAddressToSend(kServerPreferredAddress);
+ }
+ ASSERT_TRUE(Initialize());
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
+ return;
+ }
+ client_config_.SetConnectionOptionsToSend(QuicTagVector{kRVCM});
+ client_config_.SetClientConnectionOptions(QuicTagVector{kSPAD});
+ client_.reset(CreateQuicClient(nullptr));
+ EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
+ while (client_->client()->HasPendingPathValidation()) {
+ client_->client()->WaitForEvents();
+ }
+ // TODO(b/262386897): Currently, server drops packets received on preferred
+ // address because self address change is disallowed.
+ const auto client_stats = GetClientConnection()->GetStats();
+ EXPECT_FALSE(client_stats.server_preferred_address_validated);
+ EXPECT_TRUE(client_stats.failed_to_validate_server_preferred_address);
+}
+
TEST_P(EndToEndPacketReorderingTest, ReorderedPathChallenge) {
ASSERT_TRUE(Initialize());
if (!version_.HasIetfQuicFrames()) {
diff --git a/quiche/quic/core/quic_config.cc b/quiche/quic/core/quic_config.cc
index 161426d..f8895b0 100644
--- a/quiche/quic/core/quic_config.cc
+++ b/quiche/quic/core/quic_config.cc
@@ -859,21 +859,6 @@
alternate_server_address_ipv6_.SetSendValue(alternate_server_address_ipv6);
}
-void QuicConfig::SetIPv6AlternateServerAddressToSend(
- const QuicSocketAddress& alternate_server_address_ipv6,
- const QuicConnectionId& connection_id,
- const StatelessResetToken& stateless_reset_token) {
- if (!alternate_server_address_ipv6.host().IsIPv6()) {
- QUIC_BUG(quic_bug_10575_10)
- << "Cannot use SetIPv6AlternateServerAddressToSend with "
- << alternate_server_address_ipv6;
- return;
- }
- alternate_server_address_ipv6_.SetSendValue(alternate_server_address_ipv6);
- preferred_address_connection_id_and_token_ =
- std::make_pair(connection_id, stateless_reset_token);
-}
-
bool QuicConfig::HasReceivedIPv6AlternateServerAddress() const {
return alternate_server_address_ipv6_.HasReceivedValue();
}
@@ -894,21 +879,6 @@
alternate_server_address_ipv4_.SetSendValue(alternate_server_address_ipv4);
}
-void QuicConfig::SetIPv4AlternateServerAddressToSend(
- const QuicSocketAddress& alternate_server_address_ipv4,
- const QuicConnectionId& connection_id,
- const StatelessResetToken& stateless_reset_token) {
- if (!alternate_server_address_ipv4.host().IsIPv4()) {
- QUIC_BUG(quic_bug_10575_12)
- << "Cannot use SetIPv4AlternateServerAddressToSend with "
- << alternate_server_address_ipv4;
- return;
- }
- alternate_server_address_ipv4_.SetSendValue(alternate_server_address_ipv4);
- preferred_address_connection_id_and_token_ =
- std::make_pair(connection_id, stateless_reset_token);
-}
-
bool QuicConfig::HasReceivedIPv4AlternateServerAddress() const {
return alternate_server_address_ipv4_.HasReceivedValue();
}
@@ -918,6 +888,26 @@
return alternate_server_address_ipv4_.GetReceivedValue();
}
+void QuicConfig::SetPreferredAddressConnectionIdAndTokenToSend(
+ const QuicConnectionId& connection_id,
+ const StatelessResetToken& stateless_reset_token) {
+ if (!CanSendPreferredAddressConnectionIdAndToken()) {
+ QUIC_BUG(quic_bug_10575_17)
+ << "Can not send connection ID and token for preferred address";
+ return;
+ }
+ preferred_address_connection_id_and_token_ =
+ std::make_pair(connection_id, stateless_reset_token);
+}
+
+bool QuicConfig::CanSendPreferredAddressConnectionIdAndToken() const {
+ if (!alternate_server_address_ipv4_.HasSendValue() &&
+ !alternate_server_address_ipv6_.HasSendValue()) {
+ return false;
+ }
+ return !preferred_address_connection_id_and_token_.has_value();
+}
+
bool QuicConfig::HasReceivedPreferredAddressConnectionIdAndToken() const {
return (HasReceivedIPv6AlternateServerAddress() ||
HasReceivedIPv4AlternateServerAddress()) &&
diff --git a/quiche/quic/core/quic_config.h b/quiche/quic/core/quic_config.h
index 241fe84..52cc3b4 100644
--- a/quiche/quic/core/quic_config.h
+++ b/quiche/quic/core/quic_config.h
@@ -390,23 +390,27 @@
// IPv6 alternate server address.
void SetIPv6AlternateServerAddressToSend(
const QuicSocketAddress& alternate_server_address_ipv6);
- void SetIPv6AlternateServerAddressToSend(
- const QuicSocketAddress& alternate_server_address_ipv6,
- const QuicConnectionId& connection_id,
- const StatelessResetToken& stateless_reset_token);
bool HasReceivedIPv6AlternateServerAddress() const;
const QuicSocketAddress& ReceivedIPv6AlternateServerAddress() const;
// IPv4 alternate server address.
void SetIPv4AlternateServerAddressToSend(
const QuicSocketAddress& alternate_server_address_ipv4);
- void SetIPv4AlternateServerAddressToSend(
- const QuicSocketAddress& alternate_server_address_ipv4,
- const QuicConnectionId& connection_id,
- const StatelessResetToken& stateless_reset_token);
bool HasReceivedIPv4AlternateServerAddress() const;
const QuicSocketAddress& ReceivedIPv4AlternateServerAddress() const;
+ // Called to set |connection_id| and |stateless_reset_token| if server
+ // preferred address has been set via SetIPv(4|6)AlternateServerAddressToSend.
+ // Please note, this is different from SetStatelessResetTokenToSend(const
+ // StatelessResetToken&) which is used to send the token corresponding to the
+ // existing server_connection_id.
+ void SetPreferredAddressConnectionIdAndTokenToSend(
+ const QuicConnectionId& connection_id,
+ const StatelessResetToken& stateless_reset_token);
+ // Returns true if server preferred address has been set via
+ // SetIPv(4|6)AlternateServerAddressToSend.
+ bool CanSendPreferredAddressConnectionIdAndToken() const;
+
// Preferred Address Connection ID and Token.
bool HasReceivedPreferredAddressConnectionIdAndToken() const;
const std::pair<QuicConnectionId, StatelessResetToken>&
diff --git a/quiche/quic/core/quic_config_test.cc b/quiche/quic/core/quic_config_test.cc
index 244d967..495127c 100644
--- a/quiche/quic/core/quic_config_test.cc
+++ b/quiche/quic/core/quic_config_test.cc
@@ -482,8 +482,12 @@
QuicConnectionId new_connection_id = TestConnectionId(5);
StatelessResetToken new_stateless_reset_token =
QuicUtils::GenerateStatelessResetToken(new_connection_id);
- config_.SetIPv4AlternateServerAddressToSend(
- kTestServerAddress, new_connection_id, new_stateless_reset_token);
+ EXPECT_FALSE(config_.CanSendPreferredAddressConnectionIdAndToken());
+ config_.SetIPv4AlternateServerAddressToSend(kTestServerAddress);
+ ASSERT_TRUE(config_.CanSendPreferredAddressConnectionIdAndToken());
+ config_.SetPreferredAddressConnectionIdAndTokenToSend(
+ new_connection_id, new_stateless_reset_token);
+ EXPECT_FALSE(config_.CanSendPreferredAddressConnectionIdAndToken());
TransportParameters params;
config_.FillTransportParameters(¶ms);
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index d9f9f6c..2f5f56f 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -242,24 +242,6 @@
return false;
}
-// This context stores the path info from client address to server preferred
-// address while client is validating this address.
-class ServerPreferredAddressPathValidationContext
- : public QuicPathValidationContext {
- public:
- ServerPreferredAddressPathValidationContext(
- const QuicSocketAddress& server_preferred_address,
- QuicConnection* connection)
- : QuicPathValidationContext(connection->self_address(),
- server_preferred_address),
- connection_(connection) {}
-
- QuicPacketWriter* WriterToUse() override { return connection_->writer(); }
-
- private:
- QuicConnection* connection_;
-};
-
// Client migrates to server preferred address on path validation suceeds.
// Otherwise, client cleans up alternative path.
class ServerPreferredAddressResultDelegate
@@ -274,6 +256,7 @@
<< " 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(),
@@ -287,6 +270,8 @@
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);
}
@@ -721,6 +706,9 @@
config.HasReceivedIPv6AlternateServerAddress()) {
server_preferred_address_ = config.ReceivedIPv6AlternateServerAddress();
}
+ QUIC_DLOG_IF(INFO, server_preferred_address_.IsInitialized())
+ << ENDPOINT
+ << "Received server preferred address: " << server_preferred_address_;
AddKnownServerAddress(server_preferred_address_);
}
if (config.HasReceivedMaxPacketSize()) {
@@ -2763,7 +2751,8 @@
}
QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: "
<< packet.receipt_time().ToDebuggingValue() << " from peer "
- << last_received_packet_info_.source_address;
+ << last_received_packet_info_.source_address << ", to "
+ << last_received_packet_info_.destination_address;
ScopedPacketFlusher flusher(this);
if (!framer_.ProcessPacket(packet)) {
@@ -4012,11 +4001,16 @@
QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
// Validate received server preferred address.
auto context =
- std::make_unique<ServerPreferredAddressPathValidationContext>(
- server_preferred_address_, this);
- auto result_delegate =
- std::make_unique<ServerPreferredAddressResultDelegate>(this);
- ValidatePath(std::move(context), std::move(result_delegate));
+ visitor_->CreatePathValidationContextForServerPreferredAddress(
+ server_preferred_address_);
+ if (context != nullptr) {
+ 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));
+ }
}
}
@@ -6420,6 +6414,15 @@
known_server_addresses_.push_back(address);
}
+absl::optional<QuicNewConnectionIdFrame>
+QuicConnection::MaybeIssueNewConnectionIdForPreferredAddress() {
+ if (self_issued_cid_manager_ == nullptr) {
+ return absl::nullopt;
+ }
+ return self_issued_cid_manager_
+ ->MaybeIssueNewConnectionIdForPreferredAddress();
+}
+
bool QuicConnection::ShouldDetectBlackhole() const {
if (!connected_ || blackhole_detection_disabled_) {
return false;
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index a3eddff..cc22c19 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -240,6 +240,11 @@
// Returns context needed for the connection to probe on the alternative path.
virtual std::unique_ptr<QuicPathValidationContext>
CreateContextForMultiPortPath() = 0;
+
+ // Creates context to validate server preferred address.
+ virtual std::unique_ptr<QuicPathValidationContext>
+ CreatePathValidationContextForServerPreferredAddress(
+ const QuicSocketAddress& server_preferred_address) = 0;
};
// Interface which gets callbacks from the QuicConnection at interesting
@@ -1264,6 +1269,9 @@
void AddKnownServerAddress(const QuicSocketAddress& address);
+ absl::optional<QuicNewConnectionIdFrame>
+ MaybeIssueNewConnectionIdForPreferredAddress();
+
protected:
// Calls cancel() on all the alarms owned by this connection.
void CancelAllAlarms();
diff --git a/quiche/quic/core/quic_connection_stats.cc b/quiche/quic/core/quic_connection_stats.cc
index b5561eb..0321498 100644
--- a/quiche/quic/core/quic_connection_stats.cc
+++ b/quiche/quic/core/quic_connection_stats.cc
@@ -63,6 +63,10 @@
os << " address_validated_via_decrypting_packet: "
<< s.address_validated_via_decrypting_packet;
os << " address_validated_via_token: " << s.address_validated_via_token;
+ os << " server_preferred_address_validated: "
+ << s.server_preferred_address_validated;
+ os << " failed_to_validate_server_preferred_address: "
+ << s.failed_to_validate_server_preferred_address;
os << " }";
return os;
diff --git a/quiche/quic/core/quic_connection_stats.h b/quiche/quic/core/quic_connection_stats.h
index bf21073..94acd93 100644
--- a/quiche/quic/core/quic_connection_stats.h
+++ b/quiche/quic/core/quic_connection_stats.h
@@ -218,6 +218,9 @@
// Number of RETIRE_CONNECTION_ID frames sent.
size_t num_retire_connection_id_sent = 0;
+ bool server_preferred_address_validated = false;
+ bool failed_to_validate_server_preferred_address = false;
+
struct QUIC_NO_EXPORT TlsServerOperationStats {
bool success = false;
// If the operation is performed asynchronously, how long did it take.
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index ebc8891..314f491 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -1483,6 +1483,8 @@
QuicAckFrame frame = InitAckFrame(1);
// Received ACK for packet 1.
ProcessFramePacketAtLevel(1, QuicFrame(&frame), ENCRYPTION_INITIAL);
+ // Discard INITIAL key.
+ connection_.RemoveEncrypter(ENCRYPTION_INITIAL);
QuicConfig config;
config.SetConnectionOptionsToSend(QuicTagVector{kRVCM});
@@ -15988,6 +15990,9 @@
ServerPreferredAddressInit();
const QuicSocketAddress kServerPreferredAddress =
QuicConnectionPeer::GetServerPreferredAddress(&connection_);
+ const QuicSocketAddress kNewSelfAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
const StatelessResetToken kNewStatelessResetToken =
QuicUtils::GenerateStatelessResetToken(TestConnectionId(17));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
@@ -15995,17 +16000,25 @@
.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))));
connection_.OnHandshakeComplete();
EXPECT_TRUE(connection_.HasPendingPathValidation());
+ EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, kServerPreferredAddress));
EXPECT_EQ(TestConnectionId(17),
- writer_->last_packet_header().destination_connection_id);
- EXPECT_EQ(kServerPreferredAddress, writer_->last_write_peer_address());
+ new_writer.last_packet_header().destination_connection_id);
+ EXPECT_EQ(kServerPreferredAddress, new_writer.last_write_peer_address());
- ASSERT_FALSE(writer_->path_challenge_frames().empty());
+ ASSERT_FALSE(new_writer.path_challenge_frames().empty());
QuicPathFrameBuffer payload =
- writer_->path_challenge_frames().front().data_buffer;
+ new_writer.path_challenge_frames().front().data_buffer;
// Send data packet while path validation is pending.
connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ ASSERT_FALSE(writer_->stream_frames().empty());
// While path validation is pending, packet is sent on default path.
EXPECT_EQ(TestConnectionId(),
writer_->last_packet_header().destination_connection_id);
@@ -16019,13 +16032,17 @@
// Verify send_algorithm gets reset after migration (new sent packet is not
// updated to exsting send_algorithm_).
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
- ProcessFramesPacketWithAddresses(
- frames, kSelfAddress, kServerPreferredAddress, ENCRYPTION_FORWARD_SECURE);
+ ProcessFramesPacketWithAddresses(frames, kNewSelfAddress,
+ kServerPreferredAddress,
+ ENCRYPTION_FORWARD_SECURE);
ASSERT_FALSE(connection_.HasPendingPathValidation());
+ EXPECT_TRUE(QuicConnectionPeer::IsDefaultPath(&connection_, kNewSelfAddress,
+ kServerPreferredAddress));
+ ASSERT_FALSE(new_writer.stream_frames().empty());
// Verify stream data is retransmitted on new path.
EXPECT_EQ(TestConnectionId(17),
- writer_->last_packet_header().destination_connection_id);
- EXPECT_EQ(kServerPreferredAddress, writer_->last_write_peer_address());
+ new_writer.last_packet_header().destination_connection_id);
+ EXPECT_EQ(kServerPreferredAddress, new_writer.last_write_peer_address());
// Verify stateless reset token gets changed.
EXPECT_FALSE(
connection_.IsValidStatelessResetToken(kTestStatelessResetToken));
@@ -16037,6 +16054,9 @@
// Verify client retires connection ID with sequence number 0.
EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u));
retire_peer_issued_cid_alarm->Fire();
+ EXPECT_TRUE(connection_.GetStats().server_preferred_address_validated);
+ EXPECT_FALSE(
+ connection_.GetStats().failed_to_validate_server_preferred_address);
}
TEST_P(QuicConnectionTest, ClientValidatedServerPreferredAddress2) {
@@ -16048,18 +16068,27 @@
ServerPreferredAddressInit();
const QuicSocketAddress kServerPreferredAddress =
QuicConnectionPeer::GetServerPreferredAddress(&connection_);
+ const QuicSocketAddress kNewSelfAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
EXPECT_CALL(visitor_, GetHandshakeState())
.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))));
connection_.OnHandshakeComplete();
EXPECT_TRUE(connection_.HasPendingPathValidation());
- ASSERT_FALSE(writer_->path_challenge_frames().empty());
+ ASSERT_FALSE(new_writer.path_challenge_frames().empty());
QuicPathFrameBuffer payload =
- writer_->path_challenge_frames().front().data_buffer;
+ new_writer.path_challenge_frames().front().data_buffer;
// Send data packet while path validation is pending.
connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ ASSERT_FALSE(writer_->stream_frames().empty());
EXPECT_EQ(TestConnectionId(),
writer_->last_packet_header().destination_connection_id);
EXPECT_EQ(kPeerAddress, writer_->last_write_peer_address());
@@ -16067,13 +16096,14 @@
// Receive path challenge from original server address.
QuicFrames frames;
frames.push_back(QuicFrame(QuicPathResponseFrame(99, payload)));
- ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress,
+ ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, kPeerAddress,
ENCRYPTION_FORWARD_SECURE);
ASSERT_FALSE(connection_.HasPendingPathValidation());
+ ASSERT_FALSE(new_writer.stream_frames().empty());
// Verify stream data is retransmitted on new path.
EXPECT_EQ(TestConnectionId(17),
- writer_->last_packet_header().destination_connection_id);
- EXPECT_EQ(kServerPreferredAddress, writer_->last_write_peer_address());
+ new_writer.last_packet_header().destination_connection_id);
+ EXPECT_EQ(kServerPreferredAddress, new_writer.last_write_peer_address());
auto* retire_peer_issued_cid_alarm =
connection_.GetRetirePeerIssuedConnectionIdAlarm();
@@ -16088,6 +16118,9 @@
frames.push_back(QuicFrame(frame1_));
ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress,
ENCRYPTION_FORWARD_SECURE);
+ EXPECT_TRUE(connection_.GetStats().server_preferred_address_validated);
+ EXPECT_FALSE(
+ connection_.GetStats().failed_to_validate_server_preferred_address);
}
TEST_P(QuicConnectionTest, ClientFailedToValidateServerPreferredAddress) {
@@ -16097,22 +16130,36 @@
return;
}
ServerPreferredAddressInit();
+ const QuicSocketAddress kServerPreferredAddress =
+ QuicConnectionPeer::GetServerPreferredAddress(&connection_);
+ const QuicSocketAddress kNewSelfAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
EXPECT_CALL(visitor_, GetHandshakeState())
.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))));
connection_.OnHandshakeComplete();
EXPECT_TRUE(connection_.HasPendingPathValidation());
- ASSERT_FALSE(writer_->path_challenge_frames().empty());
+ EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, kServerPreferredAddress));
+ ASSERT_FALSE(new_writer.path_challenge_frames().empty());
// Receive mismatched path challenge from original server address.
QuicFrames frames;
frames.push_back(
QuicFrame(QuicPathResponseFrame(99, {0, 1, 2, 3, 4, 5, 6, 7})));
- ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress,
+ ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, kPeerAddress,
ENCRYPTION_FORWARD_SECURE);
ASSERT_TRUE(connection_.HasPendingPathValidation());
+ EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, kServerPreferredAddress));
// Simluate path validation times out.
for (size_t i = 0; i < QuicPathValidator::kMaxRetryTimes + 1; ++i) {
@@ -16123,8 +16170,11 @@
->Fire();
}
EXPECT_FALSE(connection_.HasPendingPathValidation());
+ EXPECT_FALSE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, kServerPreferredAddress));
// Verify stream data is sent on the default path.
connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ ASSERT_FALSE(writer_->stream_frames().empty());
EXPECT_EQ(TestConnectionId(),
writer_->last_packet_header().destination_connection_id);
EXPECT_EQ(kPeerAddress, writer_->last_write_peer_address());
@@ -16136,6 +16186,9 @@
EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/1u));
retire_peer_issued_cid_alarm->Fire();
EXPECT_TRUE(connection_.IsValidStatelessResetToken(kTestStatelessResetToken));
+ EXPECT_FALSE(connection_.GetStats().server_preferred_address_validated);
+ EXPECT_TRUE(
+ connection_.GetStats().failed_to_validate_server_preferred_address);
}
} // namespace
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index ae7c41a..5f132d0 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -1330,6 +1330,16 @@
config_.ReceivedInitialSessionFlowControlWindowBytes());
}
+ if (version().HasIetfQuicFrames() &&
+ config_.CanSendPreferredAddressConnectionIdAndToken()) {
+ absl::optional<QuicNewConnectionIdFrame> frame =
+ connection_->MaybeIssueNewConnectionIdForPreferredAddress();
+ if (frame.has_value()) {
+ config_.SetPreferredAddressConnectionIdAndTokenToSend(
+ frame->connection_id, frame->stateless_reset_token);
+ }
+ }
+
is_configured_ = true;
connection()->OnConfigNegotiated();
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h
index b2b8403..6576325 100644
--- a/quiche/quic/core/quic_session.h
+++ b/quiche/quic/core/quic_session.h
@@ -180,6 +180,12 @@
return nullptr;
}
+ std::unique_ptr<QuicPathValidationContext>
+ CreatePathValidationContextForServerPreferredAddress(
+ const QuicSocketAddress& /*server_preferred_address*/) override {
+ return nullptr;
+ }
+
// QuicStreamFrameDataProducer
WriteStreamDataResult WriteStreamData(QuicStreamId id,
QuicStreamOffset offset,
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc
index d8f3070..b33bc29 100644
--- a/quiche/quic/test_tools/quic_connection_peer.cc
+++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -503,6 +503,13 @@
}
// static
+bool QuicConnectionPeer::IsDefaultPath(QuicConnection* connection,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ return connection->IsDefaultPath(self_address, peer_address);
+}
+
+// static
QuicConnection::PathState* QuicConnectionPeer::GetAlternativePath(
QuicConnection* connection) {
return &connection->alternative_path_;
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h
index 91350d0..794e991 100644
--- a/quiche/quic/test_tools/quic_connection_peer.h
+++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -201,6 +201,10 @@
static QuicConnection::PathState* GetDefaultPath(QuicConnection* connection);
+ static bool IsDefaultPath(QuicConnection* connection,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address);
+
static QuicConnection::PathState* GetAlternativePath(
QuicConnection* connection);
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index 35f51a8..f2e38f5 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -502,6 +502,9 @@
MOCK_METHOD(bool, MaybeSendAddressToken, (), (override));
MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>,
CreateContextForMultiPortPath, (), (override));
+ MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>,
+ CreatePathValidationContextForServerPreferredAddress,
+ (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 03e86f3..d41bef3 100644
--- a/quiche/quic/test_tools/simulator/quic_endpoint.h
+++ b/quiche/quic/test_tools/simulator/quic_endpoint.h
@@ -109,6 +109,11 @@
override {
return nullptr;
}
+ std::unique_ptr<QuicPathValidationContext>
+ CreatePathValidationContextForServerPreferredAddress(
+ const QuicSocketAddress& /*server_preferred_address*/) override {
+ return nullptr;
+ }
// End QuicConnectionVisitorInterface implementation.
diff --git a/quiche/quic/tools/quic_simple_client_session.cc b/quiche/quic/tools/quic_simple_client_session.cc
index 2350052..c1e1b2a 100644
--- a/quiche/quic/tools/quic_simple_client_session.cc
+++ b/quiche/quic/tools/quic_simple_client_session.cc
@@ -56,4 +56,22 @@
network_helper_->GetLatestClientAddress(), peer_address());
}
+std::unique_ptr<QuicPathValidationContext>
+QuicSimpleClientSession::CreatePathValidationContextForServerPreferredAddress(
+ 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;
+ }
+ QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
+ if (writer == nullptr) {
+ return nullptr;
+ }
+ return std::make_unique<PathMigrationContext>(
+ std::unique_ptr<QuicPacketWriter>(writer),
+ network_helper_->GetLatestClientAddress(), server_preferred_address);
+}
+
} // namespace quic
diff --git a/quiche/quic/tools/quic_simple_client_session.h b/quiche/quic/tools/quic_simple_client_session.h
index 763a1a3..631b36b 100644
--- a/quiche/quic/tools/quic_simple_client_session.h
+++ b/quiche/quic/tools/quic_simple_client_session.h
@@ -27,6 +27,9 @@
HttpDatagramSupport LocalHttpDatagramSupport() override;
std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
override;
+ std::unique_ptr<QuicPathValidationContext>
+ CreatePathValidationContextForServerPreferredAddress(
+ const QuicSocketAddress& server_preferred_address) override;
bool drop_response_body() const { return drop_response_body_; }
private: