Allow long connection IDs for unknown QUIC versions
In particular, if we're a server and a client sends us a packet with an unknown version and a connection ID of any length between 8 and 255, we must respond with a version negotiation packet that includes that same connection ID.
gfe-relnote: allow longer connection IDs, protected by new disabled flag gfe2_restart_flag_quic_allow_very_long_connection_ids
PiperOrigin-RevId: 286015388
Change-Id: I53ff4e602a19f30f56aba10ad7d877776597a5cf
diff --git a/quic/core/quic_connection_id.cc b/quic/core/quic_connection_id.cc
index c495b35..c514450 100644
--- a/quic/core/quic_connection_id.cc
+++ b/quic/core/quic_connection_id.cc
@@ -53,13 +53,19 @@
QuicConnectionId::QuicConnectionId() : QuicConnectionId(nullptr, 0) {}
QuicConnectionId::QuicConnectionId(const char* data, uint8_t length) {
- static_assert(kQuicMaxConnectionIdAllVersionsLength <=
- std::numeric_limits<uint8_t>::max(),
- "kQuicMaxConnectionIdAllVersionsLength too high");
- if (length > kQuicMaxConnectionIdAllVersionsLength) {
- QUIC_BUG << "Attempted to create connection ID of length "
- << static_cast<int>(length);
- length = kQuicMaxConnectionIdAllVersionsLength;
+ if (!GetQuicRestartFlag(quic_allow_very_long_connection_ids)) {
+ // TODO(dschinazi) remove kQuicMaxConnectionIdAllVersionsLength entirely
+ // when we deprecate quic_allow_very_long_connection_ids.
+ static_assert(kQuicMaxConnectionIdAllVersionsLength <=
+ std::numeric_limits<uint8_t>::max(),
+ "kQuicMaxConnectionIdAllVersionsLength too high");
+ if (length > kQuicMaxConnectionIdAllVersionsLength) {
+ QUIC_BUG << "Attempted to create connection ID of length "
+ << static_cast<int>(length);
+ length = kQuicMaxConnectionIdAllVersionsLength;
+ }
+ } else {
+ QUIC_RESTART_FLAG_COUNT_N(quic_allow_very_long_connection_ids, 3, 5);
}
length_ = length;
if (length_ == 0) {
@@ -109,10 +115,14 @@
}
void QuicConnectionId::set_length(uint8_t length) {
- if (length > kQuicMaxConnectionIdAllVersionsLength) {
- QUIC_BUG << "Attempted to set connection ID length to "
- << static_cast<int>(length);
- length = kQuicMaxConnectionIdAllVersionsLength;
+ if (!GetQuicRestartFlag(quic_allow_very_long_connection_ids)) {
+ if (length > kQuicMaxConnectionIdAllVersionsLength) {
+ QUIC_BUG << "Attempted to set connection ID length to "
+ << static_cast<int>(length);
+ length = kQuicMaxConnectionIdAllVersionsLength;
+ }
+ } else {
+ QUIC_RESTART_FLAG_COUNT_N(quic_allow_very_long_connection_ids, 4, 5);
}
char temporary_data[sizeof(data_short_)];
if (length > sizeof(data_short_)) {
diff --git a/quic/core/quic_connection_id_test.cc b/quic/core/quic_connection_id_test.cc
index 1703913..dbc8214 100644
--- a/quic/core/quic_connection_id_test.cc
+++ b/quic/core/quic_connection_id_test.cc
@@ -92,7 +92,8 @@
// Verify that any two all-zero connection IDs of different lengths never
// have the same hash.
- const char connection_id_bytes[kQuicMaxConnectionIdAllVersionsLength] = {};
+ SetQuicRestartFlag(quic_allow_very_long_connection_ids, true);
+ const char connection_id_bytes[255] = {};
for (uint8_t i = 0; i < sizeof(connection_id_bytes) - 1; ++i) {
QuicConnectionId connection_id_i(connection_id_bytes, i);
for (uint8_t j = i + 1; j < sizeof(connection_id_bytes); ++j) {
diff --git a/quic/core/quic_data_reader.cc b/quic/core/quic_data_reader.cc
index 306a8a9..3982cc9 100644
--- a/quic/core/quic_data_reader.cc
+++ b/quic/core/quic_data_reader.cc
@@ -139,10 +139,14 @@
bool QuicDataReader::ReadConnectionId(QuicConnectionId* connection_id,
uint8_t length) {
- if (length > kQuicMaxConnectionIdAllVersionsLength) {
- QUIC_BUG << "Attempted to read connection ID with length too high "
- << static_cast<int>(length);
- return false;
+ if (!GetQuicRestartFlag(quic_allow_very_long_connection_ids)) {
+ if (length > kQuicMaxConnectionIdAllVersionsLength) {
+ QUIC_BUG << "Attempted to read connection ID with length too high "
+ << static_cast<int>(length);
+ return false;
+ }
+ } else {
+ QUIC_RESTART_FLAG_COUNT_N(quic_allow_very_long_connection_ids, 1, 5);
}
if (length == 0) {
connection_id->set_length(0);
@@ -166,8 +170,12 @@
if (!ReadUInt8(&connection_id_length)) {
return false;
}
- if (connection_id_length > kQuicMaxConnectionIdAllVersionsLength) {
- return false;
+ if (!GetQuicRestartFlag(quic_allow_very_long_connection_ids)) {
+ if (connection_id_length > kQuicMaxConnectionIdAllVersionsLength) {
+ return false;
+ }
+ } else {
+ QUIC_RESTART_FLAG_COUNT_N(quic_allow_very_long_connection_ids, 2, 5);
}
return ReadConnectionId(connection_id, connection_id_length);
}
diff --git a/quic/core/quic_data_writer_test.cc b/quic/core/quic_data_writer_test.cc
index 7cfde88..3c38673 100644
--- a/quic/core/quic_data_writer_test.cc
+++ b/quic/core/quic_data_writer_test.cc
@@ -1166,6 +1166,9 @@
}
TEST_P(QuicDataWriterTest, InvalidConnectionIdLengthRead) {
+ SetQuicRestartFlag(quic_allow_very_long_connection_ids, false);
+ // TODO(dschinazi) delete this test when we deprecate
+ // quic_allow_very_long_connection_ids.
static const uint8_t bad_connection_id_length = 200;
static_assert(
bad_connection_id_length > kQuicMaxConnectionIdAllVersionsLength,
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index 1ea8036..4f797c2 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -547,6 +547,26 @@
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
}
+TEST_F(QuicDispatcherTest,
+ StatelessVersionNegotiationWithVeryLongConnectionId) {
+ SetQuicRestartFlag(quic_allow_very_long_connection_ids, true);
+ QuicConnectionId connection_id = QuicUtils::CreateRandomConnectionId(33);
+ CreateTimeWaitListManager();
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ SendVersionNegotiationPacket(connection_id, _, _, _, _, _, _, _))
+ .Times(1);
+ // Pad the CHLO message with enough data to make the packet large enough
+ // to trigger version negotiation.
+ std::string chlo = SerializeCHLO() + std::string(1200, 'a');
+ DCHECK_LE(1200u, chlo.length());
+ ProcessPacket(client_address, connection_id, true,
+ QuicVersionReservedForNegotiation(), chlo, true,
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
TEST_F(QuicDispatcherTest, StatelessVersionNegotiationWithClientConnectionId) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index e9c682a..c3f5463 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -10472,8 +10472,13 @@
AssemblePacketFromFragments(packet99));
EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
EXPECT_THAT(framer_.error(), IsError(QUIC_INVALID_NEW_CONNECTION_ID_DATA));
- EXPECT_EQ("Unable to read new connection ID frame connection id.",
- framer_.detailed_error());
+ if (!GetQuicRestartFlag(quic_allow_very_long_connection_ids)) {
+ EXPECT_EQ("Unable to read new connection ID frame connection id.",
+ framer_.detailed_error());
+ } else {
+ EXPECT_EQ("Invalid new connection ID length for version.",
+ framer_.detailed_error());
+ }
}
// Verifies that parsing a NEW_CONNECTION_ID frame with an invalid
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index a695c94..e37d019 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -545,6 +545,16 @@
static_cast<size_t>(std::numeric_limits<uint8_t>::max())) {
return false;
}
+
+ if (GetQuicRestartFlag(quic_allow_very_long_connection_ids)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_allow_very_long_connection_ids, 5, 5);
+ if (transport_version == QUIC_VERSION_UNSUPPORTED ||
+ transport_version == QUIC_VERSION_RESERVED_FOR_NEGOTIATION) {
+ // Unknown versions could allow connection ID lengths up to 255.
+ return true;
+ }
+ }
+
const uint8_t connection_id_length8 =
static_cast<uint8_t>(connection_id_length);
// Versions that do not support variable lengths only support length 8.
diff --git a/quic/core/quic_utils_test.cc b/quic/core/quic_utils_test.cc
index fa471f0..baaa3ae 100644
--- a/quic/core/quic_utils_test.cc
+++ b/quic/core/quic_utils_test.cc
@@ -184,7 +184,8 @@
TEST_F(QuicUtilsTest, ReplacementConnectionIdLengthIsCorrect) {
// Verify that all lengths get replaced by kQuicDefaultConnectionIdLength.
- const char connection_id_bytes[kQuicMaxConnectionIdAllVersionsLength] = {};
+ SetQuicRestartFlag(quic_allow_very_long_connection_ids, true);
+ const char connection_id_bytes[255] = {};
for (uint8_t i = 0; i < sizeof(connection_id_bytes) - 1; ++i) {
QuicConnectionId connection_id(connection_id_bytes, i);
QuicConnectionId replacement_connection_id =