gfe-relnote: fix path probe request in IETF QUIC, padded ping with peer address change should not be treated as path probe request. Only a packet contains PATH_CHALLENGE frame is considered as a path probe request. Protected by disabled QUIC version 99.

This CL also changes UpdatePacketContent to no-op in IETF version.

PiperOrigin-RevId: 296970477
Change-Id: Ida8c7e6a686c08508b936081c812cd14869c2137
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 0420637..4cfc95d 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -1191,13 +1191,6 @@
   // response.
   received_path_challenge_payloads_.push_back(frame.data_buffer);
 
-  // For VERSION 99 we define a "Padded PATH CHALLENGE" to be the same thing
-  // as a PADDED PING -- it will start a connectivity check and prevent
-  // connection migration. Insofar as the connectivity check and connection
-  // migration are concerned, logically the PATH CHALLENGE is the same as the
-  // PING, so as a stopgap, tell the FSM that determines whether we have a
-  // Padded PING or not that we received a PING.
-  UpdatePacketContent(FIRST_FRAME_IS_PING);
   should_last_packet_instigate_acks_ = true;
   return true;
 }
@@ -1214,7 +1207,6 @@
   }
   // Have received the matching PATH RESPONSE, saved payload no longer valid.
   transmitted_connectivity_probe_payload_ = nullptr;
-  UpdatePacketContent(FIRST_FRAME_IS_PING);
   return true;
 }
 
@@ -1404,6 +1396,7 @@
   }
 
   if (IsCurrentPacketConnectivityProbing()) {
+    DCHECK(!version().HasIetfQuicFrames());
     ++stats_.num_connectivity_probing_received;
   }
 
