Do not create a new multi-port path when there's pending path validation.

Also add logging on why there's an existing path validation.

Merge instruction: This CL adds a new parameter in ValidatePath(). For its callers, please pass in the corresponding reason into the method. I'll be happy to review.

PiperOrigin-RevId: 502708390
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 998b134..de88ae6 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -1798,7 +1798,8 @@
                      default_path_.self_address, direct_peer_address_to_respond,
                      effective_peer_address_to_respond, this),
                  std::make_unique<ReversePathValidationResultDelegate>(
-                     this, peer_address()));
+                     this, peer_address()),
+                 PathValidationReason::kReversePathValidation);
   }
   has_path_challenge_in_current_packet_ = true;
   MaybeUpdateAckTimeout();
@@ -3994,8 +3995,13 @@
 
 void QuicConnection::MaybeCreateMultiPortPath() {
   QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
-  QUIC_BUG_IF(quic_bug_12714_20, path_validator_.HasPendingPathValidation())
-      << "Pending validation exists when multi-port path is created.";
+  if (path_validator_.HasPendingPathValidation()) {
+    QUIC_CLIENT_HISTOGRAM_ENUM("QuicConnection.MultiPortPathCreationCancelled",
+                               path_validator_.GetPathValidationReason(),
+                               PathValidationReason::kMaxValue,
+                               "Reason for cancelled multi port path creation");
+    return;
+  }
   if (multi_port_stats_->num_multi_port_paths_created >=
       kMaxNumMultiPortPaths) {
     return;
@@ -4010,7 +4016,8 @@
   multi_port_path_context_ = nullptr;
   multi_port_stats_->num_multi_port_paths_created++;
   ValidatePath(std::move(path_context),
-               std::move(multi_port_validation_result_delegate));
+               std::move(multi_port_validation_result_delegate),
+               PathValidationReason::kMultiPort);
 }
 
 void QuicConnection::SendOrQueuePacket(SerializedPacket packet) {
@@ -5291,7 +5298,8 @@
                      default_path_.self_address, peer_address(),
                      default_path_.peer_address, this),
                  std::make_unique<ReversePathValidationResultDelegate>(
-                     this, previous_direct_peer_address));
+                     this, previous_direct_peer_address),
+                 PathValidationReason::kReversePathValidation);
   } else {
     QUIC_DVLOG(1) << "Peer address " << default_path_.peer_address
                   << " is already under validation, wait for result.";
@@ -6523,7 +6531,8 @@
 
 void QuicConnection::ValidatePath(
     std::unique_ptr<QuicPathValidationContext> context,
-    std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) {
+    std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate,
+    PathValidationReason reason) {
   if (!connection_migration_use_new_cid_ &&
       perspective_ == Perspective::IS_CLIENT &&
       !IsDefaultPath(context->self_address(), context->peer_address())) {
@@ -6578,7 +6587,7 @@
                                   server_connection_id, stateless_reset_token);
   }
   path_validator_.StartPathValidation(std::move(context),
-                                      std::move(result_delegate));
+                                      std::move(result_delegate), reason);
   if (perspective_ == Perspective::IS_CLIENT &&
       IsValidatingServerPreferredAddress()) {
     AddKnownServerAddress(server_preferred_address_);
@@ -7043,7 +7052,8 @@
       std::make_unique<MultiPortPathValidationResultDelegate>(this);
   path_validator_.StartPathValidation(
       std::move(multi_port_path_context_),
-      std::move(multi_port_validation_result_delegate));
+      std::move(multi_port_validation_result_delegate),
+      PathValidationReason::kMultiPort);
 }
 
 QuicConnection::MultiPortPathValidationResultDelegate::
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index 7f58c5e..7969a0e 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -1193,7 +1193,8 @@
   // this one.
   void ValidatePath(
       std::unique_ptr<QuicPathValidationContext> context,
-      std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate);
+      std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate,
+      PathValidationReason reason);
 
   bool can_receive_ack_frequency_frame() const {
     return can_receive_ack_frequency_frame_;
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index b7959dd..e0c92b1 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -2658,7 +2658,8 @@
               connection_.self_address(), kNewPeerAddress, writer_.get()),
           std::make_unique<TestValidationResultDelegate>(
               &connection_, connection_.self_address(), kNewPeerAddress,
-              &success));
+              &success),
+          PathValidationReason::kReasonUnknown);
     }
     EXPECT_EQ((connection_.validate_client_address() ? 2 : 3) * bytes_sent,
               QuicConnectionPeer::BytesSentOnAlternativePath(&connection_));
