Use new connection ID in connection migration on the client side.

Code is protected by connection_migration_use_new_cid_, which will be enabled by reloadable flag in follow up CL once the feature is complete.

PiperOrigin-RevId: 369297584
Change-Id: Ie5ce5e2ac987beb0f04f121227633e5934ed2210
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 62e9f4d..74931d0 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2855,6 +2855,7 @@
     MaybeSendInResponseToPacket();
   }
   SetPingAlarm();
+  RetirePeerIssuedConnectionIdsNoLongerOnPath();
   current_packet_data_ = nullptr;
   is_current_packet_connectivity_probing_ = false;
 }
@@ -3672,6 +3673,7 @@
     SetRetransmissionAlarm();
   }
   SetPingAlarm();
+  RetirePeerIssuedConnectionIdsNoLongerOnPath();
 
   // The packet number length must be updated after OnPacketSent, because it
   // may change the packet number length in packet.
@@ -6337,21 +6339,22 @@
 
 bool QuicConnection::SendNewConnectionId(
     const QuicNewConnectionIdFrame& frame) {
-  QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
   visitor_->SendNewConnectionId(frame);
   return connected_;
 }
 
 void QuicConnection::OnNewConnectionIdIssued(
     const QuicConnectionId& connection_id) {
-  QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
-  visitor_->OnServerConnectionIdIssued(connection_id);
+  if (perspective_ == Perspective::IS_SERVER) {
+    visitor_->OnServerConnectionIdIssued(connection_id);
+  }
 }
 
 void QuicConnection::OnSelfIssuedConnectionIdRetired(
     const QuicConnectionId& connection_id) {
-  QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
-  visitor_->OnServerConnectionIdRetired(connection_id);
+  if (perspective_ == Perspective::IS_SERVER) {
+    visitor_->OnServerConnectionIdRetired(connection_id);
+  }
 }
 
 void QuicConnection::MaybeUpdateAckTimeout() {
@@ -6468,7 +6471,8 @@
     std::unique_ptr<QuicPathValidationContext> context,
     std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) {
   QUICHE_DCHECK(use_path_validator_);
-  if (perspective_ == Perspective::IS_CLIENT &&
+  if (!connection_migration_use_new_cid_ &&
+      perspective_ == Perspective::IS_CLIENT &&
       !IsDefaultPath(context->self_address(), context->peer_address())) {
     alternative_path_ = PathState(
         context->self_address(), context->peer_address(),
@@ -6480,6 +6484,43 @@
     // Cancel and fail any earlier validation.
     path_validator_.CancelPathValidation();
   }
+  if (connection_migration_use_new_cid_ &&
+      perspective_ == Perspective::IS_CLIENT &&
+      !IsDefaultPath(context->self_address(), context->peer_address())) {
+    if (self_issued_cid_manager_ != nullptr) {
+      self_issued_cid_manager_->MaybeSendNewConnectionIds();
+      if (!connected_) {
+        return;
+      }
+    }
+    if ((self_issued_cid_manager_ != nullptr &&
+         !self_issued_cid_manager_->HasConnectionIdToConsume()) ||
+        (peer_issued_cid_manager_ != nullptr &&
+         !peer_issued_cid_manager_->HasUnusedConnectionId())) {
+      QUIC_DVLOG(1) << "Client cannot start new path validation as there is no "
+                       "requried connection ID is available.";
+      result_delegate->OnPathValidationFailure(std::move(context));
+      return;
+    }
+    QuicConnectionId client_connection_id, server_connection_id;
+    StatelessResetToken stateless_reset_token;
+    bool stateless_reset_token_received = false;
+    if (self_issued_cid_manager_ != nullptr) {
+      client_connection_id =
+          *self_issued_cid_manager_->ConsumeOneConnectionId();
+    }
+    if (peer_issued_cid_manager_ != nullptr) {
+      const auto* connection_id_data =
+          peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
+      server_connection_id = connection_id_data->connection_id;
+      stateless_reset_token_received = true;
+      stateless_reset_token = connection_id_data->stateless_reset_token;
+    }
+    alternative_path_ =
+        PathState(context->self_address(), context->peer_address(),
+                  client_connection_id, server_connection_id,
+                  stateless_reset_token_received, stateless_reset_token);
+  }
   path_validator_.StartPathValidation(std::move(context),
                                       std::move(result_delegate));
 }
@@ -6550,12 +6591,80 @@
   path_validator_.CancelPathValidation();
 }
 
-void QuicConnection::MigratePath(const QuicSocketAddress& self_address,
+bool QuicConnection::UpdateConnectionIdsOnClientMigration(
+    const QuicSocketAddress& self_address,
+    const QuicSocketAddress& peer_address) {
+  QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
+  if (IsAlternativePath(self_address, peer_address)) {
+    // Client migration is after path validation.
+    if (peer_issued_cid_manager_ != nullptr) {
+      QUICHE_DCHECK(!default_path_.server_connection_id.IsEmpty());
+      packet_creator_.FlushCurrentPacket();
+    }
+    default_path_.client_connection_id = alternative_path_.client_connection_id;
+    default_path_.server_connection_id = alternative_path_.server_connection_id;
+    default_path_.stateless_reset_token =
+        alternative_path_.stateless_reset_token;
+    default_path_.stateless_reset_token_received =
+        alternative_path_.stateless_reset_token_received;
+    packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
+    packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
+    return true;
+  }
+  // Client migration is without path validation.
+  if (self_issued_cid_manager_ != nullptr) {
+    self_issued_cid_manager_->MaybeSendNewConnectionIds();
+    if (!connected_) {
+      return false;
+    }
+  }
+  if ((self_issued_cid_manager_ != nullptr &&
+       !self_issued_cid_manager_->HasConnectionIdToConsume()) ||
+      (peer_issued_cid_manager_ != nullptr &&
+       !peer_issued_cid_manager_->HasUnusedConnectionId())) {
+    return false;
+  }
+  if (self_issued_cid_manager_ != nullptr) {
+    default_path_.client_connection_id =
+        *self_issued_cid_manager_->ConsumeOneConnectionId();
+  }
+  if (peer_issued_cid_manager_ != nullptr) {
+    const auto* connection_id_data =
+        peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
+    default_path_.server_connection_id = connection_id_data->connection_id;
+    default_path_.stateless_reset_token_received = true;
+    default_path_.stateless_reset_token =
+        connection_id_data->stateless_reset_token;
+  }
+  packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
+  packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
+  return true;
+}
+
+void QuicConnection::RetirePeerIssuedConnectionIdsNoLongerOnPath() {
+  if (!connection_migration_use_new_cid_ ||
+      peer_issued_cid_manager_ == nullptr) {
+    return;
+  }
+  if (perspective_ == Perspective::IS_CLIENT) {
+    peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds(
+        {default_path_.server_connection_id,
+         alternative_path_.server_connection_id});
+  }
+  // TODO(haoyuewang) Do the same on the server side.
+}
+
+bool QuicConnection::MigratePath(const QuicSocketAddress& self_address,
                                  const QuicSocketAddress& peer_address,
                                  QuicPacketWriter* writer,
                                  bool owns_writer) {
   if (!connected_) {
-    return;
+    return false;
+  }
+
+  if (connection_migration_use_new_cid_ &&
+      !UpdateConnectionIdsOnClientMigration(self_address, peer_address)) {
+    return false;
   }
 
   const auto self_address_change_type = QuicUtils::DetermineAddressChangeType(
@@ -6572,6 +6681,14 @@
   UpdatePeerAddress(peer_address);
   SetQuicPacketWriter(writer, owns_writer);
   OnSuccessfulMigration(is_port_change);
+  return true;
+}
+
+void QuicConnection::OnPathValidationFailureAtClient() {
+  if (connection_migration_use_new_cid_) {
+    QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
+    alternative_path_.Clear();
+  }
 }
 
 std::vector<QuicConnectionId> QuicConnection::GetActiveServerConnectionIds()
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 454bf88..b81eb02 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1211,11 +1211,17 @@
 
   void CancelPathValidation();
 
-  void MigratePath(const QuicSocketAddress& self_address,
+  // Returns true if the migration succeeds, otherwise returns false (e.g., no
+  // available CIDs, connection disconnected, etc).
+  bool MigratePath(const QuicSocketAddress& self_address,
                    const QuicSocketAddress& peer_address,
                    QuicPacketWriter* writer,
                    bool owns_writer);
 
+  // Called to clear the alternative_path_ when path validation failed on the
+  // client side.
+  void OnPathValidationFailureAtClient();
+
   void SetSourceAddressTokenToSend(absl::string_view token);
 
   void SendPing() {
@@ -1236,6 +1242,10 @@
     return use_connection_id_on_default_path_;
   }
 
+  bool connection_migration_use_new_cid() const {
+    return connection_migration_use_new_cid_;
+  }
+
   // Instantiates connection ID manager.
   void CreateConnectionIdManager();
 
@@ -1505,6 +1515,16 @@
       bool* stateless_reset_token_received,
       StatelessResetToken* stateless_reset_token) const;
 
+  // Update the connection IDs when client migrates with/without validation.
+  // Returns false if required connection ID is not available.
+  bool UpdateConnectionIdsOnClientMigration(
+      const QuicSocketAddress& self_address,
+      const QuicSocketAddress& peer_address);
+
+  // Retire active peer issued connection IDs after they are no longer used on
+  // any path.
+  void RetirePeerIssuedConnectionIdsNoLongerOnPath();
+
   // Writes the given packet to socket, encrypted with packet's
   // encryption_level. Returns true on successful write, and false if the writer
   // was blocked and the write needs to be tried again. Notifies the
@@ -2249,6 +2269,9 @@
   // PATH_CHALLENGE received.
   bool should_proactively_validate_peer_address_on_path_challenge_ = false;
 
+  // Enable this via reloadable flag once this feature is complete.
+  bool connection_migration_use_new_cid_ = false;
+
   const bool group_path_response_and_challenge_sending_closer_ =
       GetQuicReloadableFlag(
           quic_group_path_response_and_challenge_sending_closer);
diff --git a/quic/core/quic_connection_id_manager.cc b/quic/core/quic_connection_id_manager.cc
index 5069234..f7089b7 100644
--- a/quic/core/quic_connection_id_manager.cc
+++ b/quic/core/quic_connection_id_manager.cc
@@ -199,6 +199,22 @@
   }
 }
 
+void QuicPeerIssuedConnectionIdManager::MaybeRetireUnusedConnectionIds(
+    const std::vector<QuicConnectionId>& active_connection_ids_on_path) {
+  std::vector<QuicConnectionId> cids_to_retire;
+  for (const auto& cid_data : active_connection_id_data_) {
+    if (std::find(active_connection_ids_on_path.begin(),
+                  active_connection_ids_on_path.end(),
+                  cid_data.connection_id) ==
+        active_connection_ids_on_path.end()) {
+      cids_to_retire.push_back(cid_data.connection_id);
+    }
+  }
+  for (const auto& cid : cids_to_retire) {
+    PrepareToRetireActiveConnectionId(cid);
+  }
+}
+
 bool QuicPeerIssuedConnectionIdManager::IsConnectionIdActive(
     const QuicConnectionId& cid) const {
   return FindConnectionIdData(active_connection_id_data_, cid) !=
diff --git a/quic/core/quic_connection_id_manager.h b/quic/core/quic_connection_id_manager.h
index f42915a..5e7fc8c 100644
--- a/quic/core/quic_connection_id_manager.h
+++ b/quic/core/quic_connection_id_manager.h
@@ -78,8 +78,10 @@
   // Id.
   const QuicConnectionIdData* ConsumeOneUnusedConnectionId();
 
-  // Add the connection Id to the pending retirement connection Id list.
-  void PrepareToRetireActiveConnectionId(const QuicConnectionId& cid);
+  // Add each active connection Id that is no longer on path to the pending
+  // retirement connection Id list.
+  void MaybeRetireUnusedConnectionIds(
+      const std::vector<QuicConnectionId>& active_connection_ids_on_path);
 
   bool IsConnectionIdActive(const QuicConnectionId& cid) const;
 
@@ -95,6 +97,10 @@
  private:
   friend class test::QuicConnectionIdManagerPeer;
 
+  // Add the connection Id to the pending retirement connection Id list and
+  // schedule an alarm if needed.
+  void PrepareToRetireActiveConnectionId(const QuicConnectionId& cid);
+
   bool IsConnectionIdNew(const QuicNewConnectionIdFrame& frame);
 
   void PrepareToRetireConnectionIdPriorTo(
diff --git a/quic/core/quic_connection_id_manager_test.cc b/quic/core/quic_connection_id_manager_test.cc
index d339a2c..7baeb2b 100644
--- a/quic/core/quic_connection_id_manager_test.cc
+++ b/quic/core/quic_connection_id_manager_test.cc
@@ -126,8 +126,8 @@
               frame.stateless_reset_token);
 
     // Connection migration succeed. Prepares to retire CID #0.
-    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
-        TestConnectionId(0));
+    peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+        {TestConnectionId(1)});
     cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(1));
     ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
     alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -150,8 +150,8 @@
     // Start to use CID #2 for alternative path.
     peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
     // Connection migration succeed. Prepares to retire CID #1.
