gfe-relnote: In QUIC, add exponential backoff for PTO timeout when there is not RTT measurement. Protected by gfe2_reloadable_flag_quic_enable_version_draft_25_v3 and gfe2_reloadable_flag_quic_enable_version_draft_27. Also change PTO timeout from 2 * initial_rtt to 3 * initial_rtt. PiperOrigin-RevId: 308102591 Change-Id: If44e9e7f6107b7a95650e5f4744ea847d2b998d2
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index c727d93..f0ee1a7 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -1169,10 +1169,11 @@ const QuicTime::Delta QuicSentPacketManager::GetProbeTimeoutDelay() const { DCHECK(pto_enabled_); if (rtt_stats_.smoothed_rtt().IsZero()) { - if (rtt_stats_.initial_rtt().IsZero()) { - return QuicTime::Delta::FromSeconds(1); - } - return 2 * rtt_stats_.initial_rtt(); + // Respect kMinHandshakeTimeoutMs to avoid a potential amplification attack. + QUIC_BUG_IF(rtt_stats_.initial_rtt().IsZero()); + return std::max(3 * rtt_stats_.initial_rtt(), + QuicTime::Delta::FromMilliseconds(kMinHandshakeTimeoutMs)) * + (1 << consecutive_pto_count_); } const QuicTime::Delta rtt_var = use_standard_deviation_for_pto_ ? rtt_stats_.GetStandardOrMeanDeviation()
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc index e846058..e63a9b8 100644 --- a/quic/core/quic_sent_packet_manager_test.cc +++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3772,6 +3772,59 @@ EXPECT_EQ(expected_delay, manager_.GetPathDegradingDelay()); } +// Regression test for b/154050235. +TEST_F(QuicSentPacketManagerTest, ExponentialBackoffWithNoRttMeasurement) { + QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); + manager_.EnableIetfPtoAndLossDetection(); + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(kInitialRttMs), + rtt_stats->initial_rtt()); + EXPECT_TRUE(rtt_stats->smoothed_rtt().IsZero()); + + SendCryptoPacket(1); + QuicTime::Delta expected_pto_delay = + QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs); + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Invoke PTO. + clock_.AdvanceTime(expected_pto_delay); + manager_.OnRetransmissionTimeout(); + + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this]() { RetransmitCryptoPacket(3); }))); + manager_.MaybeSendProbePackets(); + // Verify exponential backoff of the PTO timeout. + EXPECT_EQ(clock_.Now() + 2 * expected_pto_delay, + manager_.GetRetransmissionTime()); +} + +TEST_F(QuicSentPacketManagerTest, PtoDelayWithTinyInitialRtt) { + manager_.EnableIetfPtoAndLossDetection(); + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + // Assume client provided a tiny initial RTT. + rtt_stats->set_initial_rtt(QuicTime::Delta::FromMicroseconds(1)); + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1), rtt_stats->initial_rtt()); + EXPECT_TRUE(rtt_stats->smoothed_rtt().IsZero()); + + SendCryptoPacket(1); + QuicTime::Delta expected_pto_delay = QuicTime::Delta::FromMilliseconds(10); + // Verify kMinHandshakeTimeoutMs is respected. + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Invoke PTO. + clock_.AdvanceTime(expected_pto_delay); + manager_.OnRetransmissionTimeout(); + + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this]() { RetransmitCryptoPacket(3); }))); + manager_.MaybeSendProbePackets(); + // Verify exponential backoff of the PTO timeout. + EXPECT_EQ(clock_.Now() + 2 * expected_pto_delay, + manager_.GetRetransmissionTime()); +} + } // namespace } // namespace test } // namespace quic