@@ -8860,7 +8861,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
 
   // Receiving a PATH_CHALLENGE on the alternative path. Response to this
   // PATH_CHALLENGE should be sent via the alternative writer.
@@ -11625,7 +11627,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
   EXPECT_EQ(0u, writer_->packets_write_attempts());
 
   QuicFrames frames;
@@ -11658,7 +11661,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
   EXPECT_EQ(0u, writer_->packets_write_attempts());
 
   // Start another path validation request.
@@ -11682,7 +11686,8 @@
           kNewSelfAddress2, connection_.peer_address(), &new_writer2),
       std::make_unique<TestValidationResultDelegate>(
           &connection_, kNewSelfAddress2, connection_.peer_address(),
-          &success2));
+          &success2),
+      PathValidationReason::kReasonUnknown);
   EXPECT_FALSE(success);
   if (connection_.connection_migration_use_new_cid()) {
     // There is no pening path validation as there is no available connection
@@ -11712,7 +11717,8 @@
                                connection_.peer_address(), writer_.get()),
                            std::make_unique<TestValidationResultDelegate>(
                                &connection_, connection_.self_address(),
-                               connection_.peer_address(), &success));
+                               connection_.peer_address(), &success),
+                           PathValidationReason::kReasonUnknown);
   EXPECT_EQ(1u, writer_->packets_write_attempts());
   EXPECT_TRUE(connection_.HasPendingPathValidation());
 
@@ -11753,7 +11759,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
   EXPECT_EQ(0u, writer_->packets_write_attempts());
   EXPECT_TRUE(connection_.HasPendingPathValidation());
 
@@ -11798,7 +11805,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
   EXPECT_EQ(0u, writer_->packets_write_attempts());
 
   new_writer.SetWritable();
@@ -11860,7 +11868,8 @@
             connection_.self_address(), kNewPeerAddress, writer_.get()),
         std::make_unique<TestValidationResultDelegate>(
             &connection_, connection_.self_address(), kNewPeerAddress,
-            &success));
+            &success),
+        PathValidationReason::kReasonUnknown);
   }
   EXPECT_EQ(1u, writer_->packets_write_attempts());
 
@@ -11903,7 +11912,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
   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());
@@ -11939,7 +11949,8 @@
                                  connection_.peer_address(), writer_.get()),
                              std::make_unique<TestValidationResultDelegate>(
                                  &connection_, connection_.self_address(),
-                                 connection_.peer_address(), &success));
+                                 connection_.peer_address(), &success),
+                             PathValidationReason::kReasonUnknown);
   }
   EXPECT_EQ(1u, writer_->packets_write_attempts());
   EXPECT_EQ(1u, writer_->path_challenge_frames().size());
@@ -11970,7 +11981,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           connection_.self_address(), kNewPeerAddress, writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, connection_.self_address(), kNewPeerAddress, &success));
+          &connection_, connection_.self_address(), kNewPeerAddress, &success),
+      PathValidationReason::kReasonUnknown);
 
   EXPECT_EQ(1u, writer_->packets_write_attempts());
   EXPECT_FALSE(connection_.HasPendingPathValidation());
@@ -12003,7 +12015,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           connection_.self_address(), kNewPeerAddress, writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, connection_.self_address(), kNewPeerAddress, &success));
+          &connection_, connection_.self_address(), kNewPeerAddress, &success),
+      PathValidationReason::kReasonUnknown);
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   // Connection shouldn't be closed.
   EXPECT_TRUE(connection_.connected());
@@ -13187,7 +13200,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), &new_writer),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
       &connection_, kNewSelfAddress, connection_.peer_address()));
@@ -13241,6 +13255,14 @@
       &connection_, kNewSelfAddress, connection_.peer_address()));
   auto* alt_path = QuicConnectionPeer::GetAlternativePath(&connection_);
   EXPECT_FALSE(alt_path->validated);
+  EXPECT_EQ(PathValidationReason::kMultiPort,
+            QuicConnectionPeer::path_validator(&connection_)
+                ->GetPathValidationReason());
+
+  // Suppose the server retransmits the NEW_CID frame, the client will receive
+  // the same frame again. It should be ignored.
+  // Regression test of crbug.com/1406762
+  connection_.OnNewConnectionIdFrame(frame);
 
   // 30ms RTT.
   const QuicTime::Delta kTestRTT = QuicTime::Delta::FromMilliseconds(30);
@@ -14198,7 +14220,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
 
   EXPECT_FALSE(success);
 }
@@ -14250,7 +14273,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kSelfAddress1, connection_.peer_address(), writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kSelfAddress1, connection_.peer_address(), &success1));
+          &connection_, kSelfAddress1, connection_.peer_address(), &success1),
+      PathValidationReason::kReasonUnknown);
 
   // Migrate upon 1st validation success.
   TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
