gfe-relnote: In QUIC, skipping a packet number, when a single PTO packet will be sent, to elicit an immediate ACK. Protected by gfe2_reloadable_flag_quic_skip_packet_number_for_pto.

PiperOrigin-RevId: 268899762
Change-Id: I5c4313b8c9b2a01392f4728bd6364071657fa0d5
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 9b3559d..8adf0ae 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -190,6 +190,8 @@
                                                  // consecutive PTOs.
 const QuicTag k8PTO = TAG('8', 'P', 'T', 'O');   // Closes connection on 8
                                                  // consecutive PTOs.
+const QuicTag kPTOS = TAG('P', 'T', 'O', 'S');   // Skip packet number before
+                                                 // sending the last PTO.
 
 // Optional support of truncated Connection IDs.  If sent by a peer, the value
 // is the minimum number of bytes allowed for the connection ID sent to the
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index ebd2f45..49fc498 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -325,7 +325,8 @@
       max_consecutive_ptos_(0),
       bytes_received_before_address_validation_(0),
       bytes_sent_before_address_validation_(0),
-      address_validated_(false) {
+      address_validated_(false),
+      skip_packet_number_for_pto_(false) {
   QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID "
                   << server_connection_id
                   << " and version: " << ParsedQuicVersionToString(version());
@@ -432,6 +433,11 @@
       max_consecutive_ptos_ = 7;
       QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 4);
     }
+    if (GetQuicReloadableFlag(quic_skip_packet_number_for_pto) &&
+        config.HasClientSentConnectionOption(kPTOS, perspective_)) {
+      QUIC_RELOADABLE_FLAG_COUNT(quic_skip_packet_number_for_pto);
+      skip_packet_number_for_pto_ = true;
+    }
   }
   if (config.HasClientSentConnectionOption(kNSTP, perspective_)) {
     no_stop_waiting_frames_ = true;
@@ -2585,6 +2591,15 @@
 
   const auto retransmission_mode =
       sent_packet_manager_.OnRetransmissionTimeout();
+  if (skip_packet_number_for_pto_ &&
+      retransmission_mode == QuicSentPacketManager::PTO_MODE &&
+      sent_packet_manager_.pending_timer_transmission_count() == 1) {
+    // Skip a packet number when a single PTO packet is sent to elicit an
+    // immediate ACK.
+    packet_generator_.SkipNPacketNumbers(
+        1, sent_packet_manager_.GetLeastUnacked(),
+        sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length()));
+  }
   WriteIfNotBlocked();
 
   // A write failure can result in the connection being closed, don't attempt to
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index e214a99..c811e96 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1483,6 +1483,9 @@
   // packet has been successfully processed. Only used when
   // EnforceAntiAmplificationLimit returns true.
   bool address_validated_;
+
+  // If true, skip packet number before sending the last PTO retransmission.
+  bool skip_packet_number_for_pto_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 85489bc..2d07981 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -9160,6 +9160,37 @@
   connection_.GetRetransmissionAlarm()->Fire();
 }
 
+TEST_P(QuicConnectionTest, PtoSkipsPacketNumber) {
+  if (!connection_.session_decides_what_to_write() ||
+      !GetQuicReloadableFlag(quic_fix_rto_retransmission3)) {
+    return;
+  }
+  SetQuicReloadableFlag(quic_enable_pto, true);
+  SetQuicReloadableFlag(quic_skip_packet_number_for_pto, true);
+  QuicConfig config;
+  QuicTagVector connection_options;
+  connection_options.push_back(k1PTO);
+  connection_options.push_back(kPTOS);
+  config.SetConnectionOptionsToSend(connection_options);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  connection_.SetFromConfig(config);
+  EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+  QuicStreamId stream_id = 2;
+  QuicPacketNumber last_packet;
+  SendStreamDataToPeer(stream_id, "foooooo", 0, NO_FIN, &last_packet);
+  SendStreamDataToPeer(stream_id, "foooooo", 7, NO_FIN, &last_packet);
+  EXPECT_EQ(QuicPacketNumber(2), last_packet);
+  EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+  // Fire PTO and verify the PTO retransmission skips one packet number.
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+  connection_.GetRetransmissionAlarm()->Fire();
+  EXPECT_EQ(1u, writer_->stream_frames().size());
+  EXPECT_EQ(QuicPacketNumber(4), writer_->last_packet_header().packet_number);
+  EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index 0b7a9f2..4c381d4 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -159,6 +159,33 @@
       framer_->transport_version(), QuicPacketNumber(delta * 4));
 }
 