-    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
-        TestConnectionId(1));
+    peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+        {TestConnectionId(2)});
     cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(2));
     ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
     alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -174,8 +174,8 @@
     // Start to use CID #3 for alternative path.
     peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
     // Connection migration succeed. Prepares to retire CID #2.
-    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
-        TestConnectionId(2));
+    peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+        {TestConnectionId(3)});
     cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3));
     ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
     alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -214,8 +214,8 @@
     // Start to use CID #1 for alternative path.
     peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
     // Connection migration fails. Prepares to retire CID #1.
-    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
-        TestConnectionId(1));
+    peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+        {initial_connection_id_});
     // Actually retires CID #1.
     ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
     alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -238,8 +238,8 @@
     // Start to use CID #2 for alternative path.
     peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
     // Connection migration fails again. Prepares to retire CID #2.
-    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
-        TestConnectionId(2));
+    peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+        {initial_connection_id_});
     // Actually retires CID #2.
     ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
     alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -262,8 +262,8 @@
     // Start to use CID #3 for alternative path.
     peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
     // Connection migration succeed. Prepares to retire CID #0.
-    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
-        TestConnectionId(0));
+    peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+        {TestConnectionId(3)});
     // After CID #3 is default (i.e., when there is no pending frame to write
     // associated with CID #0), #0 can actually be retired.
     cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3));