@@ -14291,7 +14315,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kSelfAddress2, connection_.peer_address(), writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kSelfAddress2, connection_.peer_address(), &success2));
+          &connection_, kSelfAddress2, connection_.peer_address(), &success2),
+      PathValidationReason::kReasonUnknown);
   // Since server does not retire any client connection ID yet, 2nd validation
   // would fail due to lack of client connection ID.
   EXPECT_FALSE(success2);
@@ -14326,7 +14351,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress, connection_.peer_address(), writer_.get()),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+          &connection_, kNewSelfAddress, connection_.peer_address(), &success),
+      PathValidationReason::kReasonUnknown);
 
   auto* path_validator = QuicConnectionPeer::path_validator(&connection_);
   path_validator->CancelPathValidation();
@@ -16104,7 +16130,8 @@
             std::make_unique<TestQuicPathValidationContext>(
                 kNewSelfAddress, kServerPreferredAddress, &new_writer),
             std::make_unique<ServerPreferredAddressTestResultDelegate>(
-                &connection_));
+                &connection_),
+            PathValidationReason::kReasonUnknown);
       }));
   connection_.OnHandshakeComplete();
   EXPECT_TRUE(connection_.HasPendingPathValidation());
@@ -16183,7 +16210,8 @@
             std::make_unique<TestQuicPathValidationContext>(
                 kNewSelfAddress, kServerPreferredAddress, &new_writer),
             std::make_unique<ServerPreferredAddressTestResultDelegate>(
-                &connection_));
+                &connection_),
+            PathValidationReason::kReasonUnknown);
       }));
   connection_.OnHandshakeComplete();
   EXPECT_TRUE(connection_.HasPendingPathValidation());
@@ -16250,7 +16278,8 @@
             std::make_unique<TestQuicPathValidationContext>(
                 kNewSelfAddress, kServerPreferredAddress, &new_writer),
             std::make_unique<ServerPreferredAddressTestResultDelegate>(
-                &connection_));
+                &connection_),
+            PathValidationReason::kReasonUnknown);
       }));
   connection_.OnHandshakeComplete();
   EXPECT_TRUE(connection_.IsValidatingServerPreferredAddress());
@@ -16312,7 +16341,8 @@
             std::make_unique<TestQuicPathValidationContext>(
                 kNewSelfAddress, kServerPreferredAddress, &new_writer),
             std::make_unique<ServerPreferredAddressTestResultDelegate>(
-                &connection_));
+                &connection_),
+            PathValidationReason::kReasonUnknown);
       }));
   QuicConfig config;
   config.SetClientConnectionOptions(QuicTagVector{kSPA2});
@@ -16349,7 +16379,8 @@
             std::make_unique<TestQuicPathValidationContext>(
                 kNewSelfAddress, kServerPreferredAddress, &new_writer),
             std::make_unique<ServerPreferredAddressTestResultDelegate>(
-                &connection_));
+                &connection_),
+            PathValidationReason::kReasonUnknown);
       }));
   QuicConfig config;
   config.SetClientConnectionOptions(QuicTagVector{kSPA2});
@@ -16392,7 +16423,8 @@
             std::make_unique<TestQuicPathValidationContext>(
                 kNewSelfAddress, kServerPreferredAddress, &new_writer),
             std::make_unique<ServerPreferredAddressTestResultDelegate>(
-                &connection_));
+                &connection_),
+            PathValidationReason::kReasonUnknown);
       }));
   QuicConfig config;
   config.SetClientConnectionOptions(QuicTagVector{kSPA2});
@@ -16445,7 +16477,8 @@
             std::make_unique<TestQuicPathValidationContext>(
                 kNewSelfAddress, kServerPreferredAddress, &new_writer),
             std::make_unique<ServerPreferredAddressTestResultDelegate>(
-                &connection_));
+                &connection_),
+            PathValidationReason::kReasonUnknown);
       }));
   // The connection should start probing the preferred address after handshake
   // confirmed.
@@ -16685,8 +16718,8 @@
       std::make_unique<TestQuicPathValidationContext>(
           kNewSelfAddress2, connection_.peer_address(), &new_writer2),
       std::make_unique<TestValidationResultDelegate>(
-          &connection_, kNewSelfAddress2, connection_.peer_address(),
-          &success));
+          &connection_, kNewSelfAddress2, connection_.peer_address(), &success),
+      PathValidationReason::kServerPreferredAddressMigration);
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
       &connection_, kNewSelfAddress2, kServerPreferredAddress));
 
diff --git a/quiche/quic/core/quic_path_validator.cc b/quiche/quic/core/quic_path_validator.cc
index 6179fe5..da29fac 100644
--- a/quiche/quic/core/quic_path_validator.cc
+++ b/quiche/quic/core/quic_path_validator.cc
@@ -72,7 +72,8 @@
 
 void QuicPathValidator::StartPathValidation(
     std::unique_ptr<QuicPathValidationContext> context,
-    std::unique_ptr<ResultDelegate> result_delegate) {
+    std::unique_ptr<ResultDelegate> result_delegate,
+    PathValidationReason reason) {
   QUICHE_DCHECK(context);
   QUIC_DLOG(INFO) << "Start validating path " << *context
                   << " via writer: " << context->WriterToUse();
@@ -82,6 +83,7 @@
     ResetPathValidation();
   }
 
+  reason_ = reason;
   path_context_ = std::move(context);
   result_delegate_ = std::move(result_delegate);
   SendPathChallengeAndSetAlarm();
@@ -92,6 +94,7 @@
   result_delegate_ = nullptr;
   retry_timer_->Cancel();
   retry_count_ = 0;
+  reason_ = PathValidationReason::kReasonUnknown;
 }
 
 void QuicPathValidator::CancelPathValidation() {
diff --git a/quiche/quic/core/quic_path_validator.h b/quiche/quic/core/quic_path_validator.h
index 4b94892..6074fd8 100644
--- a/quiche/quic/core/quic_path_validator.h
+++ b/quiche/quic/core/quic_path_validator.h
@@ -29,6 +29,16 @@
 
 class QuicConnection;
 
+enum class QUIC_EXPORT_PRIVATE PathValidationReason {
+  kReasonUnknown,
+  kMultiPort,
+  kReversePathValidation,
+  kServerPreferredAddressMigration,
+  kPortMigration,
+  kConnectionMigration,
+  kMaxValue,
+};
+
 // Interface to provide the information of the path to be validated.
 class QUIC_EXPORT_PRIVATE QuicPathValidationContext {
  public:
@@ -117,7 +127,8 @@
 
   // Send PATH_CHALLENGE and start the retry timer.
   void StartPathValidation(std::unique_ptr<QuicPathValidationContext> context,
-                           std::unique_ptr<ResultDelegate> result_delegate);
+                           std::unique_ptr<ResultDelegate> result_delegate,
+                           PathValidationReason reason);
 
   // Called when a PATH_RESPONSE frame has been received. Matches the received
   // PATH_RESPONSE payload with the payloads previously sent in PATH_CHALLANGE
@@ -132,6 +143,8 @@
 
   QuicPathValidationContext* GetContext() const;
 
+  PathValidationReason GetPathValidationReason() const { return reason_; }
+
   // Send another PATH_CHALLENGE on the same path. After retrying
   // |kMaxRetryTimes| times, fail the current path validation.
   void OnRetryTimeout();
@@ -168,6 +181,7 @@
   std::unique_ptr<ResultDelegate> result_delegate_;
   QuicArenaScopedPtr<QuicAlarm> retry_timer_;
   size_t retry_count_;
+  PathValidationReason reason_ = PathValidationReason::kReasonUnknown;
 };
 
 }  // namespace quic
diff --git a/quiche/quic/core/quic_path_validator_test.cc b/quiche/quic/core/quic_path_validator_test.cc
index f45cd26..6d0be9e 100644
--- a/quiche/quic/core/quic_path_validator_test.cc
+++ b/quiche/quic/core/quic_path_validator_test.cc
@@ -86,8 +86,11 @@
   const QuicTime expected_start_time = clock_.Now();
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
-      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_),
+      PathValidationReason::kMultiPort);
   EXPECT_TRUE(path_validator_.HasPendingPathValidation());
+  EXPECT_EQ(PathValidationReason::kMultiPort,
+            path_validator_.GetPathValidationReason());
   EXPECT_TRUE(path_validator_.IsValidatingPeerAddress(effective_peer_address_));
   EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_, _))
       .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context,
@@ -98,6 +101,8 @@
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(kInitialRttMs));
   path_validator_.OnPathResponse(challenge_data, self_address_);
   EXPECT_FALSE(path_validator_.HasPendingPathValidation());
+  EXPECT_EQ(PathValidationReason::kReasonUnknown,
+            path_validator_.GetPathValidationReason());
 }
 
 TEST_F(QuicPathValidatorTest, RespondWithDifferentSelfAddress) {
@@ -115,7 +120,8 @@
   const QuicTime expected_start_time = clock_.Now();
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
-      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_),
+      PathValidationReason::kMultiPort);
 
   // Reception of a PATH_RESPONSE on a different self address should be ignored.
   const QuicSocketAddress kAlternativeSelfAddress(QuicIpAddress::Any6(), 54321);
@@ -131,6 +137,8 @@
       }));
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(kInitialRttMs));
   path_validator_.OnPathResponse(challenge_data, self_address_);
+  EXPECT_EQ(PathValidationReason::kReasonUnknown,
+            path_validator_.GetPathValidationReason());
 }
 
 TEST_F(QuicPathValidatorTest, RespondAfter1stRetry) {
@@ -156,7 +164,8 @@
   const QuicTime start_time = clock_.Now();
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
-      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_),
+      PathValidationReason::kMultiPort);
 
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
   random_.ChangeValue();
@@ -191,7 +200,8 @@
       .Times(2u);
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
-      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_),
+      PathValidationReason::kMultiPort);
 
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
   const QuicTime start_time = clock_.Now();
@@ -215,7 +225,8 @@
       .Times(3u);
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
-      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_),
+      PathValidationReason::kMultiPort);
 
   QuicPathFrameBuffer challenge_data;
   memset(challenge_data.data(), 'a', challenge_data.size());
@@ -232,6 +243,8 @@
     alarm_factory_.FireAlarm(
         QuicPathValidatorPeer::retry_timer(&path_validator_));
   }
+  EXPECT_EQ(PathValidationReason::kReasonUnknown,
+            path_validator_.GetPathValidationReason());
 }
 
 TEST_F(QuicPathValidatorTest, SendPathChallengeError) {
@@ -251,9 +264,12 @@
   EXPECT_CALL(*result_delegate_, OnPathValidationFailure(_));
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
-      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_),
+      PathValidationReason::kMultiPort);
   EXPECT_FALSE(path_validator_.HasPendingPathValidation());
   EXPECT_FALSE(QuicPathValidatorPeer::retry_timer(&path_validator_)->IsSet());
+  EXPECT_EQ(PathValidationReason::kReasonUnknown,
+            path_validator_.GetPathValidationReason());
 }
 
 }  // namespace test
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index 7aaa65b..a58e572 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -2658,8 +2658,10 @@
 
 void QuicSession::ValidatePath(
     std::unique_ptr<QuicPathValidationContext> context,
-    std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) {
-  connection_->ValidatePath(std::move(context), std::move(result_delegate));
+    std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate,
+    PathValidationReason reason) {
+  connection_->ValidatePath(std::move(context), std::move(result_delegate),
+                            reason);
 }
 
 bool QuicSession::HasPendingPathValidation() const {
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h
index ec52225..4ef5579 100644
--- a/quiche/quic/core/quic_session.h
+++ b/quiche/quic/core/quic_session.h
@@ -464,7 +464,8 @@
   //  };
   void ValidatePath(
       std::unique_ptr<QuicPathValidationContext> context,
-      std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate);
+      std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate,
+      PathValidationReason reason);
 
   // Return true if there is a path being validated.
   bool HasPendingPathValidation() const;
diff --git a/quiche/quic/tools/quic_client_base.cc b/quiche/quic/tools/quic_client_base.cc
index 02b56a4..4612be0 100644
--- a/quiche/quic/tools/quic_client_base.cc
+++ b/quiche/quic/tools/quic_client_base.cc
@@ -315,8 +315,8 @@
       std::make_unique<PathMigrationContext>(
           std::move(writer), network_helper_->GetLatestClientAddress(),
           session_->peer_address()),
-      std::make_unique<QuicClientSocketMigrationValidationResultDelegate>(
-          this));
+      std::make_unique<QuicClientSocketMigrationValidationResultDelegate>(this),
+      PathValidationReason::kConnectionMigration);
   return true;
 }
 
@@ -519,7 +519,7 @@
       std::make_unique<PathMigrationContext>(
           std::move(writer), network_helper_->GetLatestClientAddress(),
           session_->peer_address()),
-      std::move(result_delegate));
+      std::move(result_delegate), PathValidationReason::kConnectionMigration);
 }
 
 void QuicClientBase::OnServerPreferredAddressAvailable(
@@ -538,7 +538,8 @@
       std::make_unique<PathMigrationContext>(
           std::unique_ptr<QuicPacketWriter>(writer),
           network_helper_->GetLatestClientAddress(), server_preferred_address),
-      std::make_unique<ServerPreferredAddressResultDelegateWithWriter>(this));
+      std::make_unique<ServerPreferredAddressResultDelegateWithWriter>(this),
+      PathValidationReason::kServerPreferredAddressMigration);
 }
 
 }  // namespace quic