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_connection.cc b/quic/core/quic_connection.cc
index 6b6a3a6..6d41a61 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -322,7 +322,10 @@
       supports_release_time_(false),
       release_time_into_future_(QuicTime::Delta::Zero()),
       retry_has_been_parsed_(false),
-      max_consecutive_ptos_(0) {
+      max_consecutive_ptos_(0),
+      bytes_received_before_address_validation_(0),
+      bytes_sent_before_address_validation_(0),
+      address_validated_(false) {
   QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID "
                   << server_connection_id
                   << " and version: " << ParsedQuicVersionToString(version());
@@ -765,6 +768,11 @@
 void QuicConnection::OnDecryptedPacket(EncryptionLevel level) {
   last_decrypted_packet_level_ = level;
   last_packet_decrypted_ = true;
+  if (EnforceAntiAmplificationLimit() &&
+      last_decrypted_packet_level_ >= ENCRYPTION_HANDSHAKE) {
+    // Address is validated by successfully processing a HANDSHAKE packet.
+    address_validated_ = true;
+  }
 
   // Once the server receives a forward secure packet, the handshake is
   // confirmed.
@@ -1517,7 +1525,9 @@
     QUIC_BUG << "Attempt to send empty crypto frame";
     return 0;
   }
-
+  if (!ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, IS_HANDSHAKE)) {
+    return 0;
+  }
   ScopedPacketFlusher flusher(this);
   return packet_generator_.ConsumeCryptoData(level, write_length, offset);
 }
@@ -1543,7 +1553,11 @@
 bool QuicConnection::SendControlFrame(const QuicFrame& frame) {
   if (SupportsMultiplePacketNumberSpaces() &&
       (encryption_level_ == ENCRYPTION_INITIAL ||
-       encryption_level_ == ENCRYPTION_HANDSHAKE)) {
+       encryption_level_ == ENCRYPTION_HANDSHAKE) &&
+      frame.type != PING_FRAME) {
+    // Allow PING frame to be sent without APPLICATION key. For example, when
+    // anti-amplification limit is used, client needs to send something to avoid
+    // handshake deadlock.
     QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame
                   << " at encryption level: "
                   << QuicUtils::EncryptionLevelToString(encryption_level_);
@@ -1713,6 +1727,9 @@
 
   stats_.bytes_received += packet.length();
   ++stats_.packets_received;
+  if (EnforceAntiAmplificationLimit()) {
+    bytes_received_before_address_validation_ += last_size_;
+  }
 
   // Ensure the time coming from the packet reader is within 2 minutes of now.
   if (std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) >
@@ -2032,6 +2049,11 @@
   // We should serialize handshake packets immediately to ensure that they
   // end up sent at the right encryption level.
   if (handshake == IS_HANDSHAKE) {
+    if (LimitedByAmplificationFactor()) {
+      // Server is constrained by the amplification restriction.
+      QUIC_DVLOG(1) << ENDPOINT << "Constrained by amplification restriction";
+      return false;
+    }
     return true;
   }
 
@@ -2252,11 +2274,16 @@
   QUIC_DVLOG(1) << ENDPOINT << "time we began writing last sent packet: "
                 << packet_send_time.ToDebuggingValue();
 
-  bool reset_retransmission_alarm = sent_packet_manager_.OnPacketSent(
+  if (EnforceAntiAmplificationLimit()) {
+    // Include bytes sent even if they are not in flight.
+    bytes_sent_before_address_validation_ += packet->encrypted_length;
+  }
+
+  const bool in_flight = sent_packet_manager_.OnPacketSent(
       packet, packet->original_packet_number, packet_send_time,
       packet->transmission_type, IsRetransmittable(*packet));
 
-  if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) {
+  if (in_flight || !retransmission_alarm_->IsSet()) {
     SetRetransmissionAlarm();
   }
   SetPingAlarm();
@@ -2497,7 +2524,9 @@
 }
 
 void QuicConnection::OnRetransmissionTimeout() {
-  DCHECK(!sent_packet_manager_.unacked_packets().empty());
+  DCHECK(!sent_packet_manager_.unacked_packets().empty() ||
+         (sent_packet_manager_.handshake_mode_disabled() &&
+          !sent_packet_manager_.handshake_confirmed()));
   const QuicPacketNumber previous_created_packet_number =
       packet_generator_.packet_number();
   if (close_connection_after_five_rtos_ &&
@@ -2993,8 +3022,14 @@
     pending_retransmission_alarm_ = true;
     return;
   }
-  QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
-  retransmission_alarm_->Update(retransmission_time,
+  if (LimitedByAmplificationFactor()) {
+    // Do not set retransmission timer if connection is anti-amplification limit
+    // throttled. Otherwise, nothing can be sent when timer fires.
+    retransmission_alarm_->Cancel();
+    return;
+  }
+
+  retransmission_alarm_->Update(sent_packet_manager_.GetRetransmissionTime(),
                                 QuicTime::Delta::FromMilliseconds(1));
 }
 
@@ -3517,6 +3552,9 @@
       transport_version() > QUIC_VERSION_39;
   sent_packet_manager_.SetSessionDecideWhatToWrite(
       enable_session_decides_what_to_write);
+  if (version().SupportsAntiAmplificationLimit()) {
+    sent_packet_manager_.DisableHandshakeMode();
+  }
   packet_generator_.SetCanSetTransmissionType(
       enable_session_decides_what_to_write);
 }
@@ -3765,6 +3803,18 @@
       last_decrypted_packet_level_);
 }
 