+void QuicPacketCreator::SkipNPacketNumbers(
+    QuicPacketCount count,
+    QuicPacketNumber least_packet_awaited_by_peer,
+    QuicPacketCount max_packets_in_flight) {
+  if (!queued_frames_.empty()) {
+    // Don't change creator state if there are frames queued.
+    QUIC_BUG << "Called SkipNPacketNumbers with " << queued_frames_.size()
+             << " queued_frames.  First frame type:"
+             << queued_frames_.front().type
+             << " last frame type:" << queued_frames_.back().type;
+    return;
+  }
+  if (packet_.packet_number > packet_.packet_number + count) {
+    // Skipping count packet numbers causes packet number wrapping around,
+    // reject it.
+    QUIC_LOG(WARNING) << "Skipping " << count
+                      << " packet numbers causes packet number wrapping "
+                         "around, least_packet_awaited_by_peer: "
+                      << least_packet_awaited_by_peer
+                      << " packet_number:" << packet_.packet_number;
+    return;
+  }
+  packet_.packet_number += count;
+  // Packet number changes, update packet number length if necessary.
+  UpdatePacketNumberLength(least_packet_awaited_by_peer, max_packets_in_flight);
+}
+
 bool QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level,
                                           size_t write_length,
                                           QuicStreamOffset offset,
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index 8e3ea3f..9f00842 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -82,6 +82,11 @@
   void UpdatePacketNumberLength(QuicPacketNumber least_packet_awaited_by_peer,
                                 QuicPacketCount max_packets_in_flight);
 
+  // Skip |count| packet numbers.
+  void SkipNPacketNumbers(QuicPacketCount count,
+                          QuicPacketNumber least_packet_awaited_by_peer,
+                          QuicPacketCount max_packets_in_flight);
+
   // The overhead the framing will add for a packet with one frame.
   static size_t StreamFramePacketOverhead(
       QuicTransportVersion version,
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index e7c82d1..d0cfdae 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -1181,6 +1181,36 @@
             QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
 }
 
+TEST_P(QuicPacketCreatorTest, SkipNPacketNumbers) {
+  QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1);
+  if (VersionHasIetfInvariantHeader(GetParam().version.transport_version) &&
+      !GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) {
+    EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+              QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+    creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+  } else {
+    EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
+              QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+  }
+  creator_.SkipNPacketNumbers(63, QuicPacketNumber(2),
+                              10000 / kDefaultMaxPacketSize);
+  EXPECT_EQ(QuicPacketNumber(64), creator_.packet_number());
+  EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
+            QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+  creator_.SkipNPacketNumbers(64 * 255, QuicPacketNumber(2),
+                              10000 / kDefaultMaxPacketSize);
+  EXPECT_EQ(QuicPacketNumber(64 * 256), creator_.packet_number());
+  EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER,
+            QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+  creator_.SkipNPacketNumbers(64 * 256 * 255, QuicPacketNumber(2),
+                              10000 / kDefaultMaxPacketSize);
+  EXPECT_EQ(QuicPacketNumber(64 * 256 * 256), creator_.packet_number());
+  EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+            QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+}
+
 TEST_P(QuicPacketCreatorTest, SerializeFrame) {
   if (!GetParam().version_serialization) {
     creator_.StopSendingVersion();
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc
index 160222f..d4e879f 100644
--- a/quic/core/quic_packet_generator.cc
+++ b/quic/core/quic_packet_generator.cc
@@ -337,6 +337,14 @@
                                                   max_packets_in_flight);
 }
 
+void QuicPacketGenerator::SkipNPacketNumbers(
+    QuicPacketCount count,
+    QuicPacketNumber least_packet_awaited_by_peer,
+    QuicPacketCount max_packets_in_flight) {
+  packet_creator_.SkipNPacketNumbers(count, least_packet_awaited_by_peer,
+                                     max_packets_in_flight);
+}
+
 void QuicPacketGenerator::SetServerConnectionIdLength(uint32_t length) {
   if (length == 0) {
     packet_creator_.SetServerConnectionIdIncluded(CONNECTION_ID_ABSENT);
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h
index c6902e0..69c69ea 100644
--- a/quic/core/quic_packet_generator.h
+++ b/quic/core/quic_packet_generator.h
@@ -172,6 +172,11 @@
   void UpdatePacketNumberLength(QuicPacketNumber least_packet_awaited_by_peer,
                                 QuicPacketCount max_packets_in_flight);
 
+  // Skip |count| packet numbers.
+  void SkipNPacketNumbers(QuicPacketCount count,
+                          QuicPacketNumber least_packet_awaited_by_peer,
+                          QuicPacketCount max_packets_in_flight);
+
   // Set the minimum number of bytes for the server connection id length;
   void SetServerConnectionIdLength(uint32_t length);