Use next_connection_id_sequence_number to validate retired CID number as retiring an active CID would not guarantee that a new CID is issued due to CID collision in dispatcher.
Default enabled as a bug fix.
Protected by FLAGS_quic_reloadable_flag_quic_check_retire_cid_with_next_cid_sequence_number.
PiperOrigin-RevId: 487508836
diff --git a/quiche/quic/core/quic_connection_id_manager.cc b/quiche/quic/core/quic_connection_id_manager.cc
index a2a3fbf..4776545 100644
--- a/quiche/quic/core/quic_connection_id_manager.cc
+++ b/quiche/quic/core/quic_connection_id_manager.cc
@@ -340,9 +340,19 @@
const QuicRetireConnectionIdFrame& frame, QuicTime::Delta pto_delay,
std::string* error_detail) {
QUICHE_DCHECK(!active_connection_ids_.empty());
- if (frame.sequence_number > active_connection_ids_.back().second) {
- *error_detail = "To be retired connecton ID is never issued.";
- return IETF_QUIC_PROTOCOL_VIOLATION;
+ if (GetQuicReloadableFlag(
+ quic_check_retire_cid_with_next_cid_sequence_number)) {
+ QUIC_RELOADABLE_FLAG_COUNT(
+ quic_check_retire_cid_with_next_cid_sequence_number);
+ if (frame.sequence_number >= next_connection_id_sequence_number_) {
+ *error_detail = "To be retired connecton ID is never issued.";
+ return IETF_QUIC_PROTOCOL_VIOLATION;
+ }
+ } else {
+ if (frame.sequence_number > active_connection_ids_.back().second) {
+ *error_detail = "To be retired connecton ID is never issued.";
+ return IETF_QUIC_PROTOCOL_VIOLATION;
+ }
}
auto it =
diff --git a/quiche/quic/core/quic_connection_id_manager_test.cc b/quiche/quic/core/quic_connection_id_manager_test.cc
index 9df262c..2dd35bf 100644
--- a/quiche/quic/core/quic_connection_id_manager_test.cc
+++ b/quiche/quic/core/quic_connection_id_manager_test.cc
@@ -6,8 +6,10 @@
#include <cstddef>
+#include "quiche/quic/core/frames/quic_retire_connection_id_frame.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_error_codes.h"
+#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/mock_clock.h"
#include "quiche/quic/test_tools/mock_connection_id_generator.h"
@@ -1028,5 +1030,45 @@
cid_manager_.MaybeSendNewConnectionIds();
}
+// Regression test for b/258450534
+TEST_F(QuicSelfIssuedConnectionIdManagerTest,
+ RetireConnectionIdAfterConnectionIdCollisionIsFine) {
+ SetQuicReloadableFlag(quic_check_cid_collision_when_issue_new_cid, true);
+ QuicConnectionId cid0 = initial_connection_id_;
+ QuicConnectionId cid1 = CheckGenerate(cid0);
+ EXPECT_CALL(cid_manager_visitor_, MaybeReserveConnectionId(cid1))
+ .WillOnce(Return(true));
+ EXPECT_CALL(cid_manager_visitor_, SendNewConnectionId(_))
+ .WillOnce(Return(true));
+ cid_manager_.MaybeSendNewConnectionIds();
+
+ QuicRetireConnectionIdFrame retire_cid_frame(/*control_frame_id=*/0,
+ /*sequence_number=*/1);
+ QuicConnectionId cid2 = CheckGenerate(cid1);
+ // This happens when cid2 is aleady present in the dispatcher map.
+ EXPECT_CALL(cid_manager_visitor_, MaybeReserveConnectionId(cid2))
+ .WillOnce(Return(false));
+ std::string error_details;
+ EXPECT_EQ(
+ cid_manager_.OnRetireConnectionIdFrame(
+ retire_cid_frame, QuicTime::Delta::FromSeconds(1), &error_details),
+ QUIC_NO_ERROR)
+ << error_details;
+
+ if (GetQuicReloadableFlag(
+ quic_check_retire_cid_with_next_cid_sequence_number)) {
+ EXPECT_EQ(
+ cid_manager_.OnRetireConnectionIdFrame(
+ retire_cid_frame, QuicTime::Delta::FromSeconds(1), &error_details),
+ QUIC_NO_ERROR)
+ << error_details;
+ } else {
+ EXPECT_EQ(
+ cid_manager_.OnRetireConnectionIdFrame(
+ retire_cid_frame, QuicTime::Delta::FromSeconds(1), &error_details),
+ IETF_QUIC_PROTOCOL_VIOLATION);
+ }
+}
+
} // namespace
} // namespace quic::test
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h
index 88cd4a0..7f855cb 100644
--- a/quiche/quic/core/quic_flags_list.h
+++ b/quiche/quic/core/quic_flags_list.h
@@ -85,6 +85,8 @@
QUIC_FLAG(quic_reloadable_flag_quic_use_ping_manager2, true)
// If true, use new connection ID in connection migration.
QUIC_FLAG(quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, true)
+// If true, use next_connection_id_sequence_number to validate retired cid number.
+QUIC_FLAG(quic_reloadable_flag_quic_check_retire_cid_with_next_cid_sequence_number, true)
// If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped.
QUIC_FLAG(quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false)
// Instead of assuming an incoming connection ID length for short headers, ask each time.