+bool QuicConnection::EnforceAntiAmplificationLimit() const {
+  return version().SupportsAntiAmplificationLimit() &&
+         perspective_ == Perspective::IS_SERVER && !address_validated_;
+}
+
+bool QuicConnection::LimitedByAmplificationFactor() const {
+  return EnforceAntiAmplificationLimit() &&
+         bytes_sent_before_address_validation_ >=
+             GetQuicFlag(FLAGS_quic_anti_amplification_factor) *
+                 bytes_received_before_address_validation_;
+}
+
 size_t QuicConnection::min_received_before_ack_decimation() const {
   return uber_received_packet_manager_.min_received_before_ack_decimation();
 }
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 9754c14..5572f5d 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1131,6 +1131,12 @@
   // Whether incoming_connection_ids_ contains connection_id.
   bool HasIncomingConnectionId(QuicConnectionId connection_id);
 
+  // Whether connection enforces anti-amplification limit.
+  bool EnforceAntiAmplificationLimit() const;
+
+  // Whether connection is limited by amplification factor.
+  bool LimitedByAmplificationFactor() const;
+
   QuicFramer framer_;
 
   // Contents received in the current packet, especially used to identify
@@ -1440,6 +1446,20 @@
   // If max_consecutive_ptos_ > 0, close connection if consecutive PTOs is
   // greater than max_consecutive_ptos.
   size_t max_consecutive_ptos_;
+
+  // Bytes received before address validation. Only used when
+  // EnforceAntiAmplificationLimit returns true.
+  size_t bytes_received_before_address_validation_;
+
+  // Bytes sent before address validation. Only used when
+  // EnforceAntiAmplificationLimit returns true.
+  size_t bytes_sent_before_address_validation_;
+
+  // True if peer address has been validated. Address is considered validated
+  // when 1) an address token is received and validated, or 2) a HANDSHAKE
+  // packet has been successfully processed. Only used when
+  // EnforceAntiAmplificationLimit returns true.
+  bool address_validated_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 41b02f8..13a72d8 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -667,6 +667,12 @@
     if (!QuicUtils::IsCryptoStreamId(transport_version(), id) &&
         this->encryption_level() == ENCRYPTION_INITIAL) {
       this->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+      if (perspective() == Perspective::IS_CLIENT && !IsHandshakeConfirmed()) {
+        OnHandshakeComplete();
+      }
+      if (version().SupportsAntiAmplificationLimit()) {
+        QuicConnectionPeer::SetAddressValidated(this);
+      }
     }
     struct iovec iov;
     MakeIOVector(data, &iov);
@@ -823,6 +829,16 @@
     next_effective_peer_addr_ = QuicMakeUnique<QuicSocketAddress>(addr);
   }
 