@@ -374,8 +374,8 @@
   // Outcome: (active: #0 #1 unused: None)
   peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
   // Prepare to retire CID #1 as path validation fails.
-  peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
-      TestConnectionId(1));
+  peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+      {initial_connection_id_});
   // Actually retires CID #1.
   ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
   alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index fcdcf4f..e73298d 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -21,6 +21,7 @@
 #include "quic/core/crypto/quic_decrypter.h"
 #include "quic/core/crypto/quic_encrypter.h"
 #include "quic/core/frames/quic_connection_close_frame.h"
+#include "quic/core/frames/quic_new_connection_id_frame.h"
 #include "quic/core/frames/quic_path_response_frame.h"
 #include "quic/core/quic_connection_id.h"
 #include "quic/core/quic_constants.h"
@@ -2193,10 +2194,12 @@
 
 class TestValidationResultDelegate : public QuicPathValidator::ResultDelegate {
  public:
-  TestValidationResultDelegate(const QuicSocketAddress& expected_self_address,
+  TestValidationResultDelegate(QuicConnection* connection,
+                               const QuicSocketAddress& expected_self_address,
                                const QuicSocketAddress& expected_peer_address,
                                bool* success)
       : QuicPathValidator::ResultDelegate(),
+        connection_(connection),
         expected_self_address_(expected_self_address),
         expected_peer_address_(expected_peer_address),
         success_(success) {}
@@ -2211,10 +2214,14 @@
       std::unique_ptr<QuicPathValidationContext> context) override {
     EXPECT_EQ(expected_self_address_, context->self_address());
     EXPECT_EQ(expected_peer_address_, context->peer_address());
+    if (connection_->perspective() == Perspective::IS_CLIENT) {
+      connection_->OnPathValidationFailureAtClient();
+    }
     *success_ = false;
   }
 
  private:
+  QuicConnection* connection_;
   QuicSocketAddress expected_self_address_;
   QuicSocketAddress expected_peer_address_;
   bool* success_;
@@ -2300,7 +2307,8 @@
           std::make_unique<TestQuicPathValidationContext>(
               connection_.self_address(), kNewPeerAddress, writer_.get()),
           std::make_unique<TestValidationResultDelegate>(
-              connection_.self_address(), kNewPeerAddress, &success));
+              &connection_, connection_.self_address(), kNewPeerAddress,
+              &success));
     }
     EXPECT_EQ((connection_.validate_client_address() ? 2 : 3) * bytes_sent,
               QuicConnectionPeer::BytesSentOnAlternativePath(&connection_));