@@ -1420,6 +1413,7 @@
       << IsCurrentPacketConnectivityProbing();
 
   if (IsCurrentPacketConnectivityProbing()) {
+    DCHECK(!version().HasIetfQuicFrames());
     visitor_->OnPacketReceived(last_packet_destination_address_,
                                last_packet_source_address_,
                                /*is_connectivity_probe=*/true);
@@ -1436,29 +1430,24 @@
     visitor_->OnPacketReceived(last_packet_destination_address_,
                                last_packet_source_address_,
                                /*is_connectivity_probe=*/false);
-  } else {
-    // This node is not a client (is a server) AND the received packet was
-    // NOT connectivity-probing. If the packet had PATH CHALLENGES, send
-    // appropriate RESPONSE. Then deal with possible peer migration.
-    if (VersionHasIetfQuicFrames(transport_version()) &&
-        !received_path_challenge_payloads_.empty()) {
-      // If a PATH CHALLENGE was in a "Padded PING (or PATH CHALLENGE)"
-      // then it is taken care of above. This handles the case where a PATH
-      // CHALLENGE appeared someplace else (eg, the peer randomly added a PATH
-      // CHALLENGE frame to some other packet.
-      // There was at least one PATH CHALLENGE in the received packet,
-      // Generate the required PATH RESPONSE.
-      SendGenericPathProbePacket(nullptr, last_packet_source_address_,
-                                 /* is_response= */ true);
+  } else if (version().HasIetfQuicFrames() &&
+             !received_path_challenge_payloads_.empty()) {
+    if (current_effective_peer_migration_type_ != NO_CHANGE) {
+      // TODO(b/150095588): change the stats to
+      // num_valid_path_challenge_received.
+      ++stats_.num_connectivity_probing_received;
     }
-
-    if (last_header_.packet_number == GetLargestReceivedPacket()) {
-      direct_peer_address_ = last_packet_source_address_;
-      if (current_effective_peer_migration_type_ != NO_CHANGE) {
-        // TODO(fayang): When multiple packet number spaces is supported, only
-        // start peer migration for the application data.
-        StartEffectivePeerMigration(current_effective_peer_migration_type_);
-      }
+    // If the packet contains PATH CHALLENGE, send appropriate RESPONSE.
+    // There was at least one PATH CHALLENGE in the received packet,
+    // Generate the required PATH RESPONSE.
+    SendGenericPathProbePacket(nullptr, last_packet_source_address_,
+                               /* is_response=*/true);
+  } else if (last_header_.packet_number == GetLargestReceivedPacket()) {
+    direct_peer_address_ = last_packet_source_address_;
+    if (current_effective_peer_migration_type_ != NO_CHANGE) {
+      // TODO(fayang): When multiple packet number spaces is supported, only
+      // start peer migration for the application data.
+      StartEffectivePeerMigration(current_effective_peer_migration_type_);
     }
   }
 
@@ -3466,39 +3455,28 @@
                   << server_connection_id_;
 
   OwningSerializedPacketPointer probing_packet;
-  if (!VersionHasIetfQuicFrames(transport_version())) {
+  if (!version().HasIetfQuicFrames()) {
     // Non-IETF QUIC, generate a padded ping regardless of whether this is a
     // request or a response.
     probing_packet = packet_creator_.SerializeConnectivityProbingPacket();
+  } else if (is_response) {
+    // IETF QUIC path response.
+    // Respond to path probe request using IETF QUIC PATH_RESPONSE frame.
+    probing_packet =
+        packet_creator_.SerializePathResponseConnectivityProbingPacket(
+            received_path_challenge_payloads_,
+            /*is_padded=*/false);
+    received_path_challenge_payloads_.clear();
   } else {
-    if (is_response) {
-      // Respond using IETF QUIC PATH_RESPONSE frame
-      if (IsCurrentPacketConnectivityProbing()) {
-        // Pad the response if the request was a google connectivity probe
-        // (padded).
-        probing_packet =
-            packet_creator_.SerializePathResponseConnectivityProbingPacket(
-                received_path_challenge_payloads_, /* is_padded = */ true);
-        received_path_challenge_payloads_.clear();
-      } else {
-        // Do not pad the response if the path challenge was not a google
-        // connectivity probe.
-        probing_packet =
-            packet_creator_.SerializePathResponseConnectivityProbingPacket(
-                received_path_challenge_payloads_,
-                /* is_padded = */ false);
-        received_path_challenge_payloads_.clear();
-      }
-    } else {
-      // Request using IETF QUIC PATH_CHALLENGE frame
-      transmitted_connectivity_probe_payload_ =
-          std::make_unique<QuicPathFrameBuffer>();
-      probing_packet =
-          packet_creator_.SerializePathChallengeConnectivityProbingPacket(
-              transmitted_connectivity_probe_payload_.get());
-      if (!probing_packet) {
-        transmitted_connectivity_probe_payload_ = nullptr;
-      }
+    // IETF QUIC path challenge.
+    // Send a path probe request using IETF QUIC PATH_CHALLENGE frame.
+    transmitted_connectivity_probe_payload_ =
+        std::make_unique<QuicPathFrameBuffer>();
+    probing_packet =
+        packet_creator_.SerializePathChallengeConnectivityProbingPacket(
+            transmitted_connectivity_probe_payload_.get());
+    if (!probing_packet) {
+      transmitted_connectivity_probe_payload_ = nullptr;
     }
   }
 
@@ -3685,6 +3663,14 @@
 }
 
 void QuicConnection::UpdatePacketContent(PacketContent type) {
+  // Packet content is tracked to identify connectivity probe in non-IETF
+  // version, where a connectivity probe is defined as
+  // - a padded PING packet with peer address change received by server,
+  // - a padded PING packet on new path received by client.
+  if (version().HasIetfQuicFrames()) {
+    return;
+  }
+
   if (current_packet_content_ == NOT_PADDED_PING) {
     // We have already learned the current packet is not a connectivity
     // probing packet. Peer migration should have already been started earlier
@@ -3703,11 +3689,9 @@
     }
   }
 
-  // In Google QUIC we look for a packet with just a PING and PADDING.
-  // For IETF QUIC, the packet must consist of just a PATH_CHALLENGE frame,
-  // followed by PADDING. If the condition is met, mark things as
-  // connectivity-probing, causing later processing to generate the correct
-  // response.
+  // In Google QUIC, we look for a packet with just a PING and PADDING.
+  // If the condition is met, mark things as connectivity-probing, causing
+  // later processing to generate the correct response.
   if (type == SECOND_FRAME_IS_PADDING &&
       current_packet_content_ == FIRST_FRAME_IS_PING) {
     current_packet_content_ = SECOND_FRAME_IS_PADDING;
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index c17424e..9753ba1 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1149,6 +1149,12 @@
                                        QuicSocketAddress peer_address) {
     QuicFrames frames;
     frames.push_back(QuicFrame(frame));
+    return ProcessFramesPacketWithAddresses(frames, self_address, peer_address);
+  }
+
+  void ProcessFramesPacketWithAddresses(QuicFrames frames,
+                                        QuicSocketAddress self_address,
+                                        QuicSocketAddress peer_address) {
     QuicPacketCreatorPeer::SetSendVersionInPacket(
         &peer_creator_,
         QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) <
@@ -1984,7 +1990,7 @@
             connection_.active_effective_peer_migration_type());
 }
 
-TEST_P(QuicConnectionTest, ReceivePaddedPingAtServer) {
+TEST_P(QuicConnectionTest, ReceivePathProbeWithNoAddressChangeAtServer) {
   set_perspective(Perspective::IS_SERVER);
   QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
   EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
@@ -2016,17 +2022,8 @@
 
   // Process a padded PING or PATH CHALLENGE packet with no peer address change
   // on server side will be ignored.
-  OwningSerializedPacketPointer probing_packet;
-  if (VersionHasIetfQuicFrames(version().transport_version)) {
-    QuicPathFrameBuffer payload = {
-        {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}};
-    probing_packet =
-        QuicPacketCreatorPeer::SerializePathChallengeConnectivityProbingPacket(
-            &peer_creator_, &payload);
-  } else {
-    probing_packet = QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
-        &peer_creator_);
-  }
+  OwningSerializedPacketPointer probing_packet = ConstructProbingPacket();
+
   std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
       QuicEncryptedPacket(probing_packet->encrypted_buffer,
                           probing_packet->encrypted_length),
@@ -2091,7 +2088,11 @@
   EXPECT_EQ(0u, connection_.GetStats().packets_discarded);
 }
 
-TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtServer) {
+// 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
+// change.
+TEST_P(QuicConnectionTest, ReceivePathProbingAtServer) {
   set_perspective(Perspective::IS_SERVER);
   QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
   EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
@@ -2119,8 +2120,13 @@
   EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
 
   EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
-  EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1);
-
+  if (!GetParam().version.HasIetfQuicFrames()) {
+    EXPECT_CALL(visitor_,
+                OnPacketReceived(_, _, /*is_connectivity_probe=*/true))
+        .Times(1);
+  } else {
+    EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0);
+  }
   // Process a padded PING packet from a new peer address on server side
   // is effectively receiving a connectivity probing.
   const QuicSocketAddress kNewPeerAddress =
@@ -2149,7 +2155,90 @@
   EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
 }
 
-TEST_P(QuicConnectionTest, ReceiveReorderedConnectivityProbingAtServer) {
+// Receive a padded PING packet with a port change on server side.
+TEST_P(QuicConnectionTest, ReceivePaddedPingWithPortChangeAtServer) {
+  set_perspective(Perspective::IS_SERVER);
+  QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+  EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+
+  // Clear direct_peer_address.
+  QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+  // Clear effective_peer_address, it is the same as direct_peer_address for
+  // this test.
+  QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+                                              QuicSocketAddress());
+  EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+  QuicFrame frame;
+  if (GetParam().version.UsesCryptoFrames()) {
+    frame = QuicFrame(&crypto_frame_);
+    EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber());
+  } else {
+    frame = QuicFrame(QuicStreamFrame(
+        QuicUtils::GetCryptoStreamId(connection_.transport_version()), false,
+        0u, quiche::QuicheStringPiece()));
+    EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+  }
+  ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress);
+  EXPECT_EQ(kPeerAddress, connection_.peer_address());
+  EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+  if (GetParam().version.HasIetfQuicFrames()) {
+    // In IETF version, a padded PING packet with port change is not taken as
+    // connectivity probe.
+    EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
+    EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0);
+  } else {
+    // In non-IETF version, process a padded PING packet from a new peer
+    // address on server side is effectively receiving a connectivity probing.
+    EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+    EXPECT_CALL(visitor_,
+                OnPacketReceived(_, _, /*is_connectivity_probe=*/true))
+        .Times(1);
+  }
+  const QuicSocketAddress kNewPeerAddress =
+      QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+
+  QuicFrames frames;
+  // Write a PING frame, which has no data payload.
+  QuicPingFrame ping_frame;
+  frames.push_back(QuicFrame(ping_frame));
+
+  // Add padding to the rest of the packet.
+  QuicPaddingFrame padding_frame;
+  frames.push_back(QuicFrame(padding_frame));
+
+  uint64_t num_probing_received =
+      connection_.GetStats().num_connectivity_probing_received;
+
+  ProcessFramesPacketWithAddresses(frames, kSelfAddress, kNewPeerAddress);
+
+  if (GetParam().version.HasIetfQuicFrames()) {
+    // Padded PING with port changen is not considered as connectivity probe but
+    // a PORT CHANGE.
+    EXPECT_EQ(num_probing_received,
+              connection_.GetStats().num_connectivity_probing_received);
+    EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+    EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+  } else {
+    EXPECT_EQ(num_probing_received + 1,
+              connection_.GetStats().num_connectivity_probing_received);
+    EXPECT_EQ(kPeerAddress, connection_.peer_address());
+    EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+  }
+
+  // Process another packet with the old peer address on server side.
+  if (GetParam().version.HasIetfQuicFrames()) {
+    EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
+  } else {
+    EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+  }
+  ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress);
+  EXPECT_EQ(kPeerAddress, connection_.peer_address());
+  EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, ReceiveReorderedPathProbingAtServer) {
   set_perspective(Perspective::IS_SERVER);
   QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
   EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
@@ -2181,7 +2270,13 @@
   QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4);
 
   EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