+  bool PtoEnabled() {
+    if (QuicConnectionPeer::GetSentPacketManager(this)->pto_enabled()) {
+      // PTO mode is default enabled for T099. And TLP/RTO related tests are
+      // stale.
+      DCHECK_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99), version());
+      return true;
+    }
+    return false;
+  }
+
   SimpleDataProducer* producer() { return &producer_; }
 
   using QuicConnection::active_effective_peer_migration_type;
@@ -1215,6 +1231,7 @@
     } else {
       frames.push_back(QuicFrame(frame1_));
     }
+    frames.push_back(QuicFrame(QuicPaddingFrame(-1)));
     std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames);
     char buffer[kMaxOutgoingPacketSize];
     peer_creator_.set_encryption_level(ENCRYPTION_INITIAL);
@@ -3620,6 +3637,9 @@
 }
 
 TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   connection_.SetMaxTailLossProbes(0);
 
   for (int i = 0; i < 10; ++i) {
@@ -3957,7 +3977,8 @@
 }
 
 TEST_P(QuicConnectionTest, TailLossProbeDelayForStreamDataInTLPR) {
-  if (!connection_.session_decides_what_to_write()) {
+  if (!connection_.session_decides_what_to_write() ||
+      connection_.PtoEnabled()) {
     return;
   }
 
@@ -3992,7 +4013,8 @@
 }
 
 TEST_P(QuicConnectionTest, TailLossProbeDelayForNonStreamDataInTLPR) {
-  if (!connection_.session_decides_what_to_write()) {
+  if (!connection_.session_decides_what_to_write() ||
+      connection_.PtoEnabled()) {
     return;
   }
 
@@ -4133,6 +4155,9 @@
 }
 
 TEST_P(QuicConnectionTest, RTO) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   connection_.SetMaxTailLossProbes(0);
 
   QuicTime default_retransmission_time =
@@ -4154,7 +4179,8 @@
 
 // Regression test of b/133771183.
 TEST_P(QuicConnectionTest, RtoWithNoDataToRetransmit) {
-  if (!connection_.session_decides_what_to_write()) {
+  if (!connection_.session_decides_what_to_write() ||
+      connection_.PtoEnabled()) {
     return;
   }
   connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
@@ -4348,6 +4374,9 @@
 }
 
 TEST_P(QuicConnectionTest, TestRetransmitOrder) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   connection_.SetMaxTailLossProbes(0);
 
   QuicByteCount first_packet_size;
@@ -4433,6 +4462,9 @@
 }
 
 TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   connection_.SetMaxTailLossProbes(0);
 
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -5136,6 +5168,9 @@
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfterRetransmission) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   EXPECT_TRUE(connection_.connected());
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
@@ -5286,6 +5321,9 @@
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseAndTLP) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   // Same test as above, but complete a handshake which enables silent close,
   // but sending TLPs causes the connection close to be sent.
   EXPECT_TRUE(connection_.connected());
@@ -5512,6 +5550,9 @@
 }
 
 TEST_P(QuicConnectionTest, TimeoutAfter5ClientRTOs) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   connection_.SetMaxTailLossProbes(2);
   EXPECT_TRUE(connection_.connected());
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
@@ -6901,6 +6942,9 @@
 }
 
 TEST_P(QuicConnectionTest, CheckSendStats) {
+  if (connection_.PtoEnabled()) {
+    return;
+  }
   connection_.SetMaxTailLossProbes(0);
 
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
@@ -8838,7 +8882,8 @@
 // Regresstion test for b/139375344.
 TEST_P(QuicConnectionTest, RtoForcesSendingPing) {
   if (!QuicConnectionPeer::GetSentPacketManager(&connection_)
-           ->fix_rto_retransmission()) {
+           ->fix_rto_retransmission() ||
+      connection_.PtoEnabled()) {
     return;
   }
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
@@ -8990,6 +9035,91 @@
   TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS);
 }
 
