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