@@ -8908,7 +8916,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
 
   // Receiving a PATH_CHALLENGE on the alternative path. Response to this
   // PATH_CHALLENGE should be sent via the alternative writer.
@@ -11759,7 +11767,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
   EXPECT_EQ(0u, writer_->packets_write_attempts());
 
   QuicFrames frames;
@@ -11793,7 +11801,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
   EXPECT_EQ(0u, writer_->packets_write_attempts());
 
   // Start another path validation request.
@@ -11814,7 +11822,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress2, connection_.peer_address(), &new_writer2),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress2, connection_.peer_address(), &success2));
+          &connection_, kNewSelfAddress2, connection_.peer_address(),
+          &success2));
   EXPECT_FALSE(success);
   EXPECT_TRUE(connection_.HasPendingPathValidation());
 }
@@ -11834,12 +11843,12 @@
         EXPECT_EQ(1u, writer_->padding_frames().size());
       }));
   bool success = true;
-  connection_.ValidatePath(
-      std::make_unique<TestQuicPathValidationContext>(
-          connection_.self_address(), connection_.peer_address(),
-          writer_.get()),
-      std::make_unique<TestValidationResultDelegate>(
-          connection_.self_address(), connection_.peer_address(), &success));
+  connection_.ValidatePath(std::make_unique<TestQuicPathValidationContext>(
+                               connection_.self_address(),
+                               connection_.peer_address(), writer_.get()),
+                           std::make_unique<TestValidationResultDelegate>(
+                               &connection_, connection_.self_address(),
+                               connection_.peer_address(), &success));
   EXPECT_EQ(1u, writer_->packets_write_attempts());
   EXPECT_TRUE(connection_.HasPendingPathValidation());
 