+TEST_P(QuicConnectionTest, DeprecateHandshakeMode) {
+  if (!connection_.version().SupportsAntiAmplificationLimit()) {
+    return;
+  }
+  EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+  EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+  // Send CHLO.
+  connection_.SendCryptoStreamData();
+  EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+  EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  QuicAckFrame frame1 = InitAckFrame(1);
+  // Received ACK for packet 1.
+  ProcessFramePacketAtLevel(1, QuicFrame(&frame1), ENCRYPTION_INITIAL);
+
+  // Verify retransmission alarm is still set because handshake is not
+  // confirmed although there is nothing in flight.
+  EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+  EXPECT_EQ(0u, connection_.GetStats().pto_count);
+  EXPECT_EQ(0u, connection_.GetStats().crypto_retransmit_count);
+
+  // PTO fires, verify a PING packet gets sent because there is no data to send.
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _));
+  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  connection_.GetRetransmissionAlarm()->Fire();
+  EXPECT_EQ(1u, connection_.GetStats().pto_count);
+  EXPECT_EQ(0u, connection_.GetStats().crypto_retransmit_count);
+  EXPECT_EQ(1u, writer_->ping_frames().size());
+}
+
+TEST_P(QuicConnectionTest, AntiAmplificationLimit) {
+  if (!connection_.version().SupportsAntiAmplificationLimit()) {
+    return;
+  }
+  EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+  EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber());
+
+  set_perspective(Perspective::IS_SERVER);
+  // Verify no data can be sent at the beginning because bytes received is 0.
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  connection_.SendCryptoDataWithString("foo", 0);
+  EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+  // Receives packet 1.
+  ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL);
+
+  const size_t anti_amplification_factor =
+      GetQuicFlag(FLAGS_quic_anti_amplification_factor);
+  // Verify now packets can be sent.
+  for (size_t i = 0; i < anti_amplification_factor; ++i) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+    connection_.SendCryptoDataWithString("foo", i * 3);
+    // Verify retransmission alarm is not set if throttled by anti-amplification
+    // limit.
+    EXPECT_EQ(i != anti_amplification_factor - 1,
+              connection_.GetRetransmissionAlarm()->IsSet());
+  }
+  // Verify server is throttled by anti-amplification limit.
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  connection_.SendCryptoDataWithString("foo", anti_amplification_factor * 3);
+
+  // Receives packet 2.
+  ProcessCryptoPacketAtLevel(2, ENCRYPTION_INITIAL);
+  // Verify more packets can be sent.
+  for (size_t i = anti_amplification_factor; i < anti_amplification_factor * 2;
+       ++i) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+    connection_.SendCryptoDataWithString("foo", i * 3);
+  }
+  // Verify server is throttled by anti-amplification limit.
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  connection_.SendCryptoDataWithString("foo",
+                                       2 * anti_amplification_factor * 3);
+
+  ProcessPacket(3);
+  // Verify anti-amplification limit is gone after address validation.
+  for (size_t i = 0; i < 100; ++i) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+    connection_.SendStreamDataWithString(3, "first", i * 0, NO_FIN);
+  }
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
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(),
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index c024283..2d8381c 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -401,6 +401,9 @@
   // Called to adjust pending_timer_transmission_count_ accordingly.
   void AdjustPendingTimerTransmissions();
 
+  // Called to disable HANDSHAKE_MODE, and only PTO and LOSS modes are used.
+  void DisableHandshakeMode();
+
   bool supports_multiple_packet_number_spaces() const {
     return unacked_packets_.supports_multiple_packet_number_spaces();
   }
@@ -413,6 +416,8 @@
 
   bool pto_enabled() const { return pto_enabled_; }
 
+  bool handshake_mode_disabled() const { return handshake_mode_disabled_; }
+
  private:
   friend class test::QuicConnectionPeer;
   friend class test::QuicSentPacketManagerPeer;
@@ -658,6 +663,9 @@
   // Latched value of quic_fix_rto_retransmission3 and
   // session_decides_what_to_write.
   bool fix_rto_retransmission_;
+
+  // True if HANDSHAKE mode has been disabled.
+  bool handshake_mode_disabled_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index fced1eb..43b0094 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3090,6 +3090,50 @@
   manager_.MaybeSendProbePackets();
 }
 
