Make QuicConnection count bytes sent/received in probing packets on alternative paths.

Add an AlternativePathState object in QuicConnection which keeps track of the state of alternative path seen most recently. The states includes effective peer address which is unique to each path and the bytes sent/received to that address.

In order to increment bytes sent on the alternative path, pass the current packet's effective peer address around in packet creator and in quic connection to track the effective peer address which each packet is sent to.

Make connection to check if the received packet or the packet to send is on default path or not before increment existing counters: current_incoming_packet_received_bytes_counted_ and bytes_sent_before_address_validation_

Protected by quic_reloadable_flag_quic_count_bytes_on_alternative_path_seperately.

PiperOrigin-RevId: 349555303
Change-Id: I527938ba9201a3f15162b5f5c1bf4626aab79f9d
diff --git a/common/platform/api/quiche_export.h b/common/platform/api/quiche_export.h
index 22cc1f9..5df0179 100644
--- a/common/platform/api/quiche_export.h
+++ b/common/platform/api/quiche_export.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_QUICHE_PLATFORM_API_QUICHE_EXPORT_H_
 #define THIRD_PARTY_QUICHE_PLATFORM_API_QUICHE_EXPORT_H_
 
-#include "net/quiche/common/platform/impl/quiche_export_impl.h"
+#include "common/platform/impl/quiche_export_impl.h"
 
 // quiche_export_impl.h defines the following macros:
 // - QUICHE_EXPORT is not meant to be used.
diff --git a/common/platform/api/quiche_flag_utils.h b/common/platform/api/quiche_flag_utils.h
index 0534064..bcb552c 100644
--- a/common/platform/api/quiche_flag_utils.h
+++ b/common/platform/api/quiche_flag_utils.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_FLAG_UTILS_H_
 #define QUICHE_COMMON_PLATFORM_API_QUICHE_FLAG_UTILS_H_
 
-#include "net/quiche/common/platform/impl/quiche_flag_utils_impl.h"
+#include "common/platform/impl/quiche_flag_utils_impl.h"
 
 #define QUICHE_RELOADABLE_FLAG_COUNT QUICHE_RELOADABLE_FLAG_COUNT_IMPL
 #define QUICHE_RELOADABLE_FLAG_COUNT_N QUICHE_RELOADABLE_FLAG_COUNT_N_IMPL
diff --git a/common/platform/api/quiche_flags.h b/common/platform/api/quiche_flags.h
index 83db9d0..0cf2443 100644
--- a/common/platform/api/quiche_flags.h
+++ b/common/platform/api/quiche_flags.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_FLAGS_H_
 #define QUICHE_COMMON_PLATFORM_API_QUICHE_FLAGS_H_
 
-#include "net/quiche/common/platform/impl/quiche_flags_impl.h"
+#include "common/platform/impl/quiche_flags_impl.h"
 
 #define GetQuicheReloadableFlag(module, flag) \
   GetQuicheReloadableFlagImpl(module, flag)
diff --git a/common/platform/api/quiche_logging.h b/common/platform/api/quiche_logging.h
index 46c4549..1c2fcab 100644
--- a/common/platform/api/quiche_logging.h
+++ b/common/platform/api/quiche_logging.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_LOGGING_H_
 #define QUICHE_COMMON_PLATFORM_API_QUICHE_LOGGING_H_
 
-#include "net/quiche/common/platform/impl/quiche_logging_impl.h"
+#include "common/platform/impl/quiche_logging_impl.h"
 
 // Please note following QUICHE_LOG are platform dependent:
 // INFO severity can be degraded (to VLOG(1) or DVLOG(1)).
diff --git a/common/platform/api/quiche_str_cat.h b/common/platform/api/quiche_str_cat.h
index b996401..d6ef9f6 100644
--- a/common/platform/api/quiche_str_cat.h
+++ b/common/platform/api/quiche_str_cat.h
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "net/quiche/common/platform/impl/quiche_str_cat_impl.h"
+#include "common/platform/impl/quiche_str_cat_impl.h"
 
 namespace quiche {
 
diff --git a/common/platform/api/quiche_string_piece.h b/common/platform/api/quiche_string_piece.h
index ca58aae..62aad89 100644
--- a/common/platform/api/quiche_string_piece.h
+++ b/common/platform/api/quiche_string_piece.h
@@ -6,7 +6,7 @@
 #define QUICHE_COMMON_PLATFORM_API_QUICHE_STRING_PIECE_H_
 
 #include "absl/strings/string_view.h"
-#include "net/quiche/common/platform/impl/quiche_string_piece_impl.h"
+#include "common/platform/impl/quiche_string_piece_impl.h"
 
 namespace quiche {
 
diff --git a/common/platform/api/quiche_test.h b/common/platform/api/quiche_test.h
index b584752..ca980a7 100644
--- a/common/platform/api/quiche_test.h
+++ b/common/platform/api/quiche_test.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_COMMON_PLATFORM_API_QUICHE_TEST_H_
 #define QUICHE_COMMON_PLATFORM_API_QUICHE_TEST_H_
 
-#include "net/quiche/common/platform/impl/quiche_test_impl.h"
+#include "common/platform/impl/quiche_test_impl.h"
 
 using QuicheTest = quiche::test::QuicheTest;
 
diff --git a/common/platform/api/quiche_text_utils.h b/common/platform/api/quiche_text_utils.h
index 1d3a86e..8d4cac2 100644
--- a/common/platform/api/quiche_text_utils.h
+++ b/common/platform/api/quiche_text_utils.h
@@ -11,7 +11,7 @@
 #include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
 #include "common/platform/api/quiche_export.h"
-#include "net/quiche/common/platform/impl/quiche_text_utils_impl.h"
+#include "common/platform/impl/quiche_text_utils_impl.h"
 
 namespace quiche {
 
diff --git a/common/platform/api/quiche_time_utils.h b/common/platform/api/quiche_time_utils.h
index e1e59f0..90bc575 100644
--- a/common/platform/api/quiche_time_utils.h
+++ b/common/platform/api/quiche_time_utils.h
@@ -7,7 +7,7 @@
 
 #include <cstdint>
 
-#include "net/quiche/common/platform/impl/quiche_time_utils_impl.h"
+#include "common/platform/impl/quiche_time_utils_impl.h"
 
 namespace quiche {
 
diff --git a/common/platform/api/quiche_unordered_containers.h b/common/platform/api/quiche_unordered_containers.h
index a53a206..b7fbf64 100644
--- a/common/platform/api/quiche_unordered_containers.h
+++ b/common/platform/api/quiche_unordered_containers.h
@@ -7,7 +7,7 @@
 
 #include <functional>
 
-#include "net/quiche/common/platform/impl/quiche_unordered_containers_impl.h"
+#include "common/platform/impl/quiche_unordered_containers_impl.h"
 
 namespace quiche {
 
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 10e867c..9342bd6 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -370,7 +370,8 @@
       use_encryption_level_context_(
           encrypted_control_frames_ &&
           GetQuicReloadableFlag(quic_use_encryption_level_context)),
-      path_validator_(alarm_factory_, &arena_, this, random_generator_) {
+      path_validator_(alarm_factory_, &arena_, this, random_generator_),
+      most_recent_alternative_path_(QuicSocketAddress(), QuicSocketAddress()) {
   QUIC_BUG_IF(!start_peer_migration_earlier_ && send_path_response_);
 
   DCHECK(perspective_ == Perspective::IS_CLIENT ||
@@ -2348,6 +2349,7 @@
   if (debug_visitor_ != nullptr) {
     debug_visitor_->OnPacketReceived(self_address, peer_address, packet);
   }
+  current_incoming_packet_received_bytes_counted_ = false;
   last_size_ = packet.length();
   current_packet_data_ = packet.data();
 
@@ -2375,7 +2377,15 @@
 
   stats_.bytes_received += packet.length();
   ++stats_.packets_received;
-  if (EnforceAntiAmplificationLimit()) {
+  if (!count_bytes_on_alternative_path_seperately_) {
+    if (EnforceAntiAmplificationLimit()) {
+      bytes_received_before_address_validation_ += last_size_;
+    }
+  } else if (IsDefaultPath(last_packet_destination_address_,
+                           last_packet_source_address_) &&
+             EnforceAntiAmplificationLimit()) {
+    QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 1, 5);
+    current_incoming_packet_received_bytes_counted_ = true;
     bytes_received_before_address_validation_ += last_size_;
   }
 
@@ -2469,9 +2479,10 @@
   // evaluate if it's worth to send them before sending ACKs.
   while (!pending_path_challenge_payloads_.empty()) {
     QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response, 4, 5);
-    std::pair<QuicPathFrameBuffer, QuicSocketAddress> pair =
+    const PendingPathChallenge& pending_path_challenge =
         pending_path_challenge_payloads_.front();
-    if (!SendPathResponse(pair.first, pair.second)) {
+    if (!SendPathResponse(pending_path_challenge.received_path_challenge,
+                          pending_path_challenge.peer_address)) {
       break;
     }
     pending_path_challenge_payloads_.pop_front();
@@ -2683,7 +2694,18 @@
          QuicVersionUsesCryptoFrames(transport_version()))
       << ENDPOINT
       << "Handshake in STREAM frames should not check ShouldGeneratePacket";
-  return CanWrite(retransmittable);
+  if (!count_bytes_on_alternative_path_seperately_) {
+    return CanWrite(retransmittable);
+  }
+  QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 4, 5);
+  if (IsDefaultPath(self_address_, packet_creator_.peer_address())) {
+    return CanWrite(retransmittable);
+  }
+  // This is checking on the alternative path with a different peer address. The
+  // self address and the writer used are the same as the default path. In the
+  // case of different self address and writer, writing packet would use a
+  // differnt code path without checking the states of the default writer.
+  return connected_ && !HandleWriteBlocked();
 }
 
 const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() {
@@ -2739,7 +2761,9 @@
   if (LimitedByAmplificationFactor()) {
     // Server is constrained by the amplification restriction.
     QUIC_CODE_COUNT(quic_throttled_by_amplification_limit);
-    QUIC_DVLOG(1) << ENDPOINT << "Constrained by amplification restriction";
+    QUIC_DVLOG(1) << ENDPOINT
+                  << "Constrained by amplification restriction to peer address "
+                  << direct_peer_address_;
     ++stats_.num_amplification_throttling;
     return false;
   }
@@ -2753,7 +2777,7 @@
     return false;
   }
 
-  // Allow acks to be sent immediately.
+  // Allow acks and probing frames to be sent immediately.
   if (retransmittable == NO_RETRANSMITTABLE_DATA) {
     return true;
   }
@@ -2853,7 +2877,7 @@
                         : " ack or probing only ")
                 << ", encryption level: " << packet->encryption_level
                 << ", encrypted length:" << encrypted_length
-                << ", fate: " << fate;
+                << ", fate: " << fate << " to peer " << packet->peer_address;
   QUIC_DVLOG(2) << ENDPOINT << packet->encryption_level << " packet number "
                 << packet_number << " of length " << encrypted_length << ": "
                 << std::endl
@@ -3069,9 +3093,22 @@
   QUIC_DVLOG(1) << ENDPOINT << "time we began writing last sent packet: "
                 << packet_send_time.ToDebuggingValue();
 
-  if (EnforceAntiAmplificationLimit()) {
-    // Include bytes sent even if they are not in flight.
-    bytes_sent_before_address_validation_ += encrypted_length;
+  if (!count_bytes_on_alternative_path_seperately_) {
+    if (EnforceAntiAmplificationLimit()) {
+      // Include bytes sent even if they are not in flight.
+      bytes_sent_before_address_validation_ += encrypted_length;
+    }
+  } else {
+    QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 2, 5);
+    if (IsDefaultPath(self_address_, send_to_address)) {
+      if (EnforceAntiAmplificationLimit()) {
+        // Include bytes sent even if they are not in flight.
+        bytes_sent_before_address_validation_ += encrypted_length;
+      }
+    } else {
+      MaybeUpdateBytesSentToAlternativeAddress(send_to_address,
+                                               encrypted_length);
+    }
   }
 
   // Do not measure rtt of this packet if it's not sent on current path.
@@ -4633,7 +4670,30 @@
 
 void QuicConnection::UpdatePacketContent(QuicFrameType type) {
   if (version().HasIetfQuicFrames()) {
-    MaybeStartIetfPeerMigration(type);
+    if (!QuicUtils::IsProbingFrame(type)) {
+      MaybeStartIetfPeerMigration();
+      return;
+    }
+    QuicSocketAddress current_effective_peer_address =
+        GetEffectivePeerAddressFromCurrentPacket();
+    if (!count_bytes_on_alternative_path_seperately_ ||
+        IsDefaultPath(last_packet_destination_address_,
+                      last_packet_source_address_)) {
+      return;
+    }
+    QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 3, 5);
+    if (type == PATH_CHALLENGE_FRAME &&
+        !IsMostRecentAlternativePath(last_packet_destination_address_,
+                                     current_effective_peer_address)) {
+      // Only override most recent alternative path state upon a PATH_CHALLENGE.
+      QUIC_DVLOG(1)
+          << "The peer is probing a new path with effective peer address "
+          << current_effective_peer_address << ",  self address "
+          << last_packet_destination_address_;
+      most_recent_alternative_path_ = AlternativePathState(
+          last_packet_destination_address_, current_effective_peer_address);
+    }
+    MaybeUpdateBytesReceivedFromAlternativeAddress(last_size_);
     return;
   }
   // Packet content is tracked to identify connectivity probe in non-IETF
@@ -4697,9 +4757,9 @@
   current_effective_peer_migration_type_ = NO_CHANGE;
 }
 
-void QuicConnection::MaybeStartIetfPeerMigration(QuicFrameType type) {
+void QuicConnection::MaybeStartIetfPeerMigration() {
   DCHECK(version().HasIetfQuicFrames());
-  if (!start_peer_migration_earlier_ || QuicUtils::IsProbingFrame(type)) {
+  if (!start_peer_migration_earlier_) {
     return;
   }
   QUIC_CODE_COUNT(quic_start_peer_migration_earlier);
@@ -5083,8 +5143,22 @@
   // Account for added padding.
   if (length > coalesced_packet_.length()) {
     size_t padding_size = length - coalesced_packet_.length();
-    if (EnforceAntiAmplificationLimit()) {
-      bytes_sent_before_address_validation_ += padding_size;
+    if (!count_bytes_on_alternative_path_seperately_) {
+      if (EnforceAntiAmplificationLimit()) {
+        bytes_sent_before_address_validation_ += padding_size;
+      }
+    } else {
+      QUIC_CODE_COUNT_N(quic_count_bytes_on_alternative_path_seperately, 5, 5);
+      if (IsDefaultPath(coalesced_packet_.self_address(),
+                        coalesced_packet_.peer_address())) {
+        if (EnforceAntiAmplificationLimit()) {
+          // Include bytes sent even if they are not in flight.
+          bytes_sent_before_address_validation_ += padding_size;
+        }
+      } else {
+        MaybeUpdateBytesSentToAlternativeAddress(
+            coalesced_packet_.peer_address(), padding_size);
+      }
     }
     stats_.bytes_sent += padding_size;
     if (coalesced_packet_.initial_packet() != nullptr &&
@@ -5414,6 +5488,7 @@
       packet_creator_.SerializePathChallengeConnectivityProbingPacket(
           data_buffer);
   DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA);
+  DCHECK_EQ(self_address, most_recent_alternative_path_.self_address);
   WritePacketUsingWriter(std::move(probing_packet), writer, self_address,
                          peer_address, /*measure_rtt=*/false);
   return true;
@@ -5432,6 +5507,11 @@
 void QuicConnection::ValidatePath(
     std::unique_ptr<QuicPathValidationContext> context,
     std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) {
+  if (perspective_ == Perspective::IS_CLIENT &&
+      !IsDefaultPath(context->self_address(), context->peer_address())) {
+    most_recent_alternative_path_ =
+        AlternativePathState(context->self_address(), context->peer_address());
+  }
   path_validator_.StartPathValidation(std::move(context),
                                       std::move(result_delegate));
 }
@@ -5441,9 +5521,9 @@
   // Send PATH_RESPONSE using the provided peer address. If the creator has been
   // using a different peer address, it will flush before and after serializing
   // the current PATH_RESPONSE.
-  QUIC_DVLOG(1) << ENDPOINT << "Send PATH_RESPONSE to " << peer_address_to_send;
   QuicPacketCreator::ScopedPeerAddressContext context(&packet_creator_,
                                                       peer_address_to_send);
+  QUIC_DVLOG(1) << ENDPOINT << "Send PATH_RESPONSE to " << peer_address_to_send;
   return packet_creator_.AddPathResponseFrame(data_buffer);
 }
 
@@ -5496,5 +5576,74 @@
   }
 }
 
+void QuicConnection::MaybeUpdateBytesSentToAlternativeAddress(
+    const QuicSocketAddress& peer_address,
+    QuicByteCount sent_packet_size) {
+  if (!version().SupportsAntiAmplificationLimit() ||
+      perspective_ != Perspective::IS_SERVER) {
+    return;
+  }
+  DCHECK(!IsDefaultPath(self_address_, peer_address));
+  if (!IsMostRecentAlternativePath(self_address_, peer_address)) {
+    QUIC_DLOG(INFO) << "Wrote to uninteresting peer address: " << peer_address
+                    << " default direct_peer_address_ " << direct_peer_address_
+                    << " alternative path peer address "
+                    << most_recent_alternative_path_.peer_address;
+    return;
+  }
+  if (most_recent_alternative_path_.validated) {
+    return;
+  }
+  if (most_recent_alternative_path_.bytes_sent_before_address_validation_ >=
+      anti_amplification_factor_ *
+          most_recent_alternative_path_
+              .bytes_received_before_address_validation_) {
+    QUIC_LOG_FIRST_N(WARNING, 100)
+        << "Server sent more data than allowed to unverified alternative "
+           "peer address "
+        << peer_address << " bytes sent "
+        << most_recent_alternative_path_.bytes_sent_before_address_validation_
+        << ", bytes received "
+        << most_recent_alternative_path_
+               .bytes_received_before_address_validation_;
+  }
+  most_recent_alternative_path_.bytes_sent_before_address_validation_ +=
+      sent_packet_size;
+}
+
+void QuicConnection::MaybeUpdateBytesReceivedFromAlternativeAddress(
+    QuicByteCount received_packet_size) {
+  if (!version().SupportsAntiAmplificationLimit() ||
+      perspective_ != Perspective::IS_SERVER ||
+      !IsMostRecentAlternativePath(
+          last_packet_destination_address_,
+          GetEffectivePeerAddressFromCurrentPacket()) ||
+      current_incoming_packet_received_bytes_counted_) {
+    return;
+  }
+  // Only update bytes received if this probing frame is received on the most
+  // recent alternative path.
+  DCHECK(!IsDefaultPath(last_packet_destination_address_,
+                        GetEffectivePeerAddressFromCurrentPacket()));
+  if (!most_recent_alternative_path_.validated) {
+    most_recent_alternative_path_.bytes_received_before_address_validation_ +=
+        received_packet_size;
+  }
+  current_incoming_packet_received_bytes_counted_ = true;
+}
+
+bool QuicConnection::IsDefaultPath(
+    const QuicSocketAddress& self_address,
+    const QuicSocketAddress& peer_address) const {
+  return direct_peer_address_ == peer_address && self_address_ == self_address;
+}
+
+bool QuicConnection::IsMostRecentAlternativePath(
+    const QuicSocketAddress& self_address,
+    const QuicSocketAddress& peer_address) const {
+  return most_recent_alternative_path_.peer_address == peer_address &&
+         most_recent_alternative_path_.self_address == self_address;
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 156a5c6..48b560f 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1264,6 +1264,27 @@
  private:
   friend class test::QuicConnectionPeer;
 
+  struct QUIC_EXPORT_PRIVATE PendingPathChallenge {
+    QuicPathFrameBuffer received_path_challenge;
+    QuicSocketAddress peer_address;
+  };
+
+  struct QUIC_EXPORT_PRIVATE AlternativePathState {
+    AlternativePathState(const QuicSocketAddress& alternative_self_address,
+                         const QuicSocketAddress& alternative_peer_address)
+        : self_address(alternative_self_address),
+          peer_address(alternative_peer_address) {}
+
+    QuicSocketAddress self_address;
+    // The actual peer address behind the proxy if there is any.
+    QuicSocketAddress peer_address;
+    bool validated = false;
+    // Used by the sever to apply anti-amplification limit after this path
+    // becomes the default path if |peer_address| hasn't been validated.
+    QuicByteCount bytes_received_before_address_validation_ = 0;
+    QuicByteCount bytes_sent_before_address_validation_ = 0;
+  };
+
   using QueuedPacketList = std::list<SerializedPacket>;
 
   // BufferedPacket stores necessary information (encrypted buffer and self/peer
@@ -1542,7 +1563,7 @@
 
   // Called in IETF QUIC. Start peer migration if a non-probing frame is
   // received and the current packet number is largest received so far.
-  void MaybeStartIetfPeerMigration(QuicFrameType type);
+  void MaybeStartIetfPeerMigration();
 
   // Send PATH_RESPONSE to the given peer address.
   bool SendPathResponse(const QuicPathFrameBuffer& data_buffer,
@@ -1561,6 +1582,25 @@
                               const QuicSocketAddress& self_address,
                               const QuicSocketAddress& peer_address,
                               bool measure_rtt);
+
+  // Increment bytes sent/received on the most recent alternative path if the
+  // current packet is sent/received on that path.
+  void MaybeUpdateBytesSentToAlternativeAddress(
+      const QuicSocketAddress& peer_address,
+      QuicByteCount sent_packet_size);
+  void MaybeUpdateBytesReceivedFromAlternativeAddress(
+      QuicByteCount received_packet_size);
+
+  // Return true if the given self address and peer address is the same as the
+  // self address and peer address of the default path.
+  bool IsDefaultPath(const QuicSocketAddress& self_address,
+                     const QuicSocketAddress& peer_address) const;
+
+  // Return true if the given self address and peer address is the same as the
+  // self address and peer address of the most recent alternative path.
+  bool IsMostRecentAlternativePath(const QuicSocketAddress& self_address,
+                                   const QuicSocketAddress& peer_address) const;
+
   QuicFramer framer_;
 
   // Contents received in the current packet, especially used to identify
@@ -1866,8 +1906,7 @@
   // Buffer outstanding PATH_CHALLENGEs if socket write is blocked, future
   // OnCanWrite will attempt to respond with PATH_RESPONSEs using the retained
   // payload and peer addresses.
-  QuicCircularDeque<std::pair<QuicPathFrameBuffer, QuicSocketAddress>>
-      pending_path_challenge_payloads_;
+  QuicCircularDeque<PendingPathChallenge> pending_path_challenge_payloads_;
 
   // Set of connection IDs that should be accepted as destination on
   // received packets. This is conceptually a set but is implemented as a
@@ -1975,6 +2014,18 @@
   const bool use_encryption_level_context_;
 
   QuicPathValidator path_validator_;
+
+  // The most recent alternative path probed by an endpoint or its peer.
+  // It keeps track of the state of the probed path till it becomes the default
+  // path of the connection or replaced by a newer path under probing. The
+  // client starts to track it when it starts to validate the path. The server
+  // starts to track it when it receives a PATH_CHALLENGE in non-default path.
+  AlternativePathState most_recent_alternative_path_;
+
+  bool current_incoming_packet_received_bytes_counted_ = false;
+
+  bool count_bytes_on_alternative_path_seperately_ =
+      GetQuicReloadableFlag(quic_count_bytes_on_alternative_path_seperately);
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 7e75fa3..1baf44a 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_path_response_frame.h"
 #include "quic/core/quic_connection_id.h"
 #include "quic/core/quic_constants.h"
 #include "quic/core/quic_error_codes.h"
@@ -1843,6 +1844,50 @@
   EXPECT_EQ(0u, connection_.GetStats().packets_discarded);
 }
 
+class TestQuicPathValidationContext : public QuicPathValidationContext {
+ public:
+  TestQuicPathValidationContext(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 TestValidationResultDelegate : public QuicPathValidator::ResultDelegate {
+ public:
+  TestValidationResultDelegate(const QuicSocketAddress& expected_self_address,
+                               const QuicSocketAddress& expected_peer_address,
+                               bool* success)
+      : QuicPathValidator::ResultDelegate(),
+        expected_self_address_(expected_self_address),
+        expected_peer_address_(expected_peer_address),
+        success_(success) {}
+  void OnPathValidationSuccess(
+      std::unique_ptr<QuicPathValidationContext> context) override {
+    EXPECT_EQ(expected_self_address_, context->self_address());
+    EXPECT_EQ(expected_peer_address_, context->peer_address());
+    *success_ = true;
+  }
+
+  void OnPathValidationFailure(
+      std::unique_ptr<QuicPathValidationContext> context) override {
+    EXPECT_EQ(expected_self_address_, context->self_address());
+    EXPECT_EQ(expected_peer_address_, context->peer_address());
+    *success_ = false;
+  }
+
+ private:
+  QuicSocketAddress expected_self_address_;
+  QuicSocketAddress expected_peer_address_;
+  bool* success_;
+};
+
 // Receive a path probe request at the server side, i.e.,
 // in non-IETF version: receive a padded PING packet with a peer addess change;
 // in IETF version: receive a packet contains PATH CHALLENGE with peer address
@@ -1868,7 +1913,6 @@
       QuicEncryptedPacket(probing_packet->encrypted_buffer,
                           probing_packet->encrypted_length),
       clock_.Now()));
-
   uint64_t num_probing_received =
       connection_.GetStats().num_connectivity_probing_received;
   ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
@@ -1877,6 +1921,58 @@
             connection_.GetStats().num_connectivity_probing_received);
   EXPECT_EQ(kPeerAddress, connection_.peer_address());
   EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+  if (GetParam().version.HasIetfQuicFrames() &&
+      connection_.use_path_validator() &&
+      GetQuicReloadableFlag(quic_count_bytes_on_alternative_path_seperately)) {
+    QuicByteCount bytes_sent =
+        QuicConnectionPeer::BytesSentOnMostRecentAlternativePath(&connection_);
+    EXPECT_LT(0u, bytes_sent);
+    EXPECT_EQ(received->length(),
+              QuicConnectionPeer::BytesReceivedOnMostRecentAlternativePath(
+                  &connection_));
+
+    // Receiving one more probing packet should update the bytes count.
+    probing_packet = ConstructProbingPacket();
+    received.reset(ConstructReceivedPacket(
+        QuicEncryptedPacket(probing_packet->encrypted_buffer,
+                            probing_packet->encrypted_length),
+        clock_.Now()));
+    ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
+
+    EXPECT_EQ(num_probing_received + 2,
+              connection_.GetStats().num_connectivity_probing_received);
+    EXPECT_EQ(
+        2 * bytes_sent,
+        QuicConnectionPeer::BytesSentOnMostRecentAlternativePath(&connection_));
+    EXPECT_EQ(2 * received->length(),
+              QuicConnectionPeer::BytesReceivedOnMostRecentAlternativePath(
+                  &connection_));
+
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+        .Times(AtLeast(1u))
+        .WillOnce(Invoke(
+            [&]() { EXPECT_EQ(1u, writer_->path_challenge_frames().size()); }));
+
+    bool success = false;
+    connection_.ValidatePath(
+        std::make_unique<TestQuicPathValidationContext>(
+            connection_.self_address(), kNewPeerAddress, writer_.get()),
+        std::make_unique<TestValidationResultDelegate>(
+            connection_.self_address(), kNewPeerAddress, &success));
+    EXPECT_EQ(
+        3 * bytes_sent,
+        QuicConnectionPeer::BytesSentOnMostRecentAlternativePath(&connection_));
+
+    QuicFrames frames;
+    frames.push_back(QuicFrame(new QuicPathResponseFrame(
+        99, writer_->path_challenge_frames().front().data_buffer)));
+    ProcessFramesPacketWithAddresses(frames, connection_.self_address(),
+                                     kNewPeerAddress,
+                                     ENCRYPTION_FORWARD_SECURE);
+    EXPECT_EQ(3 * received->length(),
+              QuicConnectionPeer::BytesReceivedOnMostRecentAlternativePath(
+                  &connection_));
+  }
 
   // Process another packet with the old peer address on server side will not
   // start peer migration.
@@ -11074,50 +11170,6 @@
   EXPECT_TRUE(connection_.connected());
 }
 
-class TestQuicPathValidationContext : public QuicPathValidationContext {
- public:
-  TestQuicPathValidationContext(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 TestValidationResultDelegate : public QuicPathValidator::ResultDelegate {
- public:
-  TestValidationResultDelegate(const QuicSocketAddress& expected_self_address,
-                               const QuicSocketAddress& expected_peer_address,
-                               bool* success)
-      : QuicPathValidator::ResultDelegate(),
-        expected_self_address_(expected_self_address),
-        expected_peer_address_(expected_peer_address),
-        success_(success) {}
-  void OnPathValidationSuccess(
-      std::unique_ptr<QuicPathValidationContext> context) override {
-    EXPECT_EQ(expected_self_address_, context->self_address());
-    EXPECT_EQ(expected_peer_address_, context->peer_address());
-    *success_ = true;
-  }
-
-  void OnPathValidationFailure(
-      std::unique_ptr<QuicPathValidationContext> context) override {
-    EXPECT_EQ(expected_self_address_, context->self_address());
-    EXPECT_EQ(expected_peer_address_, context->peer_address());
-    *success_ = false;
-  }
-
- private:
-  QuicSocketAddress expected_self_address_;
-  QuicSocketAddress expected_peer_address_;
-  bool* success_;
-};
-
 TEST_P(QuicConnectionTest, PathValidationOnNewSocketSuccess) {
   if (!VersionHasIetfQuicFrames(connection_.version().transport_version) ||
       !connection_.use_path_validator()) {
@@ -11197,7 +11249,7 @@
       !connection_.send_path_response()) {
     return;
   }
-  PathProbeTestInit(Perspective::IS_SERVER);
+  PathProbeTestInit(Perspective::IS_CLIENT);
   if (version().SupportsAntiAmplificationLimit()) {
     QuicConnectionPeer::SetAddressValidated(&connection_);
   }
@@ -11319,7 +11371,7 @@
       !connection_.use_path_validator()) {
     return;
   }
-  PathProbeTestInit(Perspective::IS_SERVER);
+  PathProbeTestInit(Perspective::IS_CLIENT);
 
   writer_->SetShouldWriteFail();
   const QuicSocketAddress kNewPeerAddress(QuicIpAddress::Any4(), 12345);
@@ -11351,7 +11403,7 @@
       !connection_.use_path_validator()) {
     return;
   }
-  PathProbeTestInit(Perspective::IS_SERVER);
+  PathProbeTestInit(Perspective::IS_CLIENT);
 
   writer_->SetShouldWriteFail();
   writer_->SetWriteError(EMSGSIZE);
@@ -11616,8 +11668,7 @@
                                    ENCRYPTION_FORWARD_SECURE);
 
   EXPECT_EQ(
-      1u,
-      QuicConnectionPeer::pending_path_challenge_payloads(&connection_).size());
+      1u, QuicConnectionPeer::NumPendingPathChallengesToResponse(&connection_));
 
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(1));
   writer_->SetWritable();
@@ -11632,8 +11683,8 @@
   // PATH_RESPONSE should be sent in another packet to a different peer
   // address.
   EXPECT_EQ(kNewPeerAddress, writer_->last_write_peer_address());
-  EXPECT_TRUE(QuicConnectionPeer::pending_path_challenge_payloads(&connection_)
-                  .empty());
+  EXPECT_EQ(
+      0u, QuicConnectionPeer::NumPendingPathChallengesToResponse(&connection_));
 }
 
 // Regression test for b/168101557.
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 04a2244..1397140 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -20,6 +20,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_close_connection_on_0rtt_packet_number_higher_than_1rtt, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_bursts, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_count_bytes_on_alternative_path_seperately, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_enable_5rto_blackhole_detection2, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_on_pto, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false)
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index 3628908..5e0c65e 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -471,6 +471,8 @@
   // Return true if retry_token_ is not empty.
   bool HasRetryToken() const;
 
+  const QuicSocketAddress& peer_address() const { return packet_.peer_address; }
+
  private:
   friend class test::QuicPacketCreatorPeer;
 
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc
index fa99133..4989612 100644
--- a/quic/test_tools/quic_connection_peer.cc
+++ b/quic/test_tools/quic_connection_peer.cc
@@ -9,6 +9,7 @@
 #include "quic/core/quic_packet_writer.h"
 #include "quic/core/quic_received_packet_manager.h"
 #include "quic/platform/api/quic_flags.h"
+#include "quic/platform/api/quic_socket_address.h"
 #include "quic/test_tools/quic_framer_peer.h"
 #include "quic/test_tools/quic_sent_packet_manager_peer.h"
 
@@ -384,10 +385,9 @@
 }
 
 // static
-const QuicCircularDeque<std::pair<QuicPathFrameBuffer, QuicSocketAddress>>&
-QuicConnectionPeer::pending_path_challenge_payloads(
+size_t QuicConnectionPeer::NumPendingPathChallengesToResponse(
     QuicConnection* connection) {
-  return connection->pending_path_challenge_payloads_;
+  return connection->pending_path_challenge_payloads_.size();
 }
 
 void QuicConnectionPeer::SetConnectionClose(QuicConnection* connection) {
@@ -412,5 +412,19 @@
   return &connection->path_validator_;
 }
 
+//  static
+QuicByteCount QuicConnectionPeer::BytesSentOnMostRecentAlternativePath(
+    QuicConnection* connection) {
+  return connection->most_recent_alternative_path_
+      .bytes_sent_before_address_validation_;
+}
+
+//  static
+QuicByteCount QuicConnectionPeer::BytesReceivedOnMostRecentAlternativePath(
+    QuicConnection* connection) {
+  return connection->most_recent_alternative_path_
+      .bytes_sent_before_address_validation_;
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h
index 5b077b5..2795531 100644
--- a/quic/test_tools/quic_connection_peer.h
+++ b/quic/test_tools/quic_connection_peer.h
@@ -5,10 +5,12 @@
 #ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
 #define QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
 
+#include <cstddef>
 #include "absl/strings/string_view.h"
 #include "quic/core/quic_connection.h"
 #include "quic/core/quic_connection_stats.h"
 #include "quic/core/quic_packets.h"
+#include "quic/core/quic_types.h"
 #include "quic/platform/api/quic_socket_address.h"
 
 namespace quic {
@@ -158,9 +160,7 @@
 
   static size_t NumUndecryptablePackets(QuicConnection* connection);
 
-  static const QuicCircularDeque<
-      std::pair<QuicPathFrameBuffer, QuicSocketAddress>>&
-  pending_path_challenge_payloads(QuicConnection* connection);
+  static size_t NumPendingPathChallengesToResponse(QuicConnection* connection);
 
   static void SetConnectionClose(QuicConnection* connection);
 
@@ -170,6 +170,12 @@
                                               const QuicSocketAddress& address);
 
   static QuicPathValidator* path_validator(QuicConnection* connection);
+
+  static QuicByteCount BytesSentOnMostRecentAlternativePath(
+      QuicConnection* connection);
+
+  static QuicByteCount BytesReceivedOnMostRecentAlternativePath(
+      QuicConnection* connection);
 };
 
 }  // namespace test