Allow variable length connection IDs in v99
This CL enables the use of QUIC variable length connection IDs (VLCIDs) in v99. It also relaxes some checks when VLCIDs are enabled, and fixes tests that were relying on this. This CL also fixes QuicFramerTest.MissingDiversificationNonce as this work uncovered that the test was failure to parse headers because the headers were wrong, instead of testing for missing diversification nonces.
gfe-relnote: change only impacts v99 and tests, not flag protected
PiperOrigin-RevId: 238522262
Change-Id: I1a33470f1c9f8e5e83da44e82bdb2969fa9ed8ea
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index d7102de..dd40098 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -284,7 +284,8 @@
chlo_multiplier_(0),
stream_factory_(nullptr),
support_server_push_(false),
- override_connection_id_(nullptr) {
+ override_connection_id_(nullptr),
+ expected_connection_id_length_(kQuicDefaultConnectionIdLength) {
FLAGS_quic_supports_tls_handshake = true;
SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, true);
SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, true);
@@ -450,13 +451,10 @@
SetQuicReloadableFlag(quic_use_cheap_stateless_rejects,
GetParam().use_cheap_stateless_reject);
- uint8_t connection_id_length = override_connection_id_ != nullptr
- ? override_connection_id_->length()
- : kQuicDefaultConnectionIdLength;
- auto* test_server =
- new QuicTestServer(crypto_test_utils::ProofSourceForTesting(),
- server_config_, server_supported_versions_,
- &memory_cache_backend_, connection_id_length);
+ auto* test_server = new QuicTestServer(
+ crypto_test_utils::ProofSourceForTesting(), server_config_,
+ server_supported_versions_, &memory_cache_backend_,
+ expected_connection_id_length_);
server_thread_ = QuicMakeUnique<ServerThread>(test_server, server_address_);
if (chlo_multiplier_ != 0) {
server_thread_->server()->SetChloMultiplier(chlo_multiplier_);
@@ -640,6 +638,7 @@
std::string pre_shared_key_client_;
std::string pre_shared_key_server_;
QuicConnectionId* override_connection_id_;
+ uint8_t expected_connection_id_length_;
};
// Run all end to end tests with all supported versions.
@@ -717,6 +716,7 @@
QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
GetParam().negotiated_version.transport_version);
override_connection_id_ = &connection_id;
+ expected_connection_id_length_ = connection_id.length();
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
@@ -735,6 +735,47 @@
GetParam().negotiated_version.transport_version));
}
+TEST_P(EndToEndTest, BadConnectionIdLength) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ GetParam().negotiated_version.transport_version)) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ QuicConnectionId connection_id = TestConnectionIdNineBytesLong(1);
+ override_connection_id_ = &connection_id;
+ ASSERT_FALSE(Initialize());
+ EXPECT_EQ(QUIC_HANDSHAKE_FAILED, client_->connection_error());
+}
+
+TEST_P(EndToEndTest, MixGoodAndBadConnectionIdLengths) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ GetParam().negotiated_version.transport_version)) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+
+ // Start client_ which will fail due to bad connection ID length.
+ QuicConnectionId connection_id = TestConnectionIdNineBytesLong(1);
+ override_connection_id_ = &connection_id;
+ ASSERT_FALSE(Initialize());
+ EXPECT_EQ(QUIC_HANDSHAKE_FAILED, client_->connection_error());
+ override_connection_id_ = nullptr;
+
+ // Start client2 which will succeed.
+ std::unique_ptr<QuicTestClient> client2(CreateQuicClient(nullptr));
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] = "3";
+ client2->SendMessage(headers, "", /*fin=*/false);
+ client2->SendData("eep", true);
+ client2->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client2->response_body());
+ EXPECT_EQ("200", client2->response_headers()->find(":status")->second);
+}
+
TEST_P(EndToEndTest, SimpleRequestResponseWithLargeReject) {
chlo_multiplier_ = 1;
ASSERT_TRUE(Initialize());
@@ -794,6 +835,7 @@
QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
GetParam().negotiated_version.transport_version);
override_connection_id_ = &connection_id;
+ expected_connection_id_length_ = connection_id.length();
ASSERT_TRUE(Initialize());
EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index 7cf5a1b..dd7c2be 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -346,6 +346,24 @@
return false;
}
+ // We currently do not support having the server change its connection ID
+ // length during the handshake. Until then, fast-fail connections.
+ // TODO(dschinazi): actually support changing connection IDs from the server.
+ if (header.destination_connection_id.length() !=
+ framer_.GetExpectedConnectionIdLength()) {
+ DCHECK(QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ header.version.transport_version));
+ QUIC_DLOG(INFO)
+ << "Packet with unexpected connection ID lengths: destination "
+ << header.destination_connection_id << " source "
+ << header.source_connection_id << " expected "
+ << static_cast<int>(framer_.GetExpectedConnectionIdLength());
+ ProcessUnauthenticatedHeaderFate(kFateTimeWait,
+ header.destination_connection_id,
+ header.form, header.version);
+ return false;
+ }
+
// Packets with connection IDs for active connections are processed
// immediately.
QuicConnectionId connection_id = header.destination_connection_id;
@@ -460,7 +478,7 @@
// Add this connection_id to the time-wait state, to safely reject
// future packets.
QUIC_DLOG(INFO) << "Adding connection ID " << connection_id
- << "to time-wait list.";
+ << " to time-wait list.";
QUIC_CODE_COUNT(quic_reject_fate_time_wait);
StatelesslyTerminateConnection(
connection_id, form, version, QUIC_HANDSHAKE_FAILED,
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index d90e804..9e86875 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -636,6 +636,80 @@
ProcessPacket(client_address, connection_id, false, SerializeCHLO());
}
+// Makes sure nine-byte connection IDs end up in the time wait list.
+TEST_F(QuicDispatcherTest, BadConnectionIdLengthPacketToTimeWaitListManager) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ CurrentSupportedVersions()[0].transport_version)) {
+ return;
+ }
+ CreateTimeWaitListManager();
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ // Dispatcher forwards all packets for this connection_id to the time wait
+ // list manager.
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, TestConnectionIdNineBytesLong(2), _, _))
+ .Times(1);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(1);
+ ProcessPacket(client_address, TestConnectionIdNineBytesLong(2), true,
+ SerializeCHLO());
+}
+
+// Makes sure TestConnectionId(1) creates a new connection but
+// TestConnectionIdNineBytesLong(2) ends up in the time wait list.
+TEST_F(QuicDispatcherTest, MixGoodAndBadConnectionIdLengthPackets) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ CurrentSupportedVersions()[0].transport_version)) {
+ return;
+ }
+ CreateTimeWaitListManager();
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _));
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(TestConnectionIdNineBytesLong(2),
+ _, QuicStringPiece("hq"), _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, TestConnectionIdNineBytesLong(2), _, _))
+ .Times(1);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(1);
+
+ ProcessPacket(client_address, TestConnectionIdNineBytesLong(2), true,
+ SerializeCHLO());
+
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(1)
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ ProcessPacket(client_address, TestConnectionId(1), false, "data");
+}
+
TEST_F(QuicDispatcherTest, ProcessPacketWithZeroPort) {
CreateTimeWaitListManager();
@@ -1236,7 +1310,9 @@
QuicConnectionId connection_id = TestConnectionId(1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _))
.Times(0);
- if (CurrentSupportedVersions()[0].transport_version > QUIC_VERSION_43) {
+ if (CurrentSupportedVersions()[0].transport_version > QUIC_VERSION_43 &&
+ !QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ CurrentSupportedVersions()[0].transport_version)) {
// This IETF packet has invalid connection ID length.
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _))
.Times(0);
@@ -1244,8 +1320,10 @@
AddConnectionIdToTimeWait(_, _, _, _, _))
.Times(0);
} else {
- // This GQUIC packet is considered as IETF QUIC packet with short header
- // with unacceptable packet number.
+ // This is either:
+ // - a GQUIC packet considered as IETF QUIC packet with short header
+ // with unacceptable packet number or
+ // - an IETF QUIC packet with bad connection ID length which is rejected.
EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _))
.Times(1);
EXPECT_CALL(*time_wait_list_manager_,
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 72df572..1af1f68 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -2277,8 +2277,6 @@
if (packet_has_ietf_packet_header) {
return ProcessIetfPacketHeader(reader, header);
}
- DCHECK(!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
- transport_version()));
uint8_t public_flags;
if (!reader->ReadBytes(&public_flags, 1)) {
set_detailed_error("Unable to read public flags.");
@@ -2579,7 +2577,11 @@
}
if ((dcil != destination_connection_id_length ||
scil != source_connection_id_length) &&
- !should_update_expected_connection_id_length_) {
+ !should_update_expected_connection_id_length_ &&
+ !QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ header->version.transport_version)) {
+ // TODO(dschinazi): use the framer's version once the
+ // OnProtocolVersionMismatch call is moved to before this is run.
QUIC_DVLOG(1) << "dcil: " << static_cast<uint32_t>(dcil)
<< ", scil: " << static_cast<uint32_t>(scil);
set_detailed_error("Invalid ConnectionId length.");
@@ -5607,7 +5609,9 @@
return false;
}
- if (connection_id_length != kQuicDefaultConnectionIdLength) {
+ if (connection_id_length != kQuicDefaultConnectionIdLength &&
+ !QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ transport_version())) {
set_detailed_error("Invalid new connection ID length.");
return false;
}
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index f0a90c3..29be82e 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -572,6 +572,11 @@
should_update_expected_connection_id_length;
}
+ // The connection ID length the framer expects on incoming IETF short headers.
+ uint8_t GetExpectedConnectionIdLength() {
+ return expected_connection_id_length_;
+ }
+
private:
friend class test::QuicFramerPeer;
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index c699fd1..6365581 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -53,6 +53,12 @@
return TestConnectionId(UINT64_C(0xFEDCBA9876543211));
}
+QuicConnectionId FramerTestConnectionIdNineBytes() {
+ char connection_id_bytes[9] = {0xFE, 0xDC, 0xBA, 0x98, 0x76,
+ 0x54, 0x32, 0x10, 0x42};
+ return QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes));
+}
+
const QuicPacketNumber kPacketNumber = QuicPacketNumber(UINT64_C(0x12345678));
const QuicPacketNumber kSmallLargestObserved =
QuicPacketNumber(UINT16_C(0x1234));
@@ -1859,6 +1865,10 @@
}
TEST_P(QuicFramerTest, MissingDiversificationNonce) {
+ if (framer_.version().handshake_protocol != PROTOCOL_QUIC_CRYPTO) {
+ // TLS does not use diversification nonces.
+ return;
+ }
QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
framer_.SetDecrypter(ENCRYPTION_NONE,
QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
@@ -1874,63 +1884,76 @@
0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
// packet number
0x12, 0x34, 0x56, 0x78,
-
- // frame type (stream frame with fin)
- 0xFF,
- // stream id
- 0x01, 0x02, 0x03, 0x04,
- // offset
- 0x3A, 0x98, 0xFE, 0xDC,
- 0x32, 0x10, 0x76, 0x54,
- // data length
- 0x00, 0x0c,
- // data
- 'h', 'e', 'l', 'l',
- 'o', ' ', 'w', 'o',
- 'r', 'l', 'd', '!',
+ // padding frame
+ 0x00,
};
unsigned char packet44[] = {
- // type (long header with packet type ZERO_RTT_PROTECTED)
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
0xFC,
// version tag
QUIC_VERSION_BYTES,
// connection_id length
- 0x50,
+ 0x05,
// connection_id
0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
// packet number
0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
- // frame type - STREAM with LEN, FIN, and OFFSET bits set.
- 0x10 | 0x01 | 0x02 | 0x04,
- // stream id
- 0x01, 0x02, 0x03, 0x04,
- // offset
- 0x3A, 0x98, 0xFE, 0xDC,
- 0x32, 0x10, 0x76, 0x54,
- // data length
- 0x00, 0x0c,
- // data
- 'h', 'e', 'l', 'l',
- 'o', ' ', 'w', 'o',
- 'r', 'l', 'd', '!',
+ unsigned char packet46[] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xD3,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+
+ unsigned char packet99[] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xD3,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+ // IETF long header payload length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
};
// clang-format on
unsigned char* p = packet;
- if (framer_.transport_version() > QUIC_VERSION_43) {
+ size_t p_length = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_length = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() >= QUIC_VERSION_46) {
+ p = packet46;
+ p_length = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() >= QUIC_VERSION_44) {
p = packet44;
+ p_length = QUIC_ARRAYSIZE(packet44);
}
- QuicEncryptedPacket encrypted(AsChars(p),
- framer_.transport_version() > QUIC_VERSION_43
- ? QUIC_ARRAYSIZE(packet44)
- : QUIC_ARRAYSIZE(packet),
- false);
+ QuicEncryptedPacket encrypted(AsChars(p), p_length, false);
EXPECT_FALSE(framer_.ProcessPacket(encrypted));
- if (framer_.transport_version() > QUIC_VERSION_43) {
+ if (framer_.transport_version() >= QUIC_VERSION_44) {
// Cannot read diversification nonce.
EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+ EXPECT_EQ("Unable to read nonce.", framer_.detailed_error());
} else {
EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error());
}
@@ -11111,6 +11134,61 @@
CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA);
}
+TEST_P(QuicFramerTest, NewConnectionIdFrameVariableLength) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ framer_.transport_version())) {
+ return;
+ }
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_NEW_CONNECTION_ID frame)
+ {"",
+ {0x18}},
+ // error code
+ {"Unable to read new connection ID frame sequence number.",
+ {kVarInt62OneByte + 0x11}},
+ {"Unable to read new connection ID frame connection id length.",
+ {0x09}}, // connection ID length
+ {"Unable to read new connection ID frame connection id.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}},
+ {"Can not read new connection ID frame reset token.",
+ {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+ EXPECT_EQ(FramerTestConnectionIdNineBytes(),
+ visitor_.new_connection_id_.connection_id);
+ EXPECT_EQ(0x11u, visitor_.new_connection_id_.sequence_number);
+ EXPECT_EQ(kTestStatelessResetToken,
+ visitor_.new_connection_id_.stateless_reset_token);
+
+ ASSERT_EQ(0u, visitor_.ack_frames_.size());
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA);
+}
+
TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) {
if (framer_.transport_version() != QUIC_VERSION_99) {
// This frame is only for version 99.
@@ -12929,10 +13007,6 @@
if (framer_.transport_version() < QUIC_VERSION_46) {
return;
}
- char connection_id_bytes[9] = {0xFE, 0xDC, 0xBA, 0x98, 0x76,
- 0x54, 0x32, 0x10, 0x42};
- QuicConnectionId connection_id(connection_id_bytes,
- sizeof(connection_id_bytes));
framer_.SetShouldUpdateExpectedConnectionIdLength(true);
// clang-format off
@@ -12982,7 +13056,8 @@
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
- EXPECT_EQ(visitor_.header_.get()->destination_connection_id, connection_id);
+ EXPECT_EQ(visitor_.header_.get()->destination_connection_id,
+ FramerTestConnectionIdNineBytes());
EXPECT_EQ(visitor_.header_.get()->packet_number,
QuicPacketNumber(UINT64_C(0x12345678)));
@@ -13005,7 +13080,8 @@
EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
ASSERT_TRUE(visitor_.header_.get());
- EXPECT_EQ(visitor_.header_.get()->destination_connection_id, connection_id);
+ EXPECT_EQ(visitor_.header_.get()->destination_connection_id,
+ FramerTestConnectionIdNineBytes());
EXPECT_EQ(visitor_.header_.get()->packet_number,
QuicPacketNumber(UINT64_C(0x13374233)));
}
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index 24d1eb8..ddf5d3f 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -469,8 +469,7 @@
// static
bool QuicUtils::VariableLengthConnectionIdAllowedForVersion(
QuicTransportVersion version) {
- // TODO(dschinazi): Allow in appropriate version when supported.
- return false;
+ return version == QUIC_VERSION_99;
}
// static
diff --git a/quic/core/quic_utils_test.cc b/quic/core/quic_utils_test.cc
index 5f3b321..6ee6a2a 100644
--- a/quic/core/quic_utils_test.cc
+++ b/quic/core/quic_utils_test.cc
@@ -188,6 +188,8 @@
QuicUtils::CreateZeroConnectionId(QUIC_VERSION_99), QUIC_VERSION_99));
EXPECT_NE(QuicUtils::CreateZeroConnectionId(QUIC_VERSION_39),
EmptyQuicConnectionId());
+ EXPECT_EQ(QuicUtils::CreateZeroConnectionId(QUIC_VERSION_99),
+ EmptyQuicConnectionId());
EXPECT_FALSE(QuicUtils::IsConnectionIdValidForVersion(EmptyQuicConnectionId(),
QUIC_VERSION_39));
}