+TEST_P(QuicSentPacketManagerTest, DisableHandshakeModeClient) {
+  QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+  manager_.SetSessionDecideWhatToWrite(true);
+  manager_.DisableHandshakeMode();
+  // Send CHLO.
+  SendCryptoPacket(1);
+  EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime());
+  // Ack packet 1.
+  ExpectAck(1);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+  EXPECT_EQ(PACKETS_NEWLY_ACKED,
+            manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1),
+                                   ENCRYPTION_INITIAL));
+  EXPECT_EQ(0u, manager_.GetBytesInFlight());
+  // Verify retransmission timeout is not zero because handshake is not
+  // confirmed although there is no in flight packet.
+  EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime());
+  // Fire PTO.
+  EXPECT_EQ(QuicSentPacketManager::PTO_MODE,
+            manager_.OnRetransmissionTimeout());
+}
+
+TEST_P(QuicSentPacketManagerTest, DisableHandshakeModeServer) {
+  manager_.SetSessionDecideWhatToWrite(true);
+  manager_.DisableHandshakeMode();
+  // Send SHLO.
+  SendCryptoPacket(1);
+  EXPECT_NE(QuicTime::Zero(), manager_.GetRetransmissionTime());
+  // Ack packet 1.
+  ExpectAck(1);
+  manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+  EXPECT_EQ(PACKETS_NEWLY_ACKED,
+            manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1),
+                                   ENCRYPTION_INITIAL));
+  EXPECT_EQ(0u, manager_.GetBytesInFlight());
+  // Verify retransmission timeout is not set on server side because there is
+  // nothing in flight.
+  EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index 372c4f7..af93f85 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -79,6 +79,11 @@
   return VersionHasLengthPrefixedConnectionIds(transport_version);
 }
 
+bool ParsedQuicVersion::SupportsAntiAmplificationLimit() const {
+  return transport_version == QUIC_VERSION_99 &&
+         handshake_protocol == PROTOCOL_TLS1_3;
+}
+
 bool VersionHasLengthPrefixedConnectionIds(
     QuicTransportVersion transport_version) {
   return transport_version >= QUIC_VERSION_99;
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h
index f82f300..c9d4b29 100644
--- a/quic/core/quic_versions.h
+++ b/quic/core/quic_versions.h
@@ -177,6 +177,11 @@
   // connection ID lengths as described in draft-ietf-quic-invariants-06 and
   // draft-ietf-quic-transport-22.
   bool HasLengthPrefixedConnectionIds() const;
+
+  // Returns whether this version supports IETF style anti-amplification limit,
+  // i.e., server will send no more than FLAGS_quic_anti_amplification_factor
+  // times received bytes until address can be validated.
+  bool SupportsAntiAmplificationLimit() const;
 };
 
 QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion();
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
index e3e649e..27e47b2 100644
--- a/quic/test_tools/crypto_test_utils.cc
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -692,6 +692,8 @@
     // them into |framer|, perform the decryption with them, and then swap ther
     // back.
     QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+    QuicConnectionPeer::AddBytesReceived(
+        dest_conn, source_conn->encrypted_packets_[index]->length());
     if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) {
       // The framer will be unable to decrypt forward-secure packets sent after
       // the handshake is complete. Don't treat them as handshake packets.
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc
index 66d1a51..ef2ffb3 100644
--- a/quic/test_tools/quic_connection_peer.cc
+++ b/quic/test_tools/quic_connection_peer.cc
@@ -332,5 +332,18 @@
   connection->last_header_.form = format;
 }
 
+// static
+void QuicConnectionPeer::AddBytesReceived(QuicConnection* connection,
+                                          size_t length) {
+  if (connection->EnforceAntiAmplificationLimit()) {
+    connection->bytes_received_before_address_validation_ += length;
+  }
+}
+
+// static
+void QuicConnectionPeer::SetAddressValidated(QuicConnection* connection) {
+  connection->address_validated_ = true;
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h
index d0797f5..f329a1c 100644
--- a/quic/test_tools/quic_connection_peer.h
+++ b/quic/test_tools/quic_connection_peer.h
@@ -133,6 +133,8 @@
       QuicConnection* connection);
   static void SetLastHeaderFormat(QuicConnection* connection,
                                   PacketHeaderFormat format);
+  static void AddBytesReceived(QuicConnection* connection, size_t length);
+  static void SetAddressValidated(QuicConnection* connection);
 };
 
 }  // namespace test