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(&params);
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: