Ignore stateless reset packet received on a probing path.

Client side change only. not protected.

PiperOrigin-RevId: 312125302
Change-Id: I113d78c8c0a1443815a9b50cee9240b79c0a90f7
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 0747d4f..d692913 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -605,6 +605,7 @@
   // here.  (Check for a bug regression.)
   DCHECK_EQ(server_connection_id_, packet.connection_id);
   DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
+  DCHECK(!VersionHasIetfInvariantHeader(transport_version()));
   if (debug_visitor_ != nullptr) {
     debug_visitor_->OnPublicResetPacket(packet);
   }
@@ -1579,6 +1580,14 @@
     const QuicIetfStatelessResetPacket& /*packet*/) {
   // TODO(fayang): Add OnAuthenticatedIetfStatelessResetPacket to
   // debug_visitor_.
+  DCHECK(VersionHasIetfInvariantHeader(transport_version()));
+  DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
+  if (last_packet_destination_address_ != self_address()) {
+    // This packet is received on a probing path. Do not close connection.
+    visitor_->OnStatelessResetForProbing();
+    return;
+  }
+
   const std::string error_details = "Received stateless reset.";
   QUIC_CODE_COUNT(quic_tear_down_local_connection_on_stateless_reset);
   TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details,
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 6586fb2..f931567e 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -130,6 +130,9 @@
   // bandwidth.  Returns true if data was sent, false otherwise.
   virtual bool SendProbingData() = 0;
 
+  // Called when stateless reset packet is received but is on a different path.
+  virtual void OnStatelessResetForProbing() = 0;
+
   // Called when the connection experiences a change in congestion window.
   virtual void OnCongestionWindowChange(QuicTime now) = 0;
 
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 7fb8453..2e48f49 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -28,6 +28,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.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"
@@ -7368,6 +7369,33 @@
               IsError(QUIC_PUBLIC_RESET));
 }
 
+TEST_P(QuicConnectionTest, IetfStatelessResetOnProbingPath) {
+  if (!VersionHasIetfInvariantHeader(GetParam().version.transport_version)) {
+    return;
+  }
+  const QuicUint128 kTestStatelessResetToken = 1010101;
+  QuicConfig config;
+  QuicConfigPeer::SetReceivedStatelessResetToken(&config,
+                                                 kTestStatelessResetToken);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  connection_.SetFromConfig(config);
+
+  // Process a normal packet first to set the self address.
+  QuicReceivedPacket encrypted(nullptr, 0, QuicTime::Zero());
+  connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, encrypted);
+
+  std::unique_ptr<QuicEncryptedPacket> packet(
+      QuicFramer::BuildIetfStatelessResetPacket(connection_id_,
+                                                kTestStatelessResetToken));
+  std::unique_ptr<QuicReceivedPacket> received(
+      ConstructReceivedPacket(*packet, QuicTime::Zero()));
+  EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0);
+  EXPECT_CALL(visitor_, OnStatelessResetForProbing());
+  auto host = kSelfAddress.host();
+  QuicSocketAddress alternate_address(host, 80);
+  connection_.ProcessUdpPacket(alternate_address, kPeerAddress, *received);
+}
+
 TEST_P(QuicConnectionTest, GoAway) {
   if (VersionHasIetfQuicFrames(GetParam().version.transport_version)) {
     // GoAway is not available in version 99.
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 5a1ebbc..626349c 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -110,6 +110,7 @@
                         bool is_connectivity_probe) override;
   void OnCanWrite() override;
   bool SendProbingData() override;
+  void OnStatelessResetForProbing() override {}
   void OnCongestionWindowChange(QuicTime /*now*/) override {}
   void OnConnectionMigration(AddressChangeType /*type*/) override {}
   // Adds a connection level WINDOW_UPDATE frame.
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 7d1d9db..df91aab 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -498,6 +498,7 @@
   MOCK_METHOD(void, OnWriteBlocked, (), (override));
   MOCK_METHOD(void, OnCanWrite, (), (override));
   MOCK_METHOD(bool, SendProbingData, (), (override));
+  MOCK_METHOD(void, OnStatelessResetForProbing, (), (override));
   MOCK_METHOD(void, OnCongestionWindowChange, (QuicTime now), (override));
   MOCK_METHOD(void,
               OnConnectionMigration,
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h
index 8b9fd1f..b5be76f 100644
--- a/quic/test_tools/simulator/quic_endpoint.h
+++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -51,6 +51,7 @@
   void OnCryptoFrame(const QuicCryptoFrame& frame) override;
   void OnCanWrite() override;
   bool SendProbingData() override;
+  void OnStatelessResetForProbing() override {}
   bool WillingAndAbleToWrite() const override;
   bool HasPendingHandshake() const override;
   bool ShouldKeepConnectionAlive() const override;