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