gfe-relnote: In QUIC, add anti-amplification limit. Protected by version T099.

Anti-amplification limit kicks in before address validation. Now, server can only validate address by processing HANDSHAKE encrypted packet since address validation via token is not implemented.

In T099, deprecate HANDSHAKE_MODE and PTO is armed when 1) handshake is not confirmed 2) or there is packets in flight. Such that when PTO fires, at least 1 packet is sent to avoid handshake deadlock due to anti-amplification limit in case of packet losses.

PiperOrigin-RevId: 264960590
Change-Id: Ib2d9749b773af9328f96c176a49b2505be006b00
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 025962a..a70dd01 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -122,7 +122,8 @@
           GetQuicReloadableFlag(quic_loss_removes_from_inflight)),
       ignore_tlpr_if_no_pending_stream_data_(
           GetQuicReloadableFlag(quic_ignore_tlpr_if_no_pending_stream_data)),
-      fix_rto_retransmission_(false) {
+      fix_rto_retransmission_(false),
+      handshake_mode_disabled_(false) {
   if (loss_removes_from_inflight_) {
     QUIC_RELOADABLE_FLAG_COUNT(quic_loss_removes_from_inflight);
   }
@@ -726,7 +727,8 @@
 
 QuicSentPacketManager::RetransmissionTimeoutMode
 QuicSentPacketManager::OnRetransmissionTimeout() {
-  DCHECK(unacked_packets_.HasInFlightPackets());
+  DCHECK(unacked_packets_.HasInFlightPackets() ||
+         (handshake_mode_disabled_ && !handshake_confirmed_));
   DCHECK_EQ(0u, pending_timer_transmission_count_);
   // Handshake retransmission, timer based loss detection, TLP, and RTO are
   // implemented with a single alarm. The handshake alarm is set when the
@@ -735,6 +737,7 @@
   // The TLP alarm is always set to run for under an RTO.
   switch (GetRetransmissionMode()) {
     case HANDSHAKE_MODE:
+      DCHECK(!handshake_mode_disabled_);
       ++stats_->crypto_retransmit_count;
       RetransmitCryptoPackets();
       return HANDSHAKE_MODE;
@@ -918,10 +921,19 @@
   pending_timer_transmission_count_ = 1;
 }
 
+void QuicSentPacketManager::DisableHandshakeMode() {
+  DCHECK(session_decides_what_to_write());
+  fix_rto_retransmission_ = true;
+  pto_enabled_ = true;
+  handshake_mode_disabled_ = true;
+}
+
 QuicSentPacketManager::RetransmissionTimeoutMode
 QuicSentPacketManager::GetRetransmissionMode() const {
-  DCHECK(unacked_packets_.HasInFlightPackets());
-  if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) {
+  DCHECK(unacked_packets_.HasInFlightPackets() ||
+         (handshake_mode_disabled_ && !handshake_confirmed_));
+  if (!handshake_mode_disabled_ && !handshake_confirmed_ &&
+      unacked_packets_.HasPendingCryptoPackets()) {
     return HANDSHAKE_MODE;
   }
   if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
@@ -1007,10 +1019,17 @@
 }
 
 const QuicTime QuicSentPacketManager::GetRetransmissionTime() const {
-  // Don't set the timer if there is nothing to retransmit or we've already
-  // queued a tlp transmission and it hasn't been sent yet.
-  if (!unacked_packets_.HasInFlightPackets() ||
-      pending_timer_transmission_count_ > 0) {
+  if (!unacked_packets_.HasInFlightPackets() &&
+      (!handshake_mode_disabled_ || handshake_confirmed_ ||
+       unacked_packets_.perspective() == Perspective::IS_SERVER)) {
+    // Do not set the timer if there is nothing in flight. However, to avoid
+    // handshake deadlock due to anti-amplification limit, client needs to set
+    // PTO timer when the handshake is not confirmed even there is nothing in
+    // flight.
+    return QuicTime::Zero();
+  }
+  if (pending_timer_transmission_count_ > 0) {
+    // Do not set the timer if there is any credit left.
     return QuicTime::Zero();
   }
   if (!fix_rto_retransmission_ &&
@@ -1044,6 +1063,13 @@
       return std::max(tlp_time, rto_time);
     }
     case PTO_MODE: {
+      if (handshake_mode_disabled_ && !handshake_confirmed_ &&
+          !unacked_packets_.HasInFlightPackets()) {
+        DCHECK_EQ(Perspective::IS_CLIENT, unacked_packets_.perspective());
+        return std::max(clock_->ApproximateNow(),
+                        unacked_packets_.GetLastCryptoPacketSentTime() +
+                            GetProbeTimeoutDelay());
+      }
       // Ensure PTO never gets set to a time in the past.
       return std::max(
           clock_->ApproximateNow(),