gfe-relnote: In QUIC, call NeuterHandshakePackets() at most once per connection. Protected by gfe2_reloadable_flag_quic_neuter_handshake_packets_once2 which replaces gfe2_reloadable_flag_quic_neuter_handshake_packets_once.
Also fix the bug where RTO and PTO try to retransmit not in flight packets.
PiperOrigin-RevId: 279118870
Change-Id: I1235372277109c18fded6c8d162a2147424d3dbd
diff --git a/quic/core/frames/quic_stream_frame.cc b/quic/core/frames/quic_stream_frame.cc
index c7626d3..d0e65d2 100644
--- a/quic/core/frames/quic_stream_frame.cc
+++ b/quic/core/frames/quic_stream_frame.cc
@@ -49,4 +49,8 @@
offset == rhs.offset;
}
+bool QuicStreamFrame::operator!=(const QuicStreamFrame& rhs) const {
+ return !(*this == rhs);
+}
+
} // namespace quic
diff --git a/quic/core/frames/quic_stream_frame.h b/quic/core/frames/quic_stream_frame.h
index 5e7439d..5c5323b 100644
--- a/quic/core/frames/quic_stream_frame.h
+++ b/quic/core/frames/quic_stream_frame.h
@@ -33,6 +33,8 @@
bool operator==(const QuicStreamFrame& rhs) const;
+ bool operator!=(const QuicStreamFrame& rhs) const;
+
bool fin;
QuicPacketLength data_length;
QuicStreamId stream_id;
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index c8c870b..cdc5a1c 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -107,7 +107,7 @@
pto_exponential_backoff_start_point_(0),
pto_rttvar_multiplier_(4),
neuter_handshake_packets_once_(
- GetQuicReloadableFlag(quic_neuter_handshake_packets_once)) {
+ GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
SetSendAlgorithm(congestion_control_type);
}
@@ -342,7 +342,7 @@
void QuicSentPacketManager::SetHandshakeConfirmed() {
if (!neuter_handshake_packets_once_ || !handshake_confirmed_) {
if (neuter_handshake_packets_once_) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_neuter_handshake_packets_once);
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_neuter_handshake_packets_once2, 1, 3);
}
handshake_confirmed_ = true;
NeuterHandshakePackets();
@@ -454,7 +454,7 @@
void QuicSentPacketManager::NeuterUnencryptedPackets() {
QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
- for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it, ++packet_number) {
if (!it->retransmittable_frames.empty() &&
it->encryption_level == ENCRYPTION_INITIAL) {
@@ -462,18 +462,31 @@
// will be sent. The data has been abandoned in the cryto stream. Remove
// it from in flight.
unacked_packets_.RemoveFromInFlight(packet_number);
+ if (neuter_handshake_packets_once_) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_neuter_handshake_packets_once2, 2, 3);
+ it->state = NEUTERED;
+ DCHECK(!unacked_packets_.HasRetransmittableFrames(*it));
+ }
}
}
}
void QuicSentPacketManager::NeuterHandshakePackets() {
QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
- for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin();
it != unacked_packets_.end(); ++it, ++packet_number) {
if (!it->retransmittable_frames.empty() &&
unacked_packets_.GetPacketNumberSpace(it->encryption_level) ==
HANDSHAKE_DATA) {
unacked_packets_.RemoveFromInFlight(packet_number);
+ if (neuter_handshake_packets_once_) {
+ // Notify session that the data has been delivered (but do not notify
+ // send algorithm).
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_neuter_handshake_packets_once2, 3, 3);
+ it->state = NEUTERED;
+ unacked_packets_.NotifyFramesAcked(*it, QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ }
}
}
}
@@ -765,6 +778,7 @@
if (it->state == OUTSTANDING &&
unacked_packets_.HasRetransmittableFrames(*it) &&
pending_timer_transmission_count_ < max_rto_packets_) {
+ DCHECK(!neuter_handshake_packets_once_ || it->in_flight);
retransmissions.push_back(packet_number);
++pending_timer_transmission_count_;
}
@@ -797,6 +811,7 @@
it != unacked_packets_.end(); ++it, ++packet_number) {
if (it->state == OUTSTANDING &&
unacked_packets_.HasRetransmittableFrames(*it)) {
+ DCHECK(!neuter_handshake_packets_once_ || it->in_flight);
probing_packets.push_back(packet_number);
if (probing_packets.size() == pending_timer_transmission_count_) {
break;
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 95b9ee2..67ded33 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -646,7 +646,7 @@
// The multiplier of rttvar when calculating PTO timeout.
int pto_rttvar_multiplier_;
- // Latched value of quic_neuter_handshake_packets_once.
+ // Latched value of quic_neuter_handshake_packets_once2.
const bool neuter_handshake_packets_once_;
};
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 539ce97..1bd08d9 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -1125,9 +1125,9 @@
// Now neuter all unacked unencrypted packets, which occurs when the
// connection goes forward secure.
- manager_.NeuterUnencryptedPackets();
EXPECT_CALL(notifier_, HasUnackedCryptoData()).WillRepeatedly(Return(false));
EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ manager_.NeuterUnencryptedPackets();
EXPECT_FALSE(manager_.HasUnackedCryptoPackets());
uint64_t unacked[] = {1, 2, 3};
VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
@@ -3055,6 +3055,58 @@
manager_.GetRetransmissionTime());
}
+// Regression test for b/143962153
+TEST_F(QuicSentPacketManagerTest, RtoNotInFlightPacket) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ // Send SHLO.
+ QuicStreamFrame crypto_frame(1, false, 0, QuicStringPiece());
+ SendCryptoPacket(1);
+ // Send data packet.
+ SendDataPacket(2, ENCRYPTION_FORWARD_SECURE);
+
+ // Successfully decrypt a forward secure packet.
+ if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _, _)).Times(1);
+ } else {
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _, _)).Times(0);
+ }
+ manager_.SetHandshakeConfirmed();
+
+ // 1st TLP.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE);
+ })));
+ manager_.MaybeRetransmitTailLossProbe();
+
+ // 2nd TLP.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(4, type, ENCRYPTION_FORWARD_SECURE);
+ })));
+ manager_.MaybeRetransmitTailLossProbe();
+
+ // RTO retransmits SHLO although it is not in flight.
+ size_t num_rto_packets = 2;
+ if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+ num_rto_packets = 1;
+ }
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(num_rto_packets)
+ .WillOnce(
+ WithArgs<0>(Invoke([this, &crypto_frame](const QuicFrames& frames) {
+ EXPECT_EQ(1u, frames.size());
+ if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+ EXPECT_NE(crypto_frame, frames[0].stream_frame);
+ } else {
+ EXPECT_EQ(crypto_frame, frames[0].stream_frame);
+ }
+ })));
+ manager_.OnRetransmissionTimeout();
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 972e57c..06931dc 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -446,7 +446,7 @@
};
enum SentPacketState : uint8_t {
- // The packet has been sent and waiting to be acked.
+ // The packet is in flight and waiting to be acked.
OUTSTANDING,
FIRST_PACKET_STATE = OUTSTANDING,
// The packet was never sent.
@@ -455,6 +455,8 @@
ACKED,
// This packet is not expected to be acked.
UNACKABLE,
+ // This packet has been delivered or unneeded.
+ NEUTERED,
// States below are corresponding to retransmission types in TransmissionType.
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index a6f6577..eed0e37 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -161,6 +161,7 @@
RETURN_STRING_LITERAL(NEVER_SENT);
RETURN_STRING_LITERAL(ACKED);
RETURN_STRING_LITERAL(UNACKABLE);
+ RETURN_STRING_LITERAL(NEUTERED);
RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMITTED);
RETURN_STRING_LITERAL(LOST);
RETURN_STRING_LITERAL(TLP_RETRANSMITTED);
diff --git a/quic/test_tools/simple_session_notifier.cc b/quic/test_tools/simple_session_notifier.cc
index 5ba12c6..9e67c89 100644
--- a/quic/test_tools/simple_session_notifier.cc
+++ b/quic/test_tools/simple_session_notifier.cc
@@ -126,8 +126,13 @@
}
void SimpleSessionNotifier::NeuterUnencryptedData() {
- // TODO(nharper): Handle CRYPTO frame case.
if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) {
+ QuicCryptoFrame crypto_frame(ENCRYPTION_INITIAL, interval.min(),
+ interval.max() - interval.min());
+ OnFrameAcked(QuicFrame(&crypto_frame), QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ }
return;
}
for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) {