In quic with tls, remove the head of line blocking caused by an unprocessable packet. protected by gfe2_reloadable_flag_quic_fix_undecryptable_packets.
PiperOrigin-RevId: 317090147
Change-Id: Ib1376131fa7f69bad3c9b6a16fc1fad9341a52ee
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 9901afa..b30165d 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -1989,24 +1989,8 @@
++stats_.undecryptable_packets_received_before_handshake_complete;
}
- bool should_enqueue = true;
- if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) {
- // We do not expect to install any further keys.
- should_enqueue = false;
- } else if (undecryptable_packets_.size() >= max_undecryptable_packets_) {
- // We do not queue more than max_undecryptable_packets_ packets.
- should_enqueue = false;
- } else if (has_decryption_key) {
- // We already have the key for this decryption level, therefore no
- // future keys will allow it be decrypted.
- should_enqueue = false;
- } else if (version().KnowsWhichDecrypterToUse() &&
- decryption_level <= encryption_level_) {
- // On versions that know which decrypter to use, we install keys in order
- // so we will not get newer keys for lower encryption levels.
- should_enqueue = false;
- }
-
+ const bool should_enqueue =
+ ShouldEnqueueUnDecryptablePacket(decryption_level, has_decryption_key);
if (should_enqueue) {
QueueUndecryptablePacket(packet, decryption_level);
}
@@ -2017,6 +2001,31 @@
}
}
+bool QuicConnection::ShouldEnqueueUnDecryptablePacket(
+ EncryptionLevel decryption_level,
+ bool has_decryption_key) const {
+ if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) {
+ // We do not expect to install any further keys.
+ return false;
+ }
+ if (undecryptable_packets_.size() >= max_undecryptable_packets_) {
+ // We do not queue more than max_undecryptable_packets_ packets.
+ return false;
+ }
+ if (has_decryption_key) {
+ // We already have the key for this decryption level, therefore no
+ // future keys will allow it be decrypted.
+ return false;
+ }
+ if (version().KnowsWhichDecrypterToUse() &&
+ decryption_level <= encryption_level_) {
+ // On versions that know which decrypter to use, we install keys in order
+ // so we will not get newer keys for lower encryption levels.
+ return false;
+ }
+ return true;
+}
+
void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
@@ -3142,27 +3151,79 @@
return;
}
- while (connected_ && !undecryptable_packets_.empty()) {
- // Making sure there is no pending frames when processing next undecrypted
- // packet because the queued ack frame may change.
- packet_creator_.FlushCurrentPacket();
- if (!connected_) {
- return;
+ if (GetQuicReloadableFlag(quic_fix_undecryptable_packets)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_undecryptable_packets);
+ auto iter = undecryptable_packets_.begin();
+ while (connected_ && iter != undecryptable_packets_.end()) {
+ // Making sure there is no pending frames when processing next undecrypted
+ // packet because the queued ack frame may change.
+ packet_creator_.FlushCurrentPacket();
+ if (!connected_) {
+ return;
+ }
+ UndecryptablePacket* undecryptable_packet = &*iter;
+ ++iter;
+ if (undecryptable_packet->processed) {
+ continue;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet";
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnAttemptingToProcessUndecryptablePacket(
+ undecryptable_packet->encryption_level);
+ }
+ if (framer_.ProcessPacket(*undecryptable_packet->packet)) {
+ QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
+ undecryptable_packet->processed = true;
+ ++stats_.packets_processed;
+ continue;
+ }
+ const bool has_decryption_key =
+ version().KnowsWhichDecrypterToUse() &&
+ framer_.HasDecrypterOfEncryptionLevel(
+ undecryptable_packet->encryption_level);
+ if (framer_.error() == QUIC_DECRYPTION_FAILURE &&
+ ShouldEnqueueUnDecryptablePacket(
+ undecryptable_packet->encryption_level, has_decryption_key)) {
+ QUIC_DVLOG(1)
+ << ENDPOINT
+ << "Need to attempt to process this undecryptable packet later";
+ continue;
+ }
+ undecryptable_packet->processed = true;
}
- QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet";
- const auto& undecryptable_packet = undecryptable_packets_.front();
- if (debug_visitor_ != nullptr) {
- debug_visitor_->OnAttemptingToProcessUndecryptablePacket(
- undecryptable_packet.encryption_level);
+ // Remove processed packets. We cannot remove elements in the while loop
+ // above because currently QuicCircularDeque does not support removing
+ // mid elements.
+ while (!undecryptable_packets_.empty()) {
+ if (!undecryptable_packets_.front().processed) {
+ break;
+ }
+ undecryptable_packets_.pop_front();
}
- if (!framer_.ProcessPacket(*undecryptable_packet.packet) &&
- framer_.error() == QUIC_DECRYPTION_FAILURE) {
- QUIC_DVLOG(1) << ENDPOINT << "Unable to process undecryptable packet...";
- break;
+ } else {
+ while (connected_ && !undecryptable_packets_.empty()) {
+ // Making sure there is no pending frames when processing next undecrypted
+ // packet because the queued ack frame may change.
+ packet_creator_.FlushCurrentPacket();
+ if (!connected_) {
+ return;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet";
+ const auto& undecryptable_packet = undecryptable_packets_.front();
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnAttemptingToProcessUndecryptablePacket(
+ undecryptable_packet.encryption_level);
+ }
+ if (!framer_.ProcessPacket(*undecryptable_packet.packet) &&
+ framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Unable to process undecryptable packet...";
+ break;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
+ ++stats_.packets_processed;
+ undecryptable_packets_.pop_front();
}
- QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
- ++stats_.packets_processed;
- undecryptable_packets_.pop_front();
}
// Once forward secure encryption is in use, there will be no
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 118e9c4..8f4b113 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1095,12 +1095,16 @@
struct QUIC_EXPORT_PRIVATE UndecryptablePacket {
UndecryptablePacket(const QuicEncryptedPacket& packet,
EncryptionLevel encryption_level)
- : packet(packet.Clone()), encryption_level(encryption_level) {}
+ : packet(packet.Clone()),
+ encryption_level(encryption_level),
+ processed(false) {}
std::unique_ptr<QuicEncryptedPacket> packet;
- // Currently, |encryption_level| is only used for logging and does not
- // affect processing of the packet.
EncryptionLevel encryption_level;
+ // This gets set to true if 1) connection sucessfully processed the packet
+ // or 2) connection failed to process the packet and will not try to process
+ // it later.
+ bool processed;
};
// Notifies the visitor of the close and marks the connection as disconnected.
@@ -1314,6 +1318,11 @@
bool ValidateConfigConnectionIds(const QuicConfig& config);
bool ValidateConfigConnectionIdsOld(const QuicConfig& config);
+ // Returns true if an undecryptable packet of |decryption_level| should be
+ // buffered (such that connection can try to decrypt it later).
+ bool ShouldEnqueueUnDecryptablePacket(EncryptionLevel decryption_level,
+ bool has_decryption_key) const;
+
QuicFramer framer_;
// Contents received in the current packet, especially used to identify
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index db402c7..b1409bf 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -11196,6 +11196,67 @@
}
}
+TEST_P(QuicConnectionTest, ProcessUndecryptablePacketsBasedOnEncryptionLevel) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // SetFromConfig is always called after construction from InitializeSession.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(AnyNumber());
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ connection_.RemoveDecrypter(ENCRYPTION_FORWARD_SECURE);
+ use_tagging_decrypter();
+
+ peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x01));
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(0x02));
+
+ for (uint64_t i = 1; i <= 3; ++i) {
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_HANDSHAKE);
+ }
+ ProcessDataPacketAtLevel(4, !kHasStopWaiting, ENCRYPTION_FORWARD_SECURE);
+ for (uint64_t j = 5; j <= 7; ++j) {
+ ProcessDataPacketAtLevel(j, !kHasStopWaiting, ENCRYPTION_HANDSHAKE);
+ }
+ EXPECT_EQ(7u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
+ EXPECT_FALSE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
+ SetDecrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<StrictTaggingDecrypter>(0x01));
+ EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x01));
+ if (GetQuicReloadableFlag(quic_fix_undecryptable_packets)) {
+ // Verify all ENCRYPTION_HANDSHAKE packets get processed.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(6);
+ } else {
+ // Verify packets before 4 get processed.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3);
+ }
+ connection_.GetProcessUndecryptablePacketsAlarm()->Fire();
+ EXPECT_EQ(4u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
+
+ SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<StrictTaggingDecrypter>(0x02));
+ EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::make_unique<TaggingEncrypter>(0x02));
+ if (GetQuicReloadableFlag(quic_fix_undecryptable_packets)) {
+ // Verify the 1-RTT packet gets processed.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ } else {
+ // Verify all packets get processed.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(4);
+ }
+ connection_.GetProcessUndecryptablePacketsAlarm()->Fire();
+ EXPECT_EQ(0u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 726f1e5..847a166 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -2007,6 +2007,10 @@
return encrypter_[level] != nullptr;
}
+bool QuicFramer::HasDecrypterOfEncryptionLevel(EncryptionLevel level) const {
+ return decrypter_[level] != nullptr;
+}
+
bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
QuicDataWriter* writer,
size_t* length_field_offset) {
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 8ace715..0aa5bbf 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -571,6 +571,8 @@
// Returns true if encrypter of |level| is available.
bool HasEncrypterOfEncryptionLevel(EncryptionLevel level) const;
+ // Returns true if decrypter of |level| is available.
+ bool HasDecrypterOfEncryptionLevel(EncryptionLevel level) const;
void set_validate_flags(bool value) { validate_flags_ = value; }
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc
index d276f63..54616b2 100644
--- a/quic/test_tools/quic_connection_peer.cc
+++ b/quic/test_tools/quic_connection_peer.cc
@@ -377,5 +377,10 @@
connection->InstallInitialCrypters(server_connection_id);
}
+// static
+size_t QuicConnectionPeer::NumUndecryptablePackets(QuicConnection* connection) {
+ return connection->undecryptable_packets_.size();
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h
index 9882f62..a5b94ef 100644
--- a/quic/test_tools/quic_connection_peer.h
+++ b/quic/test_tools/quic_connection_peer.h
@@ -147,6 +147,8 @@
static void SetServerConnectionId(
QuicConnection* connection,
const QuicConnectionId& server_connection_id);
+
+ static size_t NumUndecryptablePackets(QuicConnection* connection);
};
} // namespace test