@@ -11881,7 +11890,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
   EXPECT_EQ(0u, writer_->packets_write_attempts());
   EXPECT_TRUE(connection_.HasPendingPathValidation());
 
@@ -11925,7 +11934,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
   EXPECT_EQ(0u, writer_->packets_write_attempts());
 
   new_writer.SetWritable();
@@ -11985,7 +11994,8 @@
         std::make_unique<TestQuicPathValidationContext>(
             connection_.self_address(), kNewPeerAddress, writer_.get()),
         std::make_unique<TestValidationResultDelegate>(
-            connection_.self_address(), kNewPeerAddress, &success));
+            &connection_, connection_.self_address(), kNewPeerAddress,
+            &success));
   }
   EXPECT_EQ(1u, writer_->packets_write_attempts());
 
@@ -12029,7 +12039,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
   EXPECT_EQ(1u, new_writer.packets_write_attempts());
   EXPECT_EQ(1u, new_writer.path_challenge_frames().size());
   EXPECT_EQ(1u, new_writer.padding_frames().size());
@@ -12061,12 +12071,12 @@
     // packet creator.
     bool success = false;
     QuicConnection::ScopedPacketFlusher flusher(&connection_);
-    connection_.ValidatePath(
-        std::make_unique<TestQuicPathValidationContext>(
-            connection_.self_address(), connection_.peer_address(),
-            writer_.get()),
-        std::make_unique<TestValidationResultDelegate>(
-            connection_.self_address(), connection_.peer_address(), &success));
+    connection_.ValidatePath(std::make_unique<TestQuicPathValidationContext>(
+                                 connection_.self_address(),
+                                 connection_.peer_address(), writer_.get()),
+                             std::make_unique<TestValidationResultDelegate>(
+                                 &connection_, connection_.self_address(),
+                                 connection_.peer_address(), &success));
   }
   EXPECT_EQ(1u, writer_->packets_write_attempts());
   EXPECT_EQ(1u, writer_->path_challenge_frames().size());
@@ -12098,7 +12108,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           connection_.self_address(), kNewPeerAddress, writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          connection_.self_address(), kNewPeerAddress, &success));
+          &connection_, connection_.self_address(), kNewPeerAddress, &success));
 
   EXPECT_EQ(1u, writer_->packets_write_attempts());
   EXPECT_FALSE(connection_.HasPendingPathValidation());
