Pass path validation start time to QuicPathValidator::ResultDelegate::OnPathValidationSuccess.

The start time can be used by multi ports to calculate RTTs on alternate path.

PiperOrigin-RevId: 464625591
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 8dfa5a2..fc83845 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -37,6 +37,7 @@
 #include "quiche/quic/core/quic_packet_writer.h"
 #include "quiche/quic/core/quic_packets.h"
 #include "quiche/quic/core/quic_path_validator.h"
+#include "quiche/quic/core/quic_time.h"
 #include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/core/quic_utils.h"
 #include "quiche/quic/platform/api/quic_bug_tracker.h"
@@ -325,7 +326,7 @@
       blackhole_detector_(this, &arena_, alarm_factory_, &context_),
       idle_network_detector_(this, clock_->ApproximateNow(), &arena_,
                              alarm_factory_, &context_),
-      path_validator_(alarm_factory_, &arena_, this, random_generator_,
+      path_validator_(alarm_factory_, &arena_, this, random_generator_, clock_,
                       &context_),
       ping_manager_(perspective, this, &arena_, alarm_factory_, &context_) {
   QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
@@ -7113,9 +7114,10 @@
           connection_->active_effective_peer_migration_type_) {}
 
 void QuicConnection::ReversePathValidationResultDelegate::
-    OnPathValidationSuccess(
-        std::unique_ptr<QuicPathValidationContext> context) {
-  QUIC_DLOG(INFO) << "Successfully validated new path " << *context;
+    OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,
+                            QuicTime start_time) {
+  QUIC_DLOG(INFO) << "Successfully validated new path " << *context
+                  << ", validation started at " << start_time;
   if (connection_->IsDefaultPath(context->self_address(),
                                  context->peer_address())) {
     QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 3, 6);
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index cd187e3..5daf2bf 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -1467,7 +1467,8 @@
         const QuicSocketAddress& direct_peer_address);
 
     void OnPathValidationSuccess(
-        std::unique_ptr<QuicPathValidationContext> context) override;
+        std::unique_ptr<QuicPathValidationContext> context,
+        QuicTime start_time) override;
 
     void OnPathValidationFailure(
         std::unique_ptr<QuicPathValidationContext> context) override;
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index b07eb49..9274962 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -2428,7 +2428,8 @@
         expected_peer_address_(expected_peer_address),
         success_(success) {}
   void OnPathValidationSuccess(
-      std::unique_ptr<QuicPathValidationContext> context) override {
+      std::unique_ptr<QuicPathValidationContext> context,
+      QuicTime /*start_time*/) override {
     EXPECT_EQ(expected_self_address_, context->self_address());
     EXPECT_EQ(expected_peer_address_, context->peer_address());
     *success_ = true;
diff --git a/quiche/quic/core/quic_path_validator.cc b/quiche/quic/core/quic_path_validator.cc
index 279ac0f..61e3dad 100644
--- a/quiche/quic/core/quic_path_validator.cc
+++ b/quiche/quic/core/quic_path_validator.cc
@@ -34,10 +34,11 @@
 QuicPathValidator::QuicPathValidator(QuicAlarmFactory* alarm_factory,
                                      QuicConnectionArena* arena,
                                      SendDelegate* send_delegate,
-                                     QuicRandom* random,
+                                     QuicRandom* random, const QuicClock* clock,
                                      QuicConnectionContext* context)
     : send_delegate_(send_delegate),
       random_(random),
+      clock_(clock),
       retry_timer_(alarm_factory->CreateAlarm(
           arena->New<RetryAlarmDelegate>(this, context), arena)),
       retry_count_(0u) {}
@@ -57,14 +58,16 @@
     return;
   }
   // This iterates at most 3 times.
-  if (std::find(probing_data_.begin(), probing_data_.end(), probing_data) !=
-      probing_data_.end()) {
-    result_delegate_->OnPathValidationSuccess(std::move(path_context_));
-    ResetPathValidation();
-  } else {
-    QUIC_DVLOG(1) << "PATH_RESPONSE with payload " << probing_data.data()
-                  << " doesn't match the probing data.";
+  for (auto it = probing_data_.begin(); it != probing_data_.end(); ++it) {
+    if (it->frame_buffer == probing_data) {
+      result_delegate_->OnPathValidationSuccess(std::move(path_context_),
+                                                it->send_time);
+      ResetPathValidation();
+      return;
+    }
   }
+  QUIC_DVLOG(1) << "PATH_RESPONSE with payload " << probing_data.data()
+                << " doesn't match the probing data.";
 }
 
 void QuicPathValidator::StartPathValidation(
@@ -109,9 +112,10 @@
 }
 
 const QuicPathFrameBuffer& QuicPathValidator::GeneratePathChallengePayload() {
-  probing_data_.push_back(QuicPathFrameBuffer());
-  random_->RandBytes(probing_data_.back().data(), sizeof(QuicPathFrameBuffer));
-  return probing_data_.back();
+  probing_data_.emplace_back(clock_->Now());
+  random_->RandBytes(probing_data_.back().frame_buffer.data(),
+                     sizeof(QuicPathFrameBuffer));
+  return probing_data_.back().frame_buffer;
 }
 
 void QuicPathValidator::OnRetryTimeout() {
diff --git a/quiche/quic/core/quic_path_validator.h b/quiche/quic/core/quic_path_validator.h
index 32758a9..4389ce5 100644
--- a/quiche/quic/core/quic_path_validator.h
+++ b/quiche/quic/core/quic_path_validator.h
@@ -16,6 +16,7 @@
 #include "quiche/quic/core/quic_connection_context.h"
 #include "quiche/quic/core/quic_one_block_arena.h"
 #include "quiche/quic/core/quic_packet_writer.h"
+#include "quiche/quic/core/quic_time.h"
 #include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/platform/api/quic_export.h"
 #include "quiche/quic/platform/api/quic_socket_address.h"
@@ -100,8 +101,11 @@
    public:
     virtual ~ResultDelegate() = default;
 
+    // Called when a PATH_RESPONSE is received with a matching PATH_CHALLANGE.
+    // |start_time| is the time when the matching PATH_CHALLANGE was sent.
     virtual void OnPathValidationSuccess(
-        std::unique_ptr<QuicPathValidationContext> context) = 0;
+        std::unique_ptr<QuicPathValidationContext> context,
+        QuicTime start_time) = 0;
 
     virtual void OnPathValidationFailure(
         std::unique_ptr<QuicPathValidationContext> context) = 0;
@@ -109,7 +113,7 @@
 
   QuicPathValidator(QuicAlarmFactory* alarm_factory, QuicConnectionArena* arena,
                     SendDelegate* delegate, QuicRandom* random,
-                    QuicConnectionContext* context);
+                    const QuicClock* clock, QuicConnectionContext* context);
 
   // Send PATH_CHALLENGE and start the retry timer.
   void StartPathValidation(std::unique_ptr<QuicPathValidationContext> context,
@@ -144,10 +148,17 @@
 
   void ResetPathValidation();
 
+  struct QUIC_NO_EXPORT ProbingData {
+    explicit ProbingData(QuicTime send_time) : send_time(send_time) {}
+    QuicPathFrameBuffer frame_buffer;
+    QuicTime send_time;
+  };
+
   // Has at most 3 entries due to validation timeout.
-  absl::InlinedVector<QuicPathFrameBuffer, 3> probing_data_;
+  absl::InlinedVector<ProbingData, 3> probing_data_;
   SendDelegate* send_delegate_;
   QuicRandom* random_;
+  const QuicClock* clock_;
   std::unique_ptr<QuicPathValidationContext> path_context_;
   std::unique_ptr<ResultDelegate> result_delegate_;
   QuicArenaScopedPtr<QuicAlarm> retry_timer_;
diff --git a/quiche/quic/core/quic_path_validator_test.cc b/quiche/quic/core/quic_path_validator_test.cc
index 7640b86..f45cd26 100644
--- a/quiche/quic/core/quic_path_validator_test.cc
+++ b/quiche/quic/core/quic_path_validator_test.cc
@@ -43,6 +43,7 @@
  public:
   QuicPathValidatorTest()
       : path_validator_(&alarm_factory_, &arena_, &send_delegate_, &random_,
+                        &clock_,
                         /*context=*/nullptr),
         context_(new MockQuicPathValidationContext(
             self_address_, peer_address_, effective_peer_address_, &writer_)),
@@ -82,15 +83,19 @@
         return true;
       }));
   EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_));
+  const QuicTime expected_start_time = clock_.Now();
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
       std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
   EXPECT_TRUE(path_validator_.HasPendingPathValidation());
   EXPECT_TRUE(path_validator_.IsValidatingPeerAddress(effective_peer_address_));
-  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_))
-      .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) {
+  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_, _))
+      .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context,
+                           QuicTime start_time) {
         EXPECT_EQ(context.get(), context_);
+        EXPECT_EQ(start_time, expected_start_time);
       }));
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(kInitialRttMs));
   path_validator_.OnPathResponse(challenge_data, self_address_);
   EXPECT_FALSE(path_validator_.HasPendingPathValidation());
 }
@@ -107,6 +112,7 @@
         return true;
       }));
   EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_));
+  const QuicTime expected_start_time = clock_.Now();
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
       std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
@@ -114,12 +120,16 @@
   // Reception of a PATH_RESPONSE on a different self address should be ignored.
   const QuicSocketAddress kAlternativeSelfAddress(QuicIpAddress::Any6(), 54321);
   EXPECT_NE(kAlternativeSelfAddress, self_address_);
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(kInitialRttMs));
   path_validator_.OnPathResponse(challenge_data, kAlternativeSelfAddress);
 
-  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_))
-      .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) {
+  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_, _))
+      .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context,
+                           QuicTime start_time) {
         EXPECT_EQ(context->self_address(), self_address_);
+        EXPECT_EQ(start_time, expected_start_time);
       }));
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(kInitialRttMs));
   path_validator_.OnPathResponse(challenge_data, self_address_);
 }
 
@@ -143,6 +153,7 @@
       }));
   EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_))
       .Times(2u);
+  const QuicTime start_time = clock_.Now();
   path_validator_.StartPathValidation(
       std::unique_ptr<QuicPathValidationContext>(context_),
       std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
@@ -152,7 +163,7 @@
   alarm_factory_.FireAlarm(
       QuicPathValidatorPeer::retry_timer(&path_validator_));
 
-  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_));
+  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_, start_time));
   // Respond to the 1st PATH_CHALLENGE should complete the validation.
   path_validator_.OnPathResponse(challenge_data, self_address_);
   EXPECT_FALSE(path_validator_.HasPendingPathValidation());
@@ -183,12 +194,13 @@
       std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
 
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
+  const QuicTime start_time = clock_.Now();
   random_.ChangeValue();
   alarm_factory_.FireAlarm(
       QuicPathValidatorPeer::retry_timer(&path_validator_));
 
   // Respond to the 2nd PATH_CHALLENGE should complete the validation.
-  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_));
+  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_, start_time));
   path_validator_.OnPathResponse(challenge_data, self_address_);
   EXPECT_FALSE(path_validator_.HasPendingPathValidation());
 }
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index 7340e7e..66f2c60 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -1434,7 +1434,8 @@
     : public QuicPathValidator::ResultDelegate {
  public:
   MOCK_METHOD(void, OnPathValidationSuccess,
-              (std::unique_ptr<QuicPathValidationContext>), (override));
+              (std::unique_ptr<QuicPathValidationContext>, QuicTime),
+              (override));
 
   MOCK_METHOD(void, OnPathValidationFailure,
               (std::unique_ptr<QuicPathValidationContext>), (override));
diff --git a/quiche/quic/tools/quic_client_base.cc b/quiche/quic/tools/quic_client_base.cc
index 826ade8..4a300c2 100644
--- a/quiche/quic/tools/quic_client_base.cc
+++ b/quiche/quic/tools/quic_client_base.cc
@@ -49,7 +49,8 @@
   // Overridden to start migration and takes the ownership of the writer in the
   // context.
   void OnPathValidationSuccess(
-      std::unique_ptr<QuicPathValidationContext> context) override {
+      std::unique_ptr<QuicPathValidationContext> context,
+      QuicTime /*start_time*/) override {
     QUIC_DLOG(INFO) << "Successfully validated path from " << *context
                     << ". Migrate to it now.";
     auto migration_context = std::unique_ptr<PathMigrationContext>(
@@ -467,8 +468,10 @@
       : QuicPathValidator::ResultDelegate(), client_(client) {}
 
   void OnPathValidationSuccess(
-      std::unique_ptr<QuicPathValidationContext> context) override {
-    QUIC_DLOG(INFO) << "Successfully validated path from " << *context;
+      std::unique_ptr<QuicPathValidationContext> context,
+      QuicTime start_time) override {
+    QUIC_DLOG(INFO) << "Successfully validated path from " << *context
+                    << ", validation started at " << start_time;
     client_->AddValidatedPath(std::move(context));
   }
   void OnPathValidationFailure(