-  EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1);
+  if (!GetParam().version.HasIetfQuicFrames()) {
+    EXPECT_CALL(visitor_,
+                OnPacketReceived(_, _, /*is_connectivity_probe=*/true))
+        .Times(1);
+  } else {
+    EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0);
+  }
 
   // Process a padded PING packet from a new peer address on server side
   // is effectively receiving a connectivity probing, even if a newer packet has
@@ -2233,7 +2328,13 @@
   EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
 
   EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
-  EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1);
+  if (!GetParam().version.HasIetfQuicFrames()) {
+    EXPECT_CALL(visitor_,
+                OnPacketReceived(_, _, /*is_connectivity_probe=*/true))
+        .Times(1);
+  } else {
+    EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0);
+  }
 
   // Process a padded PING packet from a new peer address on server side
   // is effectively receiving a connectivity probing.
@@ -2305,7 +2406,13 @@
   EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
 }
 
-TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtClient) {
+TEST_P(QuicConnectionTest, ReceiveConnectivityProbingResponseAtClient) {
+  // TODO(b/150095484): add test coverage for IETF to verify that client takes
+  // PATH RESPONSE with peer address change as correct validation on the new
+  // path.
+  if (GetParam().version.HasIetfQuicFrames()) {
+    return;
+  }
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   set_perspective(Perspective::IS_CLIENT);
   EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
@@ -2335,7 +2442,13 @@
   // Process a padded PING packet with a different self address on client side
   // is effectively receiving a connectivity probing.
   EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
-  EXPECT_CALL(visitor_, OnPacketReceived(_, _, true)).Times(1);
+  if (!GetParam().version.HasIetfQuicFrames()) {
+    EXPECT_CALL(visitor_,
+                OnPacketReceived(_, _, /*is_connectivity_probe=*/true))
+        .Times(1);
+  } else {
+    EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0);
+  }
 
   const QuicSocketAddress kNewSelfAddress =
       QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);