@@ -12129,7 +12139,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           connection_.self_address(), kNewPeerAddress, writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          connection_.self_address(), kNewPeerAddress, &success));
+          &connection_, connection_.self_address(), kNewPeerAddress, &success));
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   // Connection shouldn't be closed.
   EXPECT_TRUE(connection_.connected());
@@ -13486,7 +13496,7 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
       &connection_, kNewSelfAddress, connection_.peer_address()));
@@ -14019,6 +14029,236 @@
 }
 
 TEST_P(QuicConnectionTest,
+       PathValidationFailedOnClientDueToLackOfServerConnectionId) {
+  if (!connection_.support_multiple_connection_ids() ||
+      !connection_.use_connection_id_on_default_path()) {
+    return;
+  }
+  QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+  PathProbeTestInit(Perspective::IS_CLIENT);
+
+  const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback4(),
+                                          /*port=*/34567);
+
+  bool success;
+  connection_.ValidatePath(
+      std::make_unique<TestQuicPathValidationContext>(
+          kNewSelfAddress, connection_.peer_address(), writer_.get()),
+      std::make_unique<TestValidationResultDelegate>(
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+
+  EXPECT_FALSE(success);
+}
+
+TEST_P(QuicConnectionTest,
+       PathValidationFailedOnClientDueToLackOfClientConnectionIdTheSecondTime) {
+  if (!connection_.support_multiple_connection_ids() ||
+      !connection_.use_connection_id_on_default_path()) {
+    return;
+  }
+
+  QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+  PathProbeTestInit(Perspective::IS_CLIENT);
+  SetClientConnectionId(TestConnectionId(1));
+
+  // Make sure server connection ID is available for the 1st validation.
+  QuicNewConnectionIdFrame frame1;
+  frame1.connection_id = TestConnectionId(2);
+  frame1.sequence_number = 1u;
+  frame1.retire_prior_to = 0u;
+  frame1.stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(frame1.connection_id);
+  connection_.OnNewConnectionIdFrame(frame1);
+
+  // Client will issue a new client connection ID to server.
+  QuicConnectionId new_client_connection_id;
+  EXPECT_CALL(visitor_, SendNewConnectionId(_))
+      .WillOnce(Invoke([&](const QuicNewConnectionIdFrame& frame) {
+        new_client_connection_id = frame.connection_id;
+      }));
+
+  const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
+  ASSERT_NE(kSelfAddress1, connection_.self_address());
+  bool success1;
+  connection_.ValidatePath(
+      std::make_unique<TestQuicPathValidationContext>(
+          kSelfAddress1, connection_.peer_address(), writer_.get()),
+      std::make_unique<TestValidationResultDelegate>(
+          &connection_, kSelfAddress1, connection_.peer_address(), &success1));
+
+  // Migrate upon 1st validation success.
+  TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+  ASSERT_TRUE(connection_.MigratePath(kSelfAddress1, connection_.peer_address(),
+                                      &new_writer, /*owns_writer=*/false));
+  QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_);
+  const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_);
+  EXPECT_EQ(default_path->client_connection_id, new_client_connection_id);
+  EXPECT_EQ(default_path->server_connection_id, frame1.connection_id);
+  EXPECT_EQ(default_path->stateless_reset_token, frame1.stateless_reset_token);
+  EXPECT_TRUE(default_path->stateless_reset_token_received);
+  const auto* alternative_path =
+      QuicConnectionPeer::GetAlternativePath(&connection_);
+  EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty());
+  EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
+  EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+
+  // Client will retire server connection ID on old default_path.
+  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();
+
+  // Another server connection ID is available to client.
+  QuicNewConnectionIdFrame frame2;
+  frame2.connection_id = TestConnectionId(4);
+  frame2.sequence_number = 2u;
+  frame2.retire_prior_to = 1u;
+  frame2.stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(frame2.connection_id);
+  connection_.OnNewConnectionIdFrame(frame2);
+
+  const QuicSocketAddress kSelfAddress2(QuicIpAddress::Loopback4(),
+                                        /*port=*/45678);
+  bool success2;
+  connection_.ValidatePath(
+      std::make_unique<TestQuicPathValidationContext>(
+          kSelfAddress2, connection_.peer_address(), writer_.get()),
+      std::make_unique<TestValidationResultDelegate>(
+          &connection_, kSelfAddress2, connection_.peer_address(), &success2));
+  // Since server does not retire any client connection ID yet, 2nd validation
+  // would fail due to lack of client connection ID.
+  EXPECT_FALSE(success2);
+}
+
+TEST_P(QuicConnectionTest, ServerConnectionIdRetiredUponPathValidationFailure) {
+  if (!connection_.support_multiple_connection_ids() ||
+      !connection_.use_connection_id_on_default_path()) {
+    return;
+  }
+
+  QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+  PathProbeTestInit(Perspective::IS_CLIENT);
+
+  // Make sure server connection ID is available for validation.
+  QuicNewConnectionIdFrame frame;
+  frame.connection_id = TestConnectionId(2);
+  frame.sequence_number = 1u;
+  frame.retire_prior_to = 0u;
+  frame.stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+  connection_.OnNewConnectionIdFrame(frame);
+
+  const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback4(),
+                                          /*port=*/34567);
+  bool success;
+  connection_.ValidatePath(
+      std::make_unique<TestQuicPathValidationContext>(
+          kNewSelfAddress, connection_.peer_address(), writer_.get()),
+      std::make_unique<TestValidationResultDelegate>(
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+
+  auto* path_validator = QuicConnectionPeer::path_validator(&connection_);
+  path_validator->CancelPathValidation();
+  QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_);
+  EXPECT_FALSE(success);
+  const auto* alternative_path =
+      QuicConnectionPeer::GetAlternativePath(&connection_);
+  EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty());
+  EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
+  EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+
+  // Client will retire server connection ID on alternative_path.
+  auto* retire_peer_issued_cid_alarm =
+      connection_.GetRetirePeerIssuedConnectionIdAlarm();
+  ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet());
+  EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/1u));
+  retire_peer_issued_cid_alarm->Fire();
+}
+
+TEST_P(QuicConnectionTest,
+       MigratePathDirectlyFailedDueToLackOfServerConnectionId) {
+  if (!connection_.support_multiple_connection_ids() ||
+      !connection_.use_connection_id_on_default_path()) {
+    return;
+  }
+  QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+  PathProbeTestInit(Perspective::IS_CLIENT);
+  const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
+  ASSERT_NE(kSelfAddress1, connection_.self_address());
+
+  TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+  ASSERT_FALSE(connection_.MigratePath(kSelfAddress1,
+                                       connection_.peer_address(), &new_writer,
+                                       /*owns_writer=*/false));
+}
+
+TEST_P(QuicConnectionTest,
+       MigratePathDirectlyFailedDueToLackOfClientConnectionIdTheSecondTime) {
+  if (!connection_.support_multiple_connection_ids() ||
+      !connection_.use_connection_id_on_default_path()) {
+    return;
+  }
+  QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+  PathProbeTestInit(Perspective::IS_CLIENT);
+  SetClientConnectionId(TestConnectionId(1));
+
+  // Make sure server connection ID is available for the 1st migration.
+  QuicNewConnectionIdFrame frame1;
+  frame1.connection_id = TestConnectionId(2);
+  frame1.sequence_number = 1u;
+  frame1.retire_prior_to = 0u;
+  frame1.stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(frame1.connection_id);
+  connection_.OnNewConnectionIdFrame(frame1);
+
+  // Client will issue a new client connection ID to server.
+  QuicConnectionId new_client_connection_id;
+  EXPECT_CALL(visitor_, SendNewConnectionId(_))
+      .WillOnce(Invoke([&](const QuicNewConnectionIdFrame& frame) {
+        new_client_connection_id = frame.connection_id;
+      }));
+
+  // 1st migration is successful.
+  const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
+  ASSERT_NE(kSelfAddress1, connection_.self_address());
+  TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+  ASSERT_TRUE(connection_.MigratePath(kSelfAddress1, connection_.peer_address(),
+                                      &new_writer,
+                                      /*owns_writer=*/false));
+  QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_);
+  const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_);
+  EXPECT_EQ(default_path->client_connection_id, new_client_connection_id);
+  EXPECT_EQ(default_path->server_connection_id, frame1.connection_id);
+  EXPECT_EQ(default_path->stateless_reset_token, frame1.stateless_reset_token);
+  EXPECT_TRUE(default_path->stateless_reset_token_received);
+
+  // Client will retire server connection ID on old default_path.
+  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();
+
+  // Another server connection ID is available to client.
+  QuicNewConnectionIdFrame frame2;
+  frame2.connection_id = TestConnectionId(4);
+  frame2.sequence_number = 2u;
+  frame2.retire_prior_to = 1u;
+  frame2.stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(frame2.connection_id);
+  connection_.OnNewConnectionIdFrame(frame2);
+
+  // Since server does not retire any client connection ID yet, 2nd migration
+  // would fail due to lack of client connection ID.
+  const QuicSocketAddress kSelfAddress2(QuicIpAddress::Loopback4(),
+                                        /*port=*/45678);
+  ASSERT_FALSE(connection_.MigratePath(kSelfAddress2,
+                                       connection_.peer_address(), &new_writer,
+                                       /*owns_writer=*/false));
+}
+
+TEST_P(QuicConnectionTest,
        CloseConnectionAfterReceiveNewConnectionIdFromPeerUsingEmptyCID) {
   if (!version().HasIetfQuicFrames()) {
     return;
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 871e326..65dd0dd 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -2609,11 +2609,12 @@
   return connection_->HasPendingPathValidation();
 }
 
-void QuicSession::MigratePath(const QuicSocketAddress& self_address,
+bool QuicSession::MigratePath(const QuicSocketAddress& self_address,
                               const QuicSocketAddress& peer_address,
                               QuicPacketWriter* writer,
                               bool owns_writer) {
-  connection_->MigratePath(self_address, peer_address, writer, owns_writer);
+  return connection_->MigratePath(self_address, peer_address, writer,
+                                  owns_writer);
 }
 
 bool QuicSession::ValidateToken(absl::string_view token) const {
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 40f2d18..b6c1367 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -455,7 +455,7 @@
   bool HasPendingPathValidation() const;
 
   // Switch to the path described in |context| without validating the path.
-  void MigratePath(const QuicSocketAddress& self_address,
+  bool MigratePath(const QuicSocketAddress& self_address,
                    const QuicSocketAddress& peer_address,
                    QuicPacketWriter* writer,
                    bool owns_writer);
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc
index 3039685..bdd9eff 100644
--- a/quic/test_tools/quic_connection_peer.cc
+++ b/quic/test_tools/quic_connection_peer.cc
@@ -490,6 +490,12 @@
 }
 
 // static
+void QuicConnectionPeer::EnableConnectionMigrationUseNewCID(
+    QuicConnection* connection) {
+  connection->connection_migration_use_new_cid_ = true;
+}
+
+// static
 void QuicConnectionPeer::ResetPeerIssuedConnectionIdManager(
     QuicConnection* connection) {
   connection->peer_issued_cid_manager_ = nullptr;
@@ -507,5 +513,11 @@
   return &connection->alternative_path_;
 }
 
+// static
+void QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(
+    QuicConnection* connection) {
+  connection->RetirePeerIssuedConnectionIdsNoLongerOnPath();
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h
index 174e2c3..df2a823 100644
--- a/quic/test_tools/quic_connection_peer.h
+++ b/quic/test_tools/quic_connection_peer.h
@@ -199,12 +199,18 @@
 
   static void EnableMultipleConnectionIdSupport(QuicConnection* connection);
 
+  // Remove this method once the boolean is enabled via reloadable flag.
+  static void EnableConnectionMigrationUseNewCID(QuicConnection* connection);
+
   static void ResetPeerIssuedConnectionIdManager(QuicConnection* connection);
 
   static QuicConnection::PathState* GetDefaultPath(QuicConnection* connection);
 
   static QuicConnection::PathState* GetAlternativePath(
       QuicConnection* connection);
+
+  static void RetirePeerIssuedConnectionIdsNoLongerOnPath(
+      QuicConnection* connection);
 };
 
 }  // namespace test
diff --git a/quic/tools/quic_client_base.cc b/quic/tools/quic_client_base.cc
index 7077f16..66f28b7 100644
--- a/quic/tools/quic_client_base.cc
+++ b/quic/tools/quic_client_base.cc
@@ -465,6 +465,7 @@
       std::unique_ptr<QuicPathValidationContext> context) override {
     QUIC_LOG(WARNING) << "Fail to validate path " << *context
                       << ", stop migrating.";
+    client_->session()->connection()->OnPathValidationFailureAtClient();
   }
 
  private: