Add ShouldUpdateExpectedConnectionIdLength to QuicFramer
ShouldUpdateExpectedConnectionIdLength allows clients of QuicDispatcher like Quartc to have the QuicFramer owned by the QuicDispatcher dynamically change its expected connection ID length based on received long header packets. This will allow quartc to migrate to 0-length connection IDs.
gfe-relnote: refactor-only, no behavior change, not flag protected
PiperOrigin-RevId: 237882443
Change-Id: If7a49b092e1ff29fec740a18bbb7c5b78d749d62
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index 77d4952..aadd06b 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -349,6 +349,14 @@
// Skip validating that the public flags are set to legal values.
void DisableFlagValidation();
+ // If true, our framer will change its expected connection ID length
+ // to the received destination connection ID length of all IETF long headers.
+ void SetShouldUpdateExpectedConnectionIdLength(
+ bool should_update_expected_connection_id_length) {
+ framer_.SetShouldUpdateExpectedConnectionIdLength(
+ should_update_expected_connection_id_length);
+ }
+
private:
friend class test::QuicDispatcherPeer;
friend class StatelessRejectorProcessDoneCallback;
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 22d0ecf..32d50c5 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -473,7 +473,8 @@
data_producer_(nullptr),
infer_packet_header_type_from_version_(perspective ==
Perspective::IS_CLIENT),
- expected_connection_id_length_(expected_connection_id_length) {
+ expected_connection_id_length_(expected_connection_id_length),
+ should_update_expected_connection_id_length_(false) {
DCHECK(!supported_versions.empty());
version_ = supported_versions_[0];
decrypter_ = QuicMakeUnique<NullDecrypter>(perspective);
@@ -2565,12 +2566,20 @@
if (dcil != 0) {
dcil += kConnectionIdLengthAdjustment;
}
+ if (should_update_expected_connection_id_length_ &&
+ expected_connection_id_length_ != dcil) {
+ QUIC_DVLOG(1) << ENDPOINT << "Updating expected_connection_id_length: "
+ << static_cast<int>(expected_connection_id_length_)
+ << " -> " << static_cast<int>(dcil);
+ expected_connection_id_length_ = dcil;
+ }
uint8_t scil = connection_id_lengths_byte & kSourceConnectionIdLengthMask;
if (scil != 0) {
scil += kConnectionIdLengthAdjustment;
}
- if (dcil != destination_connection_id_length ||
- scil != source_connection_id_length) {
+ if ((dcil != destination_connection_id_length ||
+ scil != source_connection_id_length) &&
+ !should_update_expected_connection_id_length_) {
QUIC_DVLOG(1) << "dcil: " << static_cast<uint32_t>(dcil)
<< ", scil: " << static_cast<uint32_t>(scil);
set_detailed_error("Invalid ConnectionId length.");
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index d6916f6..c18e9db 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -565,6 +565,14 @@
return first_sending_packet_number_;
}
+ // If true, QuicFramer will change its expected connection ID length
+ // to the received destination connection ID length of all IETF long headers.
+ void SetShouldUpdateExpectedConnectionIdLength(
+ bool should_update_expected_connection_id_length) {
+ should_update_expected_connection_id_length_ =
+ should_update_expected_connection_id_length;
+ }
+
private:
friend class test::QuicFramerPeer;
@@ -921,7 +929,11 @@
// encode its length. This variable contains the length we expect to read.
// This is also used to validate the long header connection ID lengths in
// older versions of QUIC.
- const uint8_t expected_connection_id_length_;
+ uint8_t expected_connection_id_length_;
+
+ // When this is true, QuicFramer will change expected_connection_id_length_
+ // to the received destination connection ID length of all IETF long headers.
+ bool should_update_expected_connection_id_length_;
};
} // namespace quic
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 527307a..c848c0a 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -12925,6 +12925,91 @@
CheckFramingBoundaries(packet, QUIC_INVALID_PACKET_HEADER);
}
+TEST_P(QuicFramerTest, UpdateExpectedConnectionIdLength) {
+ 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
+ unsigned char long_header_packet[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ unsigned char long_header_packet99[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // long header packet length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet),
+ QUIC_ARRAYSIZE(long_header_packet), false)));
+ } else {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet99),
+ QUIC_ARRAYSIZE(long_header_packet99), false)));
+ }
+
+ 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()->packet_number,
+ QuicPacketNumber(UINT64_C(0x12345678)));
+
+ // clang-format off
+ unsigned char short_header_packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x13, 0x37, 0x42, 0x33,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ QuicEncryptedPacket short_header_encrypted(
+ AsChars(short_header_packet), QUIC_ARRAYSIZE(short_header_packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(short_header_encrypted));
+
+ 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()->packet_number,
+ QuicPacketNumber(UINT64_C(0x13374233)));
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/quartc/quartc_dispatcher.cc b/quic/quartc/quartc_dispatcher.cc
index 8070203..4c35c54 100644
--- a/quic/quartc/quartc_dispatcher.cc
+++ b/quic/quartc/quartc_dispatcher.cc
@@ -21,18 +21,23 @@
std::unique_ptr<QuicAlarmFactory> alarm_factory,
std::unique_ptr<QuartcPacketWriter> packet_writer,
Delegate* delegate)
- : QuicDispatcher(config.get(),
- crypto_config.get(),
- version_manager,
- std::move(helper),
- std::move(session_helper),
- std::move(alarm_factory),
- kQuicDefaultConnectionIdLength),
+ : QuicDispatcher(
+ config.get(),
+ crypto_config.get(),
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ QuicUtils::CreateZeroConnectionId(
+ version_manager->GetSupportedVersions()[0].transport_version)
+ .length()),
owned_quic_config_(std::move(config)),
owned_crypto_config_(std::move(crypto_config)),
crypto_config_(crypto_config_serialized),
delegate_(delegate),
packet_writer_(packet_writer.get()) {
+ // Allow incoming packets to set our expected connection ID length.
+ SetShouldUpdateExpectedConnectionIdLength(true);
// QuicDispatcher takes ownership of the writer.
QuicDispatcher::InitializeWithWriter(packet_writer.release());
// NB: This must happen *after* InitializeWithWriter. It can call us back
@@ -50,6 +55,8 @@
const QuicSocketAddress& client_address,
QuicStringPiece alpn,
const ParsedQuicVersion& version) {
+ // Make our expected connection ID non-mutable since we have a connection.
+ SetShouldUpdateExpectedConnectionIdLength(false);
std::unique_ptr<QuicConnection> connection = CreateQuicConnection(
connection_id, client_address, helper(), alarm_factory(), writer(),
Perspective::IS_SERVER, ParsedQuicVersionVector{version});
diff --git a/quic/quartc/quartc_factory.cc b/quic/quartc/quartc_factory.cc
index 803122a..8efa544 100644
--- a/quic/quartc/quartc_factory.cc
+++ b/quic/quartc/quartc_factory.cc
@@ -197,13 +197,12 @@
QuartcPacketWriter* packet_writer) {
// |dummy_id| and |dummy_address| are used because Quartc network layer will
// not use these two.
- char connection_id_bytes[sizeof(uint64_t)] = {};
- QuicConnectionId dummy_id = QuicConnectionId(
- static_cast<char*>(connection_id_bytes), sizeof(connection_id_bytes));
- QuicSocketAddress dummy_address(QuicIpAddress::Any4(), /*port=*/0);
- return quic::CreateQuicConnection(
- dummy_id, dummy_address, connection_helper_.get(), alarm_factory_,
- packet_writer, perspective, supported_versions);
+ QuicConnectionId dummy_id = QuicUtils::CreateZeroConnectionId(
+ supported_versions[0].transport_version);
+ QuicSocketAddress dummy_address(QuicIpAddress::Any4(), /*port=*/0);
+ return quic::CreateQuicConnection(
+ dummy_id, dummy_address, connection_helper_.get(), alarm_factory_,
+ packet_writer, perspective, supported_versions);
}
std::unique_ptr<QuicConnection> CreateQuicConnection(