In IETF quic, start peer migration by switching peer address as soon as detecting a non-probing frame in the incoming packet rather than after finishing parsing the whole packet. Protected by --gfe2_reloadable_flag_quic_start_peer_migration_earlier.

Doing so will fix the bug in IETF QUIC that a server may send in-cache response to old address while the end of current packet processing isn't reached

PiperOrigin-RevId: 328429809
Change-Id: Ib07274bdef3944240d43defc4183ac03cdd47bba
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index f0769fd..c44e5cf 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -1775,7 +1775,8 @@
   }
   // Server starts to migrate connection upon receiving of non-probing packet
   // from a new peer address.
-  if (last_header_.packet_number == GetLargestReceivedPacket()) {
+  if (!start_peer_migration_earlier_ &&
+      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
@@ -4125,14 +4126,14 @@
 }
 
 void QuicConnection::UpdatePacketContent(QuicFrameType type) {
+  if (version().HasIetfQuicFrames()) {
+    MaybeStartIetfPeerMigration(type);
+    return;
+  }
   // 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()) {
-    // TODO(b/149315151) detect IETF non-probing packet.
-    return;
-  }
 
   if (current_packet_content_ == NOT_PADDED_PING) {
     // We have already learned the current packet is not a connectivity
@@ -4184,6 +4185,24 @@
     if (current_effective_peer_migration_type_ != NO_CHANGE) {
       // Start effective peer migration immediately when the current packet is
       // confirmed not a connectivity probing packet.
+      StartEffectivePeerMigration(current_effective_peer_migration_type_);
+    }
+  }
+  current_effective_peer_migration_type_ = NO_CHANGE;
+}
+
+void QuicConnection::MaybeStartIetfPeerMigration(QuicFrameType type) {
+  DCHECK(version().HasIetfQuicFrames());
+  if (!start_peer_migration_earlier_ || QuicUtils::IsProbingFrame(type)) {
+    return;
+  }
+  QUIC_CODE_COUNT(quic_start_peer_migration_earlier);
+  if (GetLargestReceivedPacket().IsInitialized() &&
+      last_header_.packet_number == GetLargestReceivedPacket()) {
+    direct_peer_address_ = last_packet_source_address_;
+    if (current_effective_peer_migration_type_ != NO_CHANGE) {
+      // Start effective peer migration when the current packet contains a
+      // non-probing frame.
       // TODO(fayang): When multiple packet number spaces is supported, only
       // start peer migration for the application data.
       StartEffectivePeerMigration(current_effective_peer_migration_type_);
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 30d91d1..f6584ff 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1368,6 +1368,10 @@
   // deprecated.
   void MaybeRespondToConnectivityProbingOrMigration();
 
+  // 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);
+
   QuicFramer framer_;
 
   // Contents received in the current packet, especially used to identify
@@ -1723,6 +1727,9 @@
 
   size_t anti_amplification_factor_ =
       GetQuicFlag(FLAGS_quic_anti_amplification_factor);
+
+  bool start_peer_migration_earlier_ =
+      GetQuicReloadableFlag(quic_start_peer_migration_earlier);
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 0f1a8dc..e4cc987 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -375,8 +375,9 @@
   WriteResult WritePacket(const char* buffer,
                           size_t buf_len,
                           const QuicIpAddress& /*self_address*/,
-                          const QuicSocketAddress& /*peer_address*/,
+                          const QuicSocketAddress& peer_address,
                           PerPacketOptions* /*options*/) override {
+    last_write_peer_address_ = peer_address;
     // If the buffer is allocated from the pool, return it back to the pool.
     // Note the buffer content doesn't change.
     if (packet_buffer_pool_index_.find(const_cast<char*>(buffer)) !=
@@ -621,6 +622,10 @@
 
   SimpleQuicFramer* framer() { return &framer_; }
 
+  QuicSocketAddress last_write_peer_address() const {
+    return last_write_peer_address_;
+  }
+
  private:
   char* AllocPacketBuffer() {
     PacketBuffer* p = packet_buffer_free_list_.front();
@@ -673,6 +678,8 @@
   QuicHashMap<char*, PacketBuffer*> packet_buffer_pool_index_;
   // Indices in packet_buffer_pool_ that are not allocated.
   std::list<PacketBuffer*> packet_buffer_free_list_;
+  // The peer address passed into WritePacket().
+  QuicSocketAddress last_write_peer_address_;
 };
 
 class TestConnection : public QuicConnection {
@@ -1958,6 +1965,11 @@
   set_perspective(Perspective::IS_SERVER);
   QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
   EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+  peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+  // Prevent packets from being coalesced.
+  EXPECT_CALL(visitor_, GetHandshakeState())
+      .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
 
   // Clear direct_peer_address.
   QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
@@ -1967,23 +1979,30 @@
                                               QuicSocketAddress());
   EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
 
-  if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
-    EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber());
-  } else {
-    EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
-  }
-  ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress,
-                                  kPeerAddress);
+  const QuicSocketAddress kNewPeerAddress =
+      QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+  EXPECT_CALL(visitor_, OnStreamFrame(_))
+      .WillOnce(Invoke(
+          [=]() { EXPECT_EQ(kPeerAddress, connection_.peer_address()); }))
+      .WillOnce(Invoke([=]() {
+        EXPECT_EQ((GetQuicReloadableFlag(quic_start_peer_migration_earlier) ||
+                           !GetParam().version.HasIetfQuicFrames()
+                       ? kNewPeerAddress
+                       : kPeerAddress),
+                  connection_.peer_address());
+      }));
+  QuicFrames frames;
+  frames.push_back(QuicFrame(frame1_));
+  ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress);
   EXPECT_EQ(kPeerAddress, connection_.peer_address());
   EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
 
   // Process another packet with a different peer address on server side will
   // start connection migration.
-  const QuicSocketAddress kNewPeerAddress =
-      QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
   EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
-  ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress,
-                                  kNewPeerAddress);
+  QuicFrames frames2;
+  frames2.push_back(QuicFrame(frame2_));
+  ProcessFramesPacketWithAddresses(frames2, kSelfAddress, kNewPeerAddress);
   EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
   EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
 }
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index 367db32..13f6a2a 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -654,5 +654,18 @@
   }
 }
 
+// static
+bool QuicUtils::IsProbingFrame(QuicFrameType type) {
+  switch (type) {
+    case PATH_CHALLENGE_FRAME:
+    case PATH_RESPONSE_FRAME:
+    case NEW_CONNECTION_ID_FRAME:
+    case PADDING_FRAME:
+      return true;
+    default:
+      return false;
+  }
+}
+
 #undef RETURN_STRING_LITERAL  // undef for jumbo builds
 }  // namespace quic
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h
index 9e190e0..05c7124 100644
--- a/quic/core/quic_utils.h
+++ b/quic/core/quic_utils.h
@@ -230,6 +230,9 @@
   // exceeds this value, it will result in a stream ID that exceeds the
   // implementation limit on stream ID size.
   static QuicStreamCount GetMaxStreamCount();
+
+  // Return true if this frame is an IETF probing frame.
+  static bool IsProbingFrame(QuicFrameType type);
 };
 
 template <typename Mask>