Support QUIC Client connection IDs
This CL is almost exclusively plumbing client connection IDs through the stack now that previous CLs handled the behavior changes. It also adds tests for the new behavior at each layer.
gfe-relnote: support client connection IDs, protected by quic_do_not_override_connection_id
PiperOrigin-RevId: 251599233
Change-Id: I7cda028f8aa56e6da451b6d86877fd0f84d93531
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 68655c8..6a0081a 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -212,6 +212,7 @@
stream_factory_(nullptr),
support_server_push_(false),
override_server_connection_id_(nullptr),
+ override_client_connection_id_(nullptr),
expected_server_connection_id_length_(kQuicDefaultConnectionIdLength) {
SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, true);
@@ -262,6 +263,9 @@
if (override_server_connection_id_ != nullptr) {
client->UseConnectionId(*override_server_connection_id_);
}
+ if (override_client_connection_id_ != nullptr) {
+ client->UseClientConnectionId(*override_client_connection_id_);
+ }
client->Connect();
return client;
}
@@ -541,6 +545,7 @@
std::string pre_shared_key_client_;
std::string pre_shared_key_server_;
QuicConnectionId* override_server_connection_id_;
+ QuicConnectionId* override_client_connection_id_;
uint8_t expected_server_connection_id_length_;
};
@@ -641,6 +646,43 @@
.length());
}
+TEST_P(EndToEndTest, ClientConnectionId) {
+ if (!GetParam().negotiated_version.SupportsClientConnectionIds()) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ QuicConnectionId client_connection_id =
+ TestConnectionId(UINT64_C(0xc1c2c3c4c5c6c7c8));
+ override_client_connection_id_ = &client_connection_id;
+ ASSERT_TRUE(Initialize());
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(client_connection_id, client_->client()
+ ->client_session()
+ ->connection()
+ ->client_connection_id());
+}
+
+TEST_P(EndToEndTest, ForcedVersionNegotiationAndClientConnectionId) {
+ if (!GetParam().negotiated_version.SupportsClientConnectionIds()) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ client_supported_versions_.insert(client_supported_versions_.begin(),
+ QuicVersionReservedForNegotiation());
+ QuicConnectionId client_connection_id =
+ TestConnectionId(UINT64_C(0xc1c2c3c4c5c6c7c8));
+ override_client_connection_id_ = &client_connection_id;
+ ASSERT_TRUE(Initialize());
+ ASSERT_TRUE(ServerSendsVersionNegotiation());
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(client_connection_id, client_->client()
+ ->client_session()
+ ->connection()
+ ->client_connection_id());
+}
+
TEST_P(EndToEndTest, ForcedVersionNegotiationAndBadConnectionIdLength) {
if (!GetQuicRestartFlag(
quic_allow_variable_length_connection_id_for_negotiation)) {
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 471c0c9..1c589d6 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -243,6 +243,8 @@
clock_(helper->GetClock()),
random_generator_(helper->GetRandomGenerator()),
server_connection_id_(server_connection_id),
+ client_connection_id_(EmptyQuicConnectionId()),
+ client_connection_id_is_set_(false),
peer_address_(initial_peer_address),
direct_peer_address_(initial_peer_address),
active_effective_peer_migration_type_(NO_CHANGE),
@@ -830,30 +832,54 @@
QuicConnectionId server_connection_id =
GetServerConnectionIdAsRecipient(header, perspective_);
- if (server_connection_id == server_connection_id_ ||
- HasIncomingConnectionId(server_connection_id)) {
+ if (server_connection_id != server_connection_id_ &&
+ !HasIncomingConnectionId(server_connection_id)) {
+ if (PacketCanReplaceConnectionId(header, perspective_)) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID "
+ << server_connection_id << " instead of "
+ << server_connection_id_;
+ return true;
+ }
+
+ ++stats_.packets_dropped;
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Ignoring packet from unexpected server connection ID "
+ << server_connection_id << " instead of "
+ << server_connection_id_;
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnIncorrectConnectionId(server_connection_id);
+ }
+ // If this is a server, the dispatcher routes each packet to the
+ // QuicConnection responsible for the packet's connection ID. So if control
+ // arrives here and this is a server, the dispatcher must be malfunctioning.
+ DCHECK_NE(Perspective::IS_SERVER, perspective_);
+ return false;
+ }
+
+ if (!version().SupportsClientConnectionIds()) {
return true;
}
- if (PacketCanReplaceConnectionId(header, perspective_)) {
- QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID "
- << server_connection_id << " instead of "
- << server_connection_id_;
+ QuicConnectionId client_connection_id =
+ GetClientConnectionIdAsRecipient(header, perspective_);
+
+ if (client_connection_id == client_connection_id_) {
+ return true;
+ }
+
+ if (!client_connection_id_is_set_ && perspective_ == Perspective::IS_SERVER) {
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Setting client connection ID from first packet to "
+ << client_connection_id;
+ set_client_connection_id(client_connection_id);
return true;
}
++stats_.packets_dropped;
QUIC_DLOG(INFO) << ENDPOINT
- << "Ignoring packet from unexpected ConnectionId: "
- << server_connection_id << " instead of "
- << server_connection_id_;
- if (debug_visitor_ != nullptr) {
- debug_visitor_->OnIncorrectConnectionId(server_connection_id);
- }
- // If this is a server, the dispatcher routes each packet to the
- // QuicConnection responsible for the packet's connection ID. So if control
- // arrives here and this is a server, the dispatcher must be malfunctioning.
- DCHECK_NE(Perspective::IS_SERVER, perspective_);
+ << "Ignoring packet from unexpected client connection ID "
+ << client_connection_id << " instead of "
+ << client_connection_id_;
return false;
}
@@ -4259,5 +4285,23 @@
return received_packet_manager_.ack_frame();
}
+void QuicConnection::set_client_connection_id(
+ QuicConnectionId client_connection_id) {
+ if (!version().SupportsClientConnectionIds()) {
+ QUIC_BUG_IF(!client_connection_id.IsEmpty())
+ << ENDPOINT << "Attempted to use client connection ID "
+ << client_connection_id << " with unsupported version " << version();
+ return;
+ }
+ client_connection_id_ = client_connection_id;
+ client_connection_id_is_set_ = true;
+ QUIC_DLOG(INFO) << ENDPOINT << "setting client connection ID to "
+ << client_connection_id_
+ << " for connection with server connection ID "
+ << server_connection_id_;
+ packet_generator_.SetClientConnectionId(client_connection_id_);
+ framer_.SetExpectedClientConnectionIdLength(client_connection_id_.length());
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 9392f26..a0d0cd0 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -582,6 +582,10 @@
return effective_peer_address_;
}
QuicConnectionId connection_id() const { return server_connection_id_; }
+ QuicConnectionId client_connection_id() const {
+ return client_connection_id_;
+ }
+ void set_client_connection_id(QuicConnectionId client_connection_id);
const QuicClock* clock() const { return clock_; }
QuicRandom* random_generator() const { return random_generator_; }
QuicByteCount max_packet_length() const;
@@ -1173,6 +1177,10 @@
QuicRandom* random_generator_;
QuicConnectionId server_connection_id_;
+ QuicConnectionId client_connection_id_;
+ // On the server, the connection ID is set when receiving the first packet.
+ // This variable ensures we only set it this way once.
+ bool client_connection_id_is_set_;
// Address on the last successfully processed packet received from the
// direct peer.
QuicSocketAddress self_address_;
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index e3a5887..9cca434 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -8415,6 +8415,96 @@
EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
}
+// Make sure a packet received with the right client connection ID is processed.
+TEST_P(QuicConnectionTest, ValidClientConnectionId) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ if (!framer_.version().SupportsClientConnectionIds()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.set_client_connection_id(TestConnectionId(0x33));
+ QuicPacketHeader header = ConstructPacketHeader(1, ENCRYPTION_FORWARD_SECURE);
+ header.destination_connection_id = TestConnectionId(0x33);
+ header.destination_connection_id_included = CONNECTION_ID_PRESENT;
+ header.source_connection_id_included = CONNECTION_ID_ABSENT;
+ QuicFrames frames;
+ QuicPingFrame ping_frame;
+ QuicPaddingFrame padding_frame;
+ frames.push_back(QuicFrame(ping_frame));
+ frames.push_back(QuicFrame(padding_frame));
+ std::unique_ptr<QuicPacket> packet =
+ BuildUnsizedDataPacket(&framer_, header, frames);
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length = peer_framer_.EncryptPayload(
+ ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(1), *packet, buffer,
+ kMaxOutgoingPacketSize);
+ QuicReceivedPacket received_packet(buffer, encrypted_length, clock_.Now(),
+ false);
+ EXPECT_EQ(0u, connection_.GetStats().packets_dropped);
+ ProcessReceivedPacket(kSelfAddress, kPeerAddress, received_packet);
+ EXPECT_EQ(0u, connection_.GetStats().packets_dropped);
+}
+
+// Make sure a packet received with a different client connection ID is dropped.
+TEST_P(QuicConnectionTest, InvalidClientConnectionId) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ if (!framer_.version().SupportsClientConnectionIds()) {
+ return;
+ }
+ connection_.set_client_connection_id(TestConnectionId(0x33));
+ QuicPacketHeader header = ConstructPacketHeader(1, ENCRYPTION_FORWARD_SECURE);
+ header.destination_connection_id = TestConnectionId(0xbad);
+ header.destination_connection_id_included = CONNECTION_ID_PRESENT;
+ header.source_connection_id_included = CONNECTION_ID_ABSENT;
+ QuicFrames frames;
+ QuicPingFrame ping_frame;
+ QuicPaddingFrame padding_frame;
+ frames.push_back(QuicFrame(ping_frame));
+ frames.push_back(QuicFrame(padding_frame));
+ std::unique_ptr<QuicPacket> packet =
+ BuildUnsizedDataPacket(&framer_, header, frames);
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length = peer_framer_.EncryptPayload(
+ ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(1), *packet, buffer,
+ kMaxOutgoingPacketSize);
+ QuicReceivedPacket received_packet(buffer, encrypted_length, clock_.Now(),
+ false);
+ EXPECT_EQ(0u, connection_.GetStats().packets_dropped);
+ ProcessReceivedPacket(kSelfAddress, kPeerAddress, received_packet);
+ EXPECT_EQ(1u, connection_.GetStats().packets_dropped);
+}
+
+// Make sure the first packet received with a different client connection ID on
+// the server is processed and it changes the client connection ID.
+TEST_P(QuicConnectionTest, UpdateClientConnectionIdFromFirstPacket) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ if (!framer_.version().SupportsClientConnectionIds()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketHeader header = ConstructPacketHeader(1, ENCRYPTION_INITIAL);
+ header.source_connection_id = TestConnectionId(0x33);
+ header.source_connection_id_included = CONNECTION_ID_PRESENT;
+ QuicFrames frames;
+ QuicPingFrame ping_frame;
+ QuicPaddingFrame padding_frame;
+ frames.push_back(QuicFrame(ping_frame));
+ frames.push_back(QuicFrame(padding_frame));
+ std::unique_ptr<QuicPacket> packet =
+ BuildUnsizedDataPacket(&framer_, header, frames);
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length = peer_framer_.EncryptPayload(
+ ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(1), *packet, buffer,
+ kMaxOutgoingPacketSize);
+ QuicReceivedPacket received_packet(buffer, encrypted_length, clock_.Now(),
+ false);
+ EXPECT_EQ(0u, connection_.GetStats().packets_dropped);
+ ProcessReceivedPacket(kSelfAddress, kPeerAddress, received_packet);
+ EXPECT_EQ(0u, connection_.GetStats().packets_dropped);
+ EXPECT_EQ(TestConnectionId(0x33), connection_.client_connection_id());
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index 317f469..7228af3 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -472,8 +472,9 @@
current_packet_->length() >= kMinPacketSizeForVersionNegotiation) {
// Since the version is not supported, send a version negotiation
// packet and stop processing the current packet.
+ QuicConnectionId client_connection_id = header.source_connection_id;
time_wait_list_manager()->SendVersionNegotiationPacket(
- server_connection_id, EmptyQuicConnectionId(),
+ server_connection_id, client_connection_id,
header.form != GOOGLE_QUIC_PACKET, GetSupportedVersions(),
current_self_address_, current_peer_address_,
GetPerPacketContext());
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index e6936ab..188707b 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -238,64 +238,82 @@
// 6 byte packet number, default path id, and packet number 1,
// using the first supported version.
void ProcessPacket(QuicSocketAddress peer_address,
- QuicConnectionId connection_id,
+ QuicConnectionId server_connection_id,
bool has_version_flag,
const std::string& data) {
- ProcessPacket(peer_address, connection_id, has_version_flag, data,
+ ProcessPacket(peer_address, server_connection_id, has_version_flag, data,
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER);
}
// Process a packet with a default path id, and packet number 1,
// using the first supported version.
void ProcessPacket(QuicSocketAddress peer_address,
- QuicConnectionId connection_id,
+ QuicConnectionId server_connection_id,
bool has_version_flag,
const std::string& data,
- QuicConnectionIdIncluded connection_id_included,
+ QuicConnectionIdIncluded server_connection_id_included,
QuicPacketNumberLength packet_number_length) {
- ProcessPacket(peer_address, connection_id, has_version_flag, data,
- connection_id_included, packet_number_length, 1);
+ ProcessPacket(peer_address, server_connection_id, has_version_flag, data,
+ server_connection_id_included, packet_number_length, 1);
}
// Process a packet using the first supported version.
void ProcessPacket(QuicSocketAddress peer_address,
- QuicConnectionId connection_id,
+ QuicConnectionId server_connection_id,
bool has_version_flag,
const std::string& data,
- QuicConnectionIdIncluded connection_id_included,
+ QuicConnectionIdIncluded server_connection_id_included,
QuicPacketNumberLength packet_number_length,
uint64_t packet_number) {
- ProcessPacket(peer_address, connection_id, has_version_flag,
+ ProcessPacket(peer_address, server_connection_id, has_version_flag,
CurrentSupportedVersions().front(), data,
- connection_id_included, packet_number_length, packet_number);
+ server_connection_id_included, packet_number_length,
+ packet_number);
}
// Processes a packet.
void ProcessPacket(QuicSocketAddress peer_address,
- QuicConnectionId connection_id,
+ QuicConnectionId server_connection_id,
bool has_version_flag,
ParsedQuicVersion version,
const std::string& data,
- QuicConnectionIdIncluded connection_id_included,
+ QuicConnectionIdIncluded server_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ uint64_t packet_number) {
+ ProcessPacket(peer_address, server_connection_id, EmptyQuicConnectionId(),
+ has_version_flag, version, data,
+ server_connection_id_included, CONNECTION_ID_ABSENT,
+ packet_number_length, packet_number);
+ }
+
+ // Processes a packet.
+ void ProcessPacket(QuicSocketAddress peer_address,
+ QuicConnectionId server_connection_id,
+ QuicConnectionId client_connection_id,
+ bool has_version_flag,
+ ParsedQuicVersion version,
+ const std::string& data,
+ QuicConnectionIdIncluded server_connection_id_included,
+ QuicConnectionIdIncluded client_connection_id_included,
QuicPacketNumberLength packet_number_length,
uint64_t packet_number) {
ParsedQuicVersionVector versions(SupportedVersions(version));
std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
- connection_id, EmptyQuicConnectionId(), has_version_flag, false,
- packet_number, data, connection_id_included, CONNECTION_ID_ABSENT,
- packet_number_length, &versions));
+ server_connection_id, client_connection_id, has_version_flag, false,
+ packet_number, data, server_connection_id_included,
+ client_connection_id_included, packet_number_length, &versions));
std::unique_ptr<QuicReceivedPacket> received_packet(
ConstructReceivedPacket(*packet, mock_helper_.GetClock()->Now()));
if (ChloExtractor::Extract(*packet, versions, {}, nullptr,
- connection_id.length())) {
+ server_connection_id.length())) {
// Add CHLO packet to the beginning to be verified first, because it is
// also processed first by new session.
- data_connection_map_[connection_id].push_front(
+ data_connection_map_[server_connection_id].push_front(
std::string(packet->data(), packet->length()));
} else {
// For non-CHLO, always append to last.
- data_connection_map_[connection_id].push_back(
+ data_connection_map_[server_connection_id].push_back(
std::string(packet->data(), packet->length()));
}
dispatcher_->ProcessPacket(server_address_, peer_address, *received_packet);
@@ -484,8 +502,9 @@
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
- EXPECT_CALL(*time_wait_list_manager_,
- SendVersionNegotiationPacket(_, _, _, _, _, _, _))
+ EXPECT_CALL(
+ *time_wait_list_manager_,
+ SendVersionNegotiationPacket(TestConnectionId(1), _, _, _, _, _, _))
.Times(1);
// Pad the CHLO message with enough data to make the packet large enough
// to trigger version negotiation.
@@ -496,6 +515,26 @@
CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
}
+TEST_F(QuicDispatcherTest, StatelessVersionNegotiationWithClientConnectionId) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ CreateTimeWaitListManager();
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ SendVersionNegotiationPacket(TestConnectionId(1),
+ TestConnectionId(2), _, _, _, _, _))
+ .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, TestConnectionId(1), TestConnectionId(2), true,
+ QuicVersionReservedForNegotiation(), chlo,
+ CONNECTION_ID_PRESENT, CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) {
CreateTimeWaitListManager();
QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 27237d7..34b5dbb 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -453,6 +453,7 @@
: visitor_(nullptr),
error_(QUIC_NO_ERROR),
last_serialized_server_connection_id_(EmptyQuicConnectionId()),
+ last_serialized_client_connection_id_(EmptyQuicConnectionId()),
last_version_label_(0),
version_(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED),
supported_versions_(supported_versions),
@@ -470,6 +471,7 @@
Perspective::IS_CLIENT),
expected_server_connection_id_length_(
expected_server_connection_id_length),
+ expected_client_connection_id_length_(0),
should_update_expected_server_connection_id_length_(false),
supports_multiple_packet_number_spaces_(false),
last_written_packet_number_length_(0) {
@@ -2168,6 +2170,10 @@
}
last_serialized_server_connection_id_ = server_connection_id;
+ if (version_.SupportsClientConnectionIds()) {
+ last_serialized_client_connection_id_ =
+ GetClientConnectionIdAsSender(header, perspective_);
+ }
if (QuicVersionHasLongHeaderLengths(transport_version()) &&
header.version_flag) {
@@ -2490,15 +2496,18 @@
if (header->form == IETF_QUIC_LONG_HEADER_PACKET) {
// Version is always present in long headers.
header->version_flag = true;
- // Long header packets received by client must include 8-byte source
- // connection ID, and those received by server must include 8-byte
- // destination connection ID.
+ // In versions that do not support client connection IDs, we mark the
+ // corresponding connection ID as absent.
header->destination_connection_id_included =
- perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT
- : CONNECTION_ID_PRESENT;
+ (perspective_ == Perspective::IS_SERVER ||
+ version_.SupportsClientConnectionIds())
+ ? CONNECTION_ID_PRESENT
+ : CONNECTION_ID_ABSENT;
header->source_connection_id_included =
- perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_PRESENT
- : CONNECTION_ID_ABSENT;
+ (perspective_ == Perspective::IS_CLIENT ||
+ version_.SupportsClientConnectionIds())
+ ? CONNECTION_ID_PRESENT
+ : CONNECTION_ID_ABSENT;
// Read version tag.
QuicVersionLabel version_label;
if (!ProcessVersionLabel(reader, &version_label)) {
@@ -2552,11 +2561,13 @@
QUIC_DVLOG(1) << ENDPOINT << "Received IETF short header";
// Version is not present in short headers.
header->version_flag = false;
- // Connection ID length depends on the perspective. Client does not expect
- // destination connection ID, and server expects destination connection ID.
+ // In versions that do not support client connection IDs, the client will not
+ // receive destination connection IDs.
header->destination_connection_id_included =
- perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT
- : CONNECTION_ID_PRESENT;
+ (perspective_ == Perspective::IS_SERVER ||
+ version_.SupportsClientConnectionIds())
+ ? CONNECTION_ID_PRESENT
+ : CONNECTION_ID_ABSENT;
header->source_connection_id_included = CONNECTION_ID_ABSENT;
// TODO(fayang): remove version check when deprecating
// quic_no_framer_object_in_dispatcher.
@@ -2644,11 +2655,15 @@
uint8_t destination_connection_id_length =
header->destination_connection_id_included == CONNECTION_ID_PRESENT
- ? expected_server_connection_id_length_
+ ? (perspective_ == Perspective::IS_SERVER
+ ? expected_server_connection_id_length_
+ : expected_client_connection_id_length_)
: 0;
uint8_t source_connection_id_length =
header->source_connection_id_included == CONNECTION_ID_PRESENT
- ? expected_server_connection_id_length_
+ ? (perspective_ == Perspective::IS_CLIENT
+ ? expected_server_connection_id_length_
+ : expected_client_connection_id_length_)
: 0;
if (header->form == IETF_QUIC_LONG_HEADER_PACKET) {
if (!ProcessAndValidateIetfConnectionIdLength(
@@ -2694,8 +2709,17 @@
} else {
QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 5, 5);
if (header->source_connection_id_included == CONNECTION_ID_ABSENT) {
- DCHECK_EQ(EmptyQuicConnectionId(), header->source_connection_id);
- header->source_connection_id = last_serialized_server_connection_id_;
+ if (!header->source_connection_id.IsEmpty()) {
+ DCHECK(!version_.SupportsClientConnectionIds());
+ set_detailed_error(
+ "Client connection ID not supported in this version.");
+ return false;
+ }
+ if (perspective_ == Perspective::IS_CLIENT) {
+ header->source_connection_id = last_serialized_server_connection_id_;
+ } else {
+ header->source_connection_id = last_serialized_client_connection_id_;
+ }
}
}
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 57b1ed7..9b9c48f 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -588,6 +588,14 @@
return expected_server_connection_id_length_;
}
+ // Change the expected destination connection ID length for short headers on
+ // the client.
+ void SetExpectedClientConnectionIdLength(
+ uint8_t expected_client_connection_id_length) {
+ expected_client_connection_id_length_ =
+ expected_client_connection_id_length;
+ }
+
void EnableMultiplePacketNumberSpacesSupport();
// Writes an array of bytes that, if sent as a UDP datagram, will trigger
@@ -967,6 +975,8 @@
QuicPacketNumber largest_decrypted_packet_numbers_[NUM_PACKET_NUMBER_SPACES];
// Last server connection ID seen on the wire.
QuicConnectionId last_serialized_server_connection_id_;
+ // Last client connection ID seen on the wire.
+ QuicConnectionId last_serialized_client_connection_id_;
// The last QUIC version label received.
// TODO(fayang): Remove this when deprecating
// quic_no_framer_object_in_dispatcher.
@@ -1021,10 +1031,11 @@
bool infer_packet_header_type_from_version_;
// IETF short headers contain a destination connection ID but do not
- // 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.
+ // encode its length. These variables contains the length we expect to read.
+ // This is also used to validate the long header destination connection ID
+ // lengths in older versions of QUIC.
uint8_t expected_server_connection_id_length_;
+ uint8_t expected_client_connection_id_length_;
// When this is true, QuicFramer will change
// expected_server_connection_id_length_ to the received destination
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 6890c85..37ed301 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -1017,6 +1017,73 @@
EXPECT_EQ(FramerTestConnectionIdPlusOne(), source_connection_id);
}
+TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToClient) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ if (!framer_.version().SupportsClientConnectionIds()) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicFramerPeer::SetLastSerializedServerConnectionId(&framer_,
+ TestConnectionId(0x33));
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ framer_.SetExpectedClientConnectionIdLength(kQuicDefaultConnectionIdLength);
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x13, 0x37, 0x42, 0x33,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_EQ("", framer_.detailed_error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id);
+}
+
+// In short header packets from client to server, the client connection ID
+// is omitted, but the framer adds it to the header struct using its
+// last serialized client connection ID. This test ensures that this
+// mechanism behaves as expected.
+TEST_P(QuicFramerTest, ClientConnectionIdFromShortHeaderToServer) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ if (!framer_.version().SupportsClientConnectionIds()) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicFramerPeer::SetLastSerializedClientConnectionId(&framer_,
+ TestConnectionId(0x33));
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x13, 0x37, 0x42, 0x33,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_EQ("", framer_.detailed_error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_EQ(TestConnectionId(0x33), visitor_.header_->source_connection_id);
+}
+
TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) {
SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
QuicFramerPeer::SetLastSerializedServerConnectionId(&framer_,
@@ -12188,24 +12255,21 @@
parsed_probe_payload_bytes, parsed_probe_payload_length);
}
-TEST_P(QuicFramerTest, ClientConnectionIdNotSupportedYet) {
- if (GetQuicRestartFlag(quic_do_not_override_connection_id)) {
- // This check is currently only performed when this flag is disabled.
- return;
- }
+TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToClient) {
if (framer_.transport_version() <= QUIC_VERSION_43) {
// This test requires an IETF long header.
return;
}
+ SetDecrypterLevel(ENCRYPTION_HANDSHAKE);
QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
// clang-format off
unsigned char packet[] = {
- // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // public flags (long header with packet type HANDSHAKE and
// 4-byte packet number)
- 0xD3,
+ 0xE3,
// version
QUIC_VERSION_BYTES,
- // destination connection ID length
+ // connection ID lengths
0x50,
// destination connection ID
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
@@ -12217,16 +12281,80 @@
0x00,
};
// clang-format on
- EXPECT_FALSE(framer_.ProcessPacket(
- QuicEncryptedPacket(AsChars(packet), QUIC_ARRAYSIZE(packet), false)));
- EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+ const bool parse_success = framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(packet), QUIC_ARRAYSIZE(packet), false));
if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
framer_.transport_version())) {
+ EXPECT_FALSE(parse_success);
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
EXPECT_EQ("Invalid ConnectionId length.", framer_.detailed_error());
- } else {
+ return;
+ }
+ if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) {
+ // When the flag is disabled we expect processing to fail.
+ EXPECT_FALSE(parse_success);
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
EXPECT_EQ("Client connection ID not supported yet.",
framer_.detailed_error());
+ return;
}
+ EXPECT_TRUE(parse_success);
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_EQ("", framer_.detailed_error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_.get()->destination_connection_id);
+}
+
+TEST_P(QuicFramerTest, ClientConnectionIdFromLongHeaderToServer) {
+ if (framer_.transport_version() <= QUIC_VERSION_43) {
+ // This test requires an IETF long header.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_HANDSHAKE);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (long header with packet type HANDSHAKE and
+ // 4-byte packet number)
+ 0xE3,
+ // version
+ QUIC_VERSION_BYTES,
+ // connection ID lengths
+ 0x05,
+ // source connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x00,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+ const bool parse_success = framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(packet), QUIC_ARRAYSIZE(packet), false));
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ framer_.transport_version())) {
+ EXPECT_FALSE(parse_success);
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+ EXPECT_EQ("Invalid ConnectionId length.", framer_.detailed_error());
+ return;
+ }
+ if (!framer_.version().SupportsClientConnectionIds() &&
+ GetQuicRestartFlag(quic_do_not_override_connection_id)) {
+ EXPECT_FALSE(parse_success);
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+ EXPECT_EQ("Client connection ID not supported in this version.",
+ framer_.detailed_error());
+ return;
+ }
+ EXPECT_TRUE(parse_success);
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_EQ("", framer_.detailed_error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_.get()->source_connection_id);
}
TEST_P(QuicFramerTest, ProcessAndValidateIetfConnectionIdLengthClient) {
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index dcc3417..e411664 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -75,6 +75,7 @@
server_connection_id_included_(CONNECTION_ID_PRESENT),
packet_size_(0),
server_connection_id_(server_connection_id),
+ client_connection_id_(EmptyQuicConnectionId()),
packet_(QuicPacketNumber(),
PACKET_1BYTE_PACKET_NUMBER,
nullptr,
@@ -636,7 +637,7 @@
DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective());
std::unique_ptr<QuicEncryptedPacket> encrypted =
QuicFramer::BuildVersionNegotiationPacket(server_connection_id_,
- EmptyQuicConnectionId(),
+ client_connection_id_,
ietf_quic, supported_versions);
DCHECK(encrypted);
DCHECK_GE(max_packet_length_, encrypted->length());
@@ -751,7 +752,7 @@
}
QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 1, 5);
if (framer_->perspective() == Perspective::IS_SERVER) {
- return EmptyQuicConnectionId();
+ return client_connection_id_;
}
return server_connection_id_;
}
@@ -762,7 +763,7 @@
}
QUIC_RESTART_FLAG_COUNT_N(quic_do_not_override_connection_id, 6, 6);
if (framer_->perspective() == Perspective::IS_CLIENT) {
- return EmptyQuicConnectionId();
+ return client_connection_id_;
}
return server_connection_id_;
}
@@ -771,9 +772,10 @@
const {
if (VersionHasIetfInvariantHeader(framer_->transport_version()) ||
GetQuicRestartFlag(quic_do_not_override_connection_id)) {
- // Packets sent by client always include destination connection ID, and
- // those sent by the server do not include destination connection ID.
- return framer_->perspective() == Perspective::IS_CLIENT
+ // In versions that do not support client connection IDs, the destination
+ // connection ID is only sent from client to server.
+ return (framer_->perspective() == Perspective::IS_CLIENT ||
+ framer_->version().SupportsClientConnectionIds())
? CONNECTION_ID_PRESENT
: CONNECTION_ID_ABSENT;
}
@@ -783,7 +785,11 @@
QuicConnectionIdIncluded QuicPacketCreator::GetSourceConnectionIdIncluded()
const {
// Long header packets sent by server include source connection ID.
- if (HasIetfLongHeader() && framer_->perspective() == Perspective::IS_SERVER) {
+ // Ones sent by the client only include source connection ID if the version
+ // supports client connection IDs.
+ if (HasIetfLongHeader() &&
+ (framer_->perspective() == Perspective::IS_SERVER ||
+ framer_->version().SupportsClientConnectionIds())) {
return CONNECTION_ID_PRESENT;
}
if (GetQuicRestartFlag(quic_do_not_override_connection_id) &&
@@ -1043,6 +1049,15 @@
server_connection_id_ = server_connection_id;
}
+void QuicPacketCreator::SetClientConnectionId(
+ QuicConnectionId client_connection_id) {
+ DCHECK(client_connection_id.IsEmpty() ||
+ framer_->version().SupportsClientConnectionIds());
+ DCHECK(client_connection_id.IsEmpty() ||
+ GetQuicRestartFlag(quic_do_not_override_connection_id));
+ client_connection_id_ = client_connection_id;
+}
+
void QuicPacketCreator::SetTransmissionType(TransmissionType type) {
DCHECK(can_set_transmission_type_);
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index a7b1d5c..e9306b3 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -228,6 +228,9 @@
// Update the server connection ID used in outgoing packets.
void SetServerConnectionId(QuicConnectionId server_connection_id);
+ // Update the client connection ID used in outgoing packets.
+ void SetClientConnectionId(QuicConnectionId client_connection_id);
+
// Sets the encryption level that will be applied to new packets.
void set_encryption_level(EncryptionLevel level) {
packet_.encryption_level = level;
@@ -407,6 +410,7 @@
// QuicEncryptedPacket has been flattened into SerializedPacket.
size_t packet_size_;
QuicConnectionId server_connection_id_;
+ QuicConnectionId client_connection_id_;
// Packet used to invoke OnSerializedPacket.
SerializedPacket packet_;
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index eab8670..665dbac 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -1916,6 +1916,18 @@
EXPECT_EQ(EmptyQuicConnectionId(), creator_.GetSourceConnectionId());
}
+TEST_P(QuicPacketCreatorTest, ClientConnectionId) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ if (!client_framer_.version().SupportsClientConnectionIds()) {
+ return;
+ }
+ EXPECT_EQ(TestConnectionId(2), creator_.GetDestinationConnectionId());
+ EXPECT_EQ(EmptyQuicConnectionId(), creator_.GetSourceConnectionId());
+ creator_.SetClientConnectionId(TestConnectionId(0x33));
+ EXPECT_EQ(TestConnectionId(2), creator_.GetDestinationConnectionId());
+ EXPECT_EQ(TestConnectionId(0x33), creator_.GetSourceConnectionId());
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc
index d57aad4..614182c 100644
--- a/quic/core/quic_packet_generator.cc
+++ b/quic/core/quic_packet_generator.cc
@@ -588,4 +588,9 @@
packet_creator_.SetServerConnectionId(server_connection_id);
}
+void QuicPacketGenerator::SetClientConnectionId(
+ QuicConnectionId client_connection_id) {
+ packet_creator_.SetClientConnectionId(client_connection_id);
+}
+
} // namespace quic
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h
index 327cc47..96d2cf2 100644
--- a/quic/core/quic_packet_generator.h
+++ b/quic/core/quic_packet_generator.h
@@ -238,6 +238,9 @@
// Update the server connection ID used in outgoing packets.
void SetServerConnectionId(QuicConnectionId server_connection_id);
+ // Update the client connection ID used in outgoing packets.
+ void SetClientConnectionId(QuicConnectionId client_connection_id);
+
void set_debug_delegate(QuicPacketCreator::DebugDelegate* debug_delegate) {
packet_creator_.set_debug_delegate(debug_delegate);
}
diff --git a/quic/core/quic_packet_generator_test.cc b/quic/core/quic_packet_generator_test.cc
index 1756874..6f3f8f3 100644
--- a/quic/core/quic_packet_generator_test.cc
+++ b/quic/core/quic_packet_generator_test.cc
@@ -1666,5 +1666,18 @@
&storage)));
}
+TEST_F(QuicPacketGeneratorTest, ConnectionId) {
+ SetQuicRestartFlag(quic_do_not_override_connection_id, true);
+ generator_.SetServerConnectionId(TestConnectionId(0x1337));
+ EXPECT_EQ(TestConnectionId(0x1337), creator_->GetDestinationConnectionId());
+ EXPECT_EQ(EmptyQuicConnectionId(), creator_->GetSourceConnectionId());
+ if (!framer_.version().SupportsClientConnectionIds()) {
+ return;
+ }
+ generator_.SetClientConnectionId(TestConnectionId(0x33));
+ EXPECT_EQ(TestConnectionId(0x1337), creator_->GetDestinationConnectionId());
+ EXPECT_EQ(TestConnectionId(0x33), creator_->GetSourceConnectionId());
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc
index e23fc53..d540def 100644
--- a/quic/core/quic_versions.cc
+++ b/quic/core/quic_versions.cc
@@ -51,9 +51,12 @@
}
bool ParsedQuicVersion::SupportsClientConnectionIds() const {
- // This will be enabled in v99 after the rest of the client connection ID
- // code lands.
- return false;
+ if (!GetQuicRestartFlag(quic_do_not_override_connection_id)) {
+ // Do not enable this feature in a production version until this flag has
+ // been deprecated.
+ return false;
+ }
+ return transport_version >= QUIC_VERSION_99;
}
std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version) {