Add QuicPathValidator implementation.

PiperOrigin-RevId: 338150507
Change-Id: I96f0f70e5037a854618da69387d6002dc5e716e4
diff --git a/quic/core/quic_path_validator.cc b/quic/core/quic_path_validator.cc
new file mode 100644
index 0000000..5d5e720
--- /dev/null
+++ b/quic/core/quic_path_validator.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_path_validator.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+class RetryAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+  explicit RetryAlarmDelegate(QuicPathValidator* path_validator)
+      : path_validator_(path_validator) {}
+  RetryAlarmDelegate(const RetryAlarmDelegate&) = delete;
+  RetryAlarmDelegate& operator=(const RetryAlarmDelegate&) = delete;
+
+  void OnAlarm() override { path_validator_->OnRetryTimeout(); }
+
+ private:
+  QuicPathValidator* path_validator_;
+};
+
+std::ostream& operator<<(std::ostream& os,
+                         const QuicPathValidationContext& context) {
+  return os << " from " << context.self_address_ << " to "
+            << context.peer_address_;
+}
+
+QuicPathValidator::QuicPathValidator(QuicAlarmFactory* alarm_factory,
+                                     QuicOneBlockArena<1024>* arena,
+                                     SendDelegate* send_delegate,
+                                     QuicRandom* random)
+    : send_delegate_(send_delegate),
+      random_(random),
+      retry_timer_(
+          alarm_factory->CreateAlarm(arena->New<RetryAlarmDelegate>(this),
+                                     arena)),
+      retry_count_(0u) {}
+
+void QuicPathValidator::OnPathResponse(const QuicPathFrameBuffer& probing_data,
+                                       QuicSocketAddress self_address) {
+  if (!HasPendingPathValidation()) {
+    return;
+  }
+
+  QUIC_DVLOG(1) << "Match PATH_RESPONSE received on " << self_address;
+  QUIC_BUG_IF(!path_context_->self_address().IsInitialized())
+      << "Self address should have been known by now";
+  if (self_address != path_context_->self_address()) {
+    QUIC_DVLOG(1) << "Expect the response to be received on "
+                  << path_context_->self_address();
+    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();
+  }
+}
+
+void QuicPathValidator::StartValidingPath(
+    std::unique_ptr<QuicPathValidationContext> context,
+    std::unique_ptr<ResultDelegate> result_delegate) {
+  CancelPathValidation();
+  DCHECK_NE(nullptr, context);
+  QUIC_DLOG(INFO) << "Start validating path " << *context
+                  << " via writer: " << context->WriterToUse();
+
+  path_context_ = std::move(context);
+  result_delegate_ = std::move(result_delegate);
+  SendPathChallengeAndSetAlarm();
+}
+
+void QuicPathValidator::ResetPathValidation() {
+  path_context_ = nullptr;
+  result_delegate_ = nullptr;
+  retry_timer_->Cancel();
+  retry_count_ = 0;
+}
+
+void QuicPathValidator::CancelPathValidation() {
+  if (path_context_ == nullptr) {
+    return;
+  }
+  QUIC_DVLOG(1) << "Cancel validation on path" << *path_context_;
+  ResetPathValidation();
+}
+
+bool QuicPathValidator::HasPendingPathValidation() const {
+  return path_context_ != nullptr;
+}
+
+const QuicPathFrameBuffer& QuicPathValidator::GeneratePathChallengePayload() {
+  probing_data_.push_back(QuicPathFrameBuffer());
+  random_->RandBytes(probing_data_.back().data(), sizeof(QuicPathFrameBuffer));
+  return probing_data_.back();
+}
+
+void QuicPathValidator::OnRetryTimeout() {
+  ++retry_count_;
+  if (retry_count_ > kMaxRetryTimes) {
+    result_delegate_->OnPathValidationFailure(std::move(path_context_));
+    CancelPathValidation();
+    return;
+  }
+  QUIC_DVLOG(1) << "Send another PATH_CHALLENGE on path " << *path_context_;
+  SendPathChallengeAndSetAlarm();
+}
+
+void QuicPathValidator::SendPathChallengeAndSetAlarm() {
+  bool should_continue = send_delegate_->SendPathChallenge(
+      GeneratePathChallengePayload(), path_context_->self_address(),
+      path_context_->peer_address(), path_context_->WriterToUse());
+
+  if (!should_continue) {
+    // The delegate doesn't want to continue the path validation.
+    CancelPathValidation();
+    return;
+  }
+  retry_timer_->Set(send_delegate_->GetRetryTimeout(
+      path_context_->peer_address(), path_context_->WriterToUse()));
+}
+
+}  // namespace quic
diff --git a/quic/core/quic_path_validator.h b/quic/core/quic_path_validator.h
new file mode 100644
index 0000000..8cfa81d
--- /dev/null
+++ b/quic/core/quic_path_validator.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PATH_VALIDATOR_H_
+#define QUICHE_QUIC_CORE_QUIC_PATH_VALIDATOR_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h"
+#include "net/third_party/quiche/src/quic/core/quic_clock.h"
+#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/quic/platform/impl/quic_export_impl.h"
+
+namespace quic {
+
+namespace test {
+class QuicPathValidatorPeer;
+}
+
+class QuicConnection;
+
+// Interface to provide the information of the path to be validated.
+class QUIC_EXPORT_PRIVATE QuicPathValidationContext {
+ public:
+  QuicPathValidationContext(const QuicSocketAddress& self_address,
+                            const QuicSocketAddress& peer_address)
+      : self_address_(self_address), peer_address_(peer_address) {}
+
+  virtual ~QuicPathValidationContext() = default;
+
+  virtual QuicPacketWriter* WriterToUse() = 0;
+
+  const QuicSocketAddress& self_address() const { return self_address_; }
+  const QuicSocketAddress& peer_address() const { return peer_address_; }
+
+ private:
+  QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+      std::ostream& os,
+      const QuicPathValidationContext& context);
+
+  QuicSocketAddress self_address_;
+  QuicSocketAddress peer_address_;
+};
+
+// Used to validate a path by sending up to 3 PATH_CHALLENGE frames before
+// declaring a path validation failure.
+class QUIC_EXPORT_PRIVATE QuicPathValidator {
+ public:
+  static const uint16_t kMaxRetryTimes = 2;
+
+  // Used to write PATH_CHALLENGE on the path to be validated and to get retry
+  // timeout.
+  class QUIC_EXPORT_PRIVATE SendDelegate {
+   public:
+    virtual ~SendDelegate() = default;
+
+    // Send a PATH_CHALLENGE with |data_buffer| as the frame payload using given
+    // path information. Return false if the delegate doesn't want to continue
+    // the validation.
+    virtual bool SendPathChallenge(const QuicPathFrameBuffer& data_buffer,
+                                   const QuicSocketAddress& self_address,
+                                   const QuicSocketAddress& peer_address,
+                                   QuicPacketWriter* writer) = 0;
+    // Return the time to retry sending PATH_CHALLENGE again based on given peer
+    // address and writer.
+    virtual QuicTime GetRetryTimeout(const QuicSocketAddress& peer_address,
+                                     QuicPacketWriter* writer) const = 0;
+  };
+
+  // Handles the validation result.
+  class QUIC_EXPORT_PRIVATE ResultDelegate {
+   public:
+    virtual ~ResultDelegate() = default;
+
+    virtual void OnPathValidationSuccess(
+        std::unique_ptr<QuicPathValidationContext> context) = 0;
+
+    virtual void OnPathValidationFailure(
+        std::unique_ptr<QuicPathValidationContext> context) = 0;
+  };
+
+  QuicPathValidator(QuicAlarmFactory* alarm_factory,
+                    QuicConnectionArena* arena,
+                    SendDelegate* delegate,
+                    QuicRandom* random);
+
+  // Send PATH_CHALLENGE and start the retry timer.
+  void StartValidingPath(std::unique_ptr<QuicPathValidationContext> context,
+                         std::unique_ptr<ResultDelegate> result_delegate);
+
+  // Called when a PATH_RESPONSE frame has been received. Matches the received
+  // PATH_RESPONSE payload with the payloads previously sent in PATH_CHALLANGE
+  // frames and the self address on which it was sent.
+  void OnPathResponse(const QuicPathFrameBuffer& probing_data,
+                      QuicSocketAddress self_address);
+
+  // Cancel the retry timer and reset the path and result delegate.
+  void CancelPathValidation();
+
+  bool HasPendingPathValidation() const;
+
+  // Send another PATH_CHALLENGE on the same path. After retrying
+  // |kMaxRetryTimes| times, fail the current path validation.
+  void OnRetryTimeout();
+
+ private:
+  friend class test::QuicPathValidatorPeer;
+
+  // Return the payload to be used in the next PATH_CHALLENGE frame.
+  const QuicPathFrameBuffer& GeneratePathChallengePayload();
+
+  void SendPathChallengeAndSetAlarm();
+
+  void ResetPathValidation();
+
+  // Has at most 3 entries due to validation timeout.
+  QuicInlinedVector<QuicPathFrameBuffer, 3> probing_data_;
+  SendDelegate* send_delegate_;
+  QuicRandom* random_;
+  std::unique_ptr<QuicPathValidationContext> path_context_;
+  std::unique_ptr<ResultDelegate> result_delegate_;
+  QuicArenaScopedPtr<QuicAlarm> retry_timer_;
+  size_t retry_count_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QUIC_PATH_VALIDATOR_H_
diff --git a/quic/core/quic_path_validator_test.cc b/quic/core/quic_path_validator_test.cc
new file mode 100644
index 0000000..656292e
--- /dev/null
+++ b/quic/core/quic_path_validator_test.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_path_validator.h"
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_path_validator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+
+namespace quic {
+namespace test {
+
+class MockSendDelegate : public QuicPathValidator::SendDelegate {
+ public:
+  // Send a PATH_CHALLENGE frame using given path information and populate
+  // |data_buffer| with the frame payload. Return true if the validator should
+  // move forward in validation, i.e. arm the retry timer.
+  MOCK_METHOD(bool,
+              SendPathChallenge,
+              (const QuicPathFrameBuffer&,
+               const QuicSocketAddress&,
+               const QuicSocketAddress&,
+               QuicPacketWriter*),
+              (override));
+
+  MOCK_METHOD(QuicTime,
+              GetRetryTimeout,
+              (const QuicSocketAddress&, QuicPacketWriter*),
+              (const override));
+};
+
+class QuicPathValidatorTest : public QuicTest {
+ public:
+  QuicPathValidatorTest()
+      : path_validator_(&alarm_factory_, &arena_, &send_delegate_, &random_),
+        context_(new MockQuicPathValidationContext(self_address_,
+                                                   peer_address_,
+                                                   &writer_)),
+        result_delegate_(new MockQuicPathValidationResultDelegate()) {
+    clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+    ON_CALL(send_delegate_, GetRetryTimeout(_, _))
+        .WillByDefault(
+            Return(clock_.ApproximateNow() +
+                   3 * QuicTime::Delta::FromMilliseconds(kInitialRttMs)));
+  }
+
+ protected:
+  quic::test::MockAlarmFactory alarm_factory_;
+  MockSendDelegate send_delegate_;
+  MockRandom random_;
+  MockClock clock_;
+  QuicConnectionArena arena_;
+  QuicPathValidator path_validator_;
+  QuicSocketAddress self_address_{QuicIpAddress::Any4(), 443};
+  QuicSocketAddress peer_address_{QuicIpAddress::Loopback4(), 443};
+  MockPacketWriter writer_;
+  MockQuicPathValidationContext* context_;
+  MockQuicPathValidationResultDelegate* result_delegate_;
+};
+
+TEST_F(QuicPathValidatorTest, PathValidationSuccessOnFirstRound) {
+  QuicPathFrameBuffer challenge_data;
+  EXPECT_CALL(send_delegate_,
+              SendPathChallenge(_, self_address_, peer_address_, &writer_))
+      .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload,
+                           const QuicSocketAddress&, const QuicSocketAddress&,
+                           QuicPacketWriter*) {
+        memcpy(challenge_data.data(), payload.data(), payload.size());
+        return true;
+      }));
+  EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_));
+  path_validator_.StartValidingPath(
+      std::unique_ptr<QuicPathValidationContext>(context_),
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+  EXPECT_TRUE(path_validator_.HasPendingPathValidation());
+  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_))
+      .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) {
+        EXPECT_EQ(context.get(), context_);
+      }));
+  path_validator_.OnPathResponse(challenge_data, self_address_);
+  EXPECT_FALSE(path_validator_.HasPendingPathValidation());
+}
+
+TEST_F(QuicPathValidatorTest, RespondWithDifferentSelfAddress) {
+  QuicPathFrameBuffer challenge_data;
+  EXPECT_CALL(send_delegate_,
+              SendPathChallenge(_, self_address_, peer_address_, &writer_))
+      .WillOnce(Invoke([&](const QuicPathFrameBuffer payload,
+                           const QuicSocketAddress&, const QuicSocketAddress&,
+                           QuicPacketWriter*) {
+        memcpy(challenge_data.data(), payload.data(), payload.size());
+        return true;
+      }));
+  EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_));
+  path_validator_.StartValidingPath(
+      std::unique_ptr<QuicPathValidationContext>(context_),
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+
+  // Reception of a PATH_RESPONSE on a different self address should be ignored.
+  const QuicSocketAddress kAlternativeSelfAddress(QuicIpAddress::Any6(), 54321);
+  EXPECT_NE(kAlternativeSelfAddress, self_address_);
+  path_validator_.OnPathResponse(challenge_data, kAlternativeSelfAddress);
+
+  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_))
+      .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) {
+        EXPECT_EQ(context->self_address(), self_address_);
+      }));
+  path_validator_.OnPathResponse(challenge_data, self_address_);
+}
+
+TEST_F(QuicPathValidatorTest, RespondAfter1stRetry) {
+  QuicPathFrameBuffer challenge_data;
+  EXPECT_CALL(send_delegate_,
+              SendPathChallenge(_, self_address_, peer_address_, &writer_))
+      .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload,
+                           const QuicSocketAddress&, const QuicSocketAddress&,
+                           QuicPacketWriter*) {
+        // Store up the 1st PATH_CHALLANGE payload.
+        memcpy(challenge_data.data(), payload.data(), payload.size());
+        return true;
+      }))
+      .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload,
+                           const QuicSocketAddress&, const QuicSocketAddress&,
+                           QuicPacketWriter*) {
+        EXPECT_NE(payload, challenge_data);
+        return true;
+      }));
+  EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_))
+      .Times(2u);
+  path_validator_.StartValidingPath(
+      std::unique_ptr<QuicPathValidationContext>(context_),
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
+  random_.ChangeValue();
+  alarm_factory_.FireAlarm(
+      QuicPathValidatorPeer::retry_timer(&path_validator_));
+
+  EXPECT_CALL(*result_delegate_, OnPathValidationSuccess(_));
+  // Respond to the 1st PATH_CHALLENGE should complete the validation.
+  path_validator_.OnPathResponse(challenge_data, self_address_);
+  EXPECT_FALSE(path_validator_.HasPendingPathValidation());
+}
+
+TEST_F(QuicPathValidatorTest, RespondToRetryChallenge) {
+  QuicPathFrameBuffer challenge_data;
+  EXPECT_CALL(send_delegate_,
+              SendPathChallenge(_, self_address_, peer_address_, &writer_))
+      .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload,
+                           const QuicSocketAddress&, const QuicSocketAddress&,
+                           QuicPacketWriter*) {
+        memcpy(challenge_data.data(), payload.data(), payload.size());
+        return true;
+      }))
+      .WillOnce(Invoke([&](const QuicPathFrameBuffer& payload,
+                           const QuicSocketAddress&, const QuicSocketAddress&,
+                           QuicPacketWriter*) {
+        EXPECT_NE(challenge_data, payload);
+        memcpy(challenge_data.data(), payload.data(), payload.size());
+        return true;
+      }));
+  EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_))
+      .Times(2u);
+  path_validator_.StartValidingPath(
+      std::unique_ptr<QuicPathValidationContext>(context_),
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
+  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(_));
+  path_validator_.OnPathResponse(challenge_data, self_address_);
+  EXPECT_FALSE(path_validator_.HasPendingPathValidation());
+}
+
+TEST_F(QuicPathValidatorTest, ValidationTimeOut) {
+  EXPECT_CALL(send_delegate_,
+              SendPathChallenge(_, self_address_, peer_address_, &writer_))
+      .Times(3u)
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_))
+      .Times(3u);
+  path_validator_.StartValidingPath(
+      std::unique_ptr<QuicPathValidationContext>(context_),
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+
+  QuicPathFrameBuffer challenge_data;
+  memset(challenge_data.data(), 'a', challenge_data.size());
+  // Reception of a PATH_RESPONSE with different payload should be ignored.
+  path_validator_.OnPathResponse(challenge_data, self_address_);
+
+  // Retry 3 times. The 3rd time should fail the validation.
+  EXPECT_CALL(*result_delegate_, OnPathValidationFailure(_))
+      .WillOnce(Invoke([=](std::unique_ptr<QuicPathValidationContext> context) {
+        EXPECT_EQ(context_, context.get());
+      }));
+  for (size_t i = 0; i <= QuicPathValidator::kMaxRetryTimes; ++i) {
+    clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
+    alarm_factory_.FireAlarm(
+        QuicPathValidatorPeer::retry_timer(&path_validator_));
+  }
+}
+
+TEST_F(QuicPathValidatorTest, SendPathChallengeError) {
+  EXPECT_CALL(send_delegate_,
+              SendPathChallenge(_, self_address_, peer_address_, &writer_))
+      .WillOnce(Invoke([&](const QuicPathFrameBuffer&, const QuicSocketAddress&,
+                           const QuicSocketAddress&, QuicPacketWriter*) {
+        // Abandon this validation in the call stack shouldn't cause crash and
+        // should cancel the alarm.
+        path_validator_.CancelPathValidation();
+        return false;
+      }));
+  EXPECT_CALL(send_delegate_, GetRetryTimeout(peer_address_, &writer_))
+      .Times(0u);
+  path_validator_.StartValidingPath(
+      std::unique_ptr<QuicPathValidationContext>(context_),
+      std::unique_ptr<MockQuicPathValidationResultDelegate>(result_delegate_));
+  EXPECT_FALSE(path_validator_.HasPendingPathValidation());
+  EXPECT_FALSE(QuicPathValidatorPeer::retry_timer(&path_validator_)->IsSet());
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/test_tools/quic_path_validator_peer.cc b/quic/test_tools/quic_path_validator_peer.cc
new file mode 100644
index 0000000..54d97d0
--- /dev/null
+++ b/quic/test_tools/quic_path_validator_peer.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_path_validator_peer.h"
+
+namespace quic {
+namespace test {
+//  static
+QuicAlarm* QuicPathValidatorPeer::retry_timer(QuicPathValidator* validator) {
+  return validator->retry_timer_.get();
+}
+
+}  // namespace test
+}  // namespace quic
diff --git a/quic/test_tools/quic_path_validator_peer.h b/quic/test_tools/quic_path_validator_peer.h
new file mode 100644
index 0000000..5b2b56c
--- /dev/null
+++ b/quic/test_tools/quic_path_validator_peer.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_PATH_VALIDATOR_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_PATH_VALIDATOR_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_path_validator.h"
+
+namespace quic {
+namespace test {
+
+class QuicPathValidatorPeer {
+ public:
+  static QuicAlarm* retry_timer(QuicPathValidator* validator);
+};
+
+}  // namespace test
+}  // namespace quic
+#endif  // QUICHE_QUIC_TEST_TOOLS_QUIC_PATH_VALIDATOR_PEER_H_
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index a5a774d..8b53f8f 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -24,6 +24,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
 #include "net/third_party/quiche/src/quic/core/quic_framer.h"
 #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_path_validator.h"
 #include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
 #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
@@ -1627,6 +1628,33 @@
   MOCK_METHOD(bool, HasUnackedStreamData, (), (const, override));
 };
 
+class MockQuicPathValidationContext : public QuicPathValidationContext {
+ public:
+  MockQuicPathValidationContext(const QuicSocketAddress& self_address,
+                                const QuicSocketAddress& peer_address,
+                                QuicPacketWriter* writer)
+      : QuicPathValidationContext(self_address, peer_address),
+        writer_(writer) {}
+  QuicPacketWriter* WriterToUse() override { return writer_; }
+
+ private:
+  QuicPacketWriter* writer_;
+};
+
+class MockQuicPathValidationResultDelegate
+    : public QuicPathValidator::ResultDelegate {
+ public:
+  MOCK_METHOD(void,
+              OnPathValidationSuccess,
+              (std::unique_ptr<QuicPathValidationContext>),
+              (override));
+
+  MOCK_METHOD(void,
+              OnPathValidationFailure,
+              (std::unique_ptr<QuicPathValidationContext>),
+              (override));
+};
+
 class QuicCryptoClientStreamPeer {
  public:
   QuicCryptoClientStreamPeer() = delete;