Add a ConnectionIdLength() interface to ConnectionIdGeneratorInterface.
As the connection ID length can change dynamically, QuicDispatcher will leverage this interface to know how many bytes to extract from short headers.
PiperOrigin-RevId: 480435323
diff --git a/quiche/quic/core/connection_id_generator.h b/quiche/quic/core/connection_id_generator.h
index 65284d8..d27b44f 100644
--- a/quiche/quic/core/connection_id_generator.h
+++ b/quiche/quic/core/connection_id_generator.h
@@ -23,6 +23,9 @@
// and consider replacing it. Returns empty if not replaced.
virtual absl::optional<QuicConnectionId> MaybeReplaceConnectionId(
const QuicConnectionId& original, const ParsedQuicVersion& version) = 0;
+ // Returns the length of a connection ID generated by this generator with the
+ // specified first byte.
+ virtual uint8_t ConnectionIdLength(uint8_t first_byte) const = 0;
virtual ~ConnectionIdGeneratorInterface() = default;
};
diff --git a/quiche/quic/core/deterministic_connection_id_generator.h b/quiche/quic/core/deterministic_connection_id_generator.h
index cd27811..74d42d9 100644
--- a/quiche/quic/core/deterministic_connection_id_generator.h
+++ b/quiche/quic/core/deterministic_connection_id_generator.h
@@ -27,6 +27,9 @@
absl::optional<QuicConnectionId> MaybeReplaceConnectionId(
const QuicConnectionId& original,
const ParsedQuicVersion& version) override;
+ uint8_t ConnectionIdLength(uint8_t /*first_byte*/) const override {
+ return expected_connection_id_length_;
+ }
private:
const uint8_t expected_connection_id_length_;
diff --git a/quiche/quic/core/deterministic_connection_id_generator_test.cc b/quiche/quic/core/deterministic_connection_id_generator_test.cc
index 67016b5..6c3ee21 100644
--- a/quiche/quic/core/deterministic_connection_id_generator_test.cc
+++ b/quiche/quic/core/deterministic_connection_id_generator_test.cc
@@ -118,5 +118,9 @@
}
}
+TEST_P(DeterministicConnectionIdGeneratorTest, ReturnLength) {
+ EXPECT_EQ(generator_.ConnectionIdLength(0x01), connection_id_length_);
+}
+
} // namespace test
} // namespace quic
diff --git a/quiche/quic/load_balancer/load_balancer_encoder.cc b/quiche/quic/load_balancer/load_balancer_encoder.cc
index 914b890..9e71d6c 100644
--- a/quiche/quic/load_balancer/load_balancer_encoder.cc
+++ b/quiche/quic/load_balancer/load_balancer_encoder.cc
@@ -44,9 +44,6 @@
} // namespace
-constexpr uint8_t kLoadBalancerLengthMask = 0x3f;
-constexpr uint8_t kLoadBalancerUnroutableConfigId = 0xc0;
-
absl::optional<LoadBalancerEncoder> LoadBalancerEncoder::Create(
QuicRandom &random, LoadBalancerEncoderVisitorInterface *const visitor,
const bool len_self_encoded, const uint8_t unroutable_connection_id_len) {
@@ -89,6 +86,7 @@
seed_ = absl::MakeUint128(random_.RandUint64(), random_.RandUint64()) %
NumberOfNonces(config.nonce_len());
num_nonces_left_ = NumberOfNonces(config.nonce_len());
+ connection_id_lengths_[config.config_id()] = config.total_len();
return true;
}
@@ -102,10 +100,10 @@
}
QuicConnectionId LoadBalancerEncoder::GenerateConnectionId() {
- uint8_t length = (config_.has_value()) ? config_->total_len()
- : unroutable_connection_id_len_;
- uint8_t config_id = config_.has_value() ? (config_->config_id() << 6)
+ uint8_t config_id = config_.has_value() ? config_->config_id()
: kLoadBalancerUnroutableConfigId;
+ uint8_t shifted_config_id = config_id << 6;
+ uint8_t length = connection_id_lengths_[config_id];
if (config_.has_value() != server_id_.has_value()) {
QUIC_BUG(quic_bug_435375038_04)
<< "Existence of config and server_id are out of sync";
@@ -114,12 +112,12 @@
uint8_t first_byte;
// first byte
if (len_self_encoded_) {
- first_byte = config_id | (length - 1);
+ first_byte = shifted_config_id | (length - 1);
} else {
random_.RandBytes(static_cast<void *>(&first_byte), 1);
- first_byte = config_id | (first_byte & kLoadBalancerLengthMask);
+ first_byte = shifted_config_id | (first_byte & kLoadBalancerLengthMask);
}
- if (config_id == kLoadBalancerUnroutableConfigId) {
+ if (!config_.has_value()) {
return MakeUnroutableConnectionId(first_byte);
}
QuicConnectionId id;
@@ -178,19 +176,27 @@
const QuicConnectionId &original, const ParsedQuicVersion &version) {
// Pre-IETF versions of QUIC can respond poorly to new connection IDs issued
// during the handshake.
- uint8_t needed_length = config_.has_value() ? config_->total_len()
- : unroutable_connection_id_len_;
+ uint8_t needed_length = config_.has_value()
+ ? config_->total_len()
+ : connection_id_lengths_[kNumLoadBalancerConfigs];
return (!version.HasIetfQuicFrames() && original.length() == needed_length)
? absl::optional<QuicConnectionId>()
: GenerateConnectionId();
}
+uint8_t LoadBalancerEncoder::ConnectionIdLength(uint8_t first_byte) const {
+ if (len_self_encoded()) {
+ return (first_byte &= kLoadBalancerLengthMask) + 1;
+ }
+ return connection_id_lengths_[first_byte >> 6];
+}
+
QuicConnectionId LoadBalancerEncoder::MakeUnroutableConnectionId(
uint8_t first_byte) {
QuicConnectionId id;
- id.set_length(unroutable_connection_id_len_);
+ id.set_length(connection_id_lengths_[kLoadBalancerUnroutableConfigId]);
id.mutable_data()[0] = first_byte;
- random_.RandBytes(&id.mutable_data()[1], unroutable_connection_id_len_ - 1);
+ random_.RandBytes(&id.mutable_data()[1], connection_id_lengths_[3] - 1);
return id;
}
diff --git a/quiche/quic/load_balancer/load_balancer_encoder.h b/quiche/quic/load_balancer/load_balancer_encoder.h
index 266ebc1..1099d7a 100644
--- a/quiche/quic/load_balancer/load_balancer_encoder.h
+++ b/quiche/quic/load_balancer/load_balancer_encoder.h
@@ -18,6 +18,18 @@
// Default length of a 4-tuple connection ID.
inline constexpr uint8_t kLoadBalancerUnroutableLen = 8;
+// When the encoder is self-encoding the connection ID length, these are the
+// bits of the first byte that do so.
+constexpr uint8_t kLoadBalancerLengthMask = 0x3f;
+// The bits of the connection ID first byte that encode the config ID.
+constexpr uint8_t kLoadBalancerConfigIdMask = 0xc0;
+// The config ID that means the connection ID does not contain routing
+// information.
+constexpr uint8_t kLoadBalancerUnroutableConfigId = kNumLoadBalancerConfigs;
+// The bits of the connection ID first byte that correspond to a connection ID
+// that does not contain routing information.
+constexpr uint8_t kLoadBalancerUnroutablePrefix =
+ kLoadBalancerUnroutableConfigId << 6;
// Interface which receives notifications when the current config is updated.
class QUIC_EXPORT_PRIVATE LoadBalancerEncoderVisitorInterface {
@@ -111,6 +123,7 @@
absl::optional<QuicConnectionId> MaybeReplaceConnectionId(
const QuicConnectionId& original,
const ParsedQuicVersion& version) override;
+ uint8_t ConnectionIdLength(uint8_t first_byte) const override;
protected:
LoadBalancerEncoder(QuicRandom& random,
@@ -119,8 +132,9 @@
const uint8_t unroutable_connection_id_len)
: random_(random),
len_self_encoded_(len_self_encoded),
- visitor_(visitor),
- unroutable_connection_id_len_(unroutable_connection_id_len) {}
+ visitor_(visitor) {
+ std::fill_n(connection_id_lengths_, 4, unroutable_connection_id_len);
+ }
private:
friend class test::LoadBalancerEncoderPeer;
@@ -130,11 +144,11 @@
QuicRandom& random_;
const bool len_self_encoded_;
LoadBalancerEncoderVisitorInterface* const visitor_;
- const uint8_t unroutable_connection_id_len_;
absl::optional<LoadBalancerConfig> config_;
absl::uint128 seed_, num_nonces_left_ = 0;
absl::optional<LoadBalancerServerId> server_id_;
+ uint8_t connection_id_lengths_[kNumLoadBalancerConfigs + 1];
};
} // namespace quic
diff --git a/quiche/quic/load_balancer/load_balancer_encoder_test.cc b/quiche/quic/load_balancer/load_balancer_encoder_test.cc
index a331713..12d8fc7 100644
--- a/quiche/quic/load_balancer/load_balancer_encoder_test.cc
+++ b/quiche/quic/load_balancer/load_balancer_encoder_test.cc
@@ -397,6 +397,53 @@
EXPECT_EQ(*encoder->GenerateNextConnectionId(TestConnectionId(1)), expected);
}
+TEST_F(LoadBalancerEncoderTest, ConnectionIdLengthsEncoded) {
+ // The first byte literally encodes the length.
+ auto len_encoder = LoadBalancerEncoder::Create(random_, nullptr, true);
+ ASSERT_TRUE(len_encoder.has_value());
+ EXPECT_EQ(len_encoder->ConnectionIdLength(0xc8), 9);
+ EXPECT_EQ(len_encoder->ConnectionIdLength(0x4a), 11);
+ EXPECT_EQ(len_encoder->ConnectionIdLength(0x09), 10);
+ // The length is not self-encoded anymore.
+ auto encoder = LoadBalancerEncoder::Create(random_, nullptr, false);
+ ASSERT_TRUE(encoder.has_value());
+ EXPECT_EQ(encoder->ConnectionIdLength(0xc8), kQuicDefaultConnectionIdLength);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x4a), kQuicDefaultConnectionIdLength);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x09), kQuicDefaultConnectionIdLength);
+ // Add config ID 0, so that ID now returns a different length.
+ uint8_t config_id = 0;
+ uint8_t server_id_len = 3;
+ uint8_t nonce_len = 6;
+ uint8_t config_0_len = server_id_len + nonce_len + 1;
+ auto config0 = LoadBalancerConfig::CreateUnencrypted(config_id, server_id_len,
+ nonce_len);
+ ASSERT_TRUE(config0.has_value());
+ EXPECT_TRUE(
+ encoder->UpdateConfig(*config0, MakeServerId(kServerId, server_id_len)));
+ EXPECT_EQ(encoder->ConnectionIdLength(0xc8), kQuicDefaultConnectionIdLength);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x4a), kQuicDefaultConnectionIdLength);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x09), config_0_len);
+ // Replace config ID 0 with 1. There are probably still packets with config
+ // ID 0 arriving, so keep that length in memory.
+ config_id = 1;
+ nonce_len++;
+ uint8_t config_1_len = server_id_len + nonce_len + 1;
+ auto config1 = LoadBalancerConfig::CreateUnencrypted(config_id, server_id_len,
+ nonce_len);
+ ASSERT_TRUE(config1.has_value());
+ // Old config length still there after replacement
+ EXPECT_TRUE(
+ encoder->UpdateConfig(*config1, MakeServerId(kServerId, server_id_len)));
+ EXPECT_EQ(encoder->ConnectionIdLength(0xc8), kQuicDefaultConnectionIdLength);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x4a), config_1_len);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x09), config_0_len);
+ // Old config length still there after delete
+ encoder->DeleteConfig();
+ EXPECT_EQ(encoder->ConnectionIdLength(0xc8), kQuicDefaultConnectionIdLength);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x4a), config_1_len);
+ EXPECT_EQ(encoder->ConnectionIdLength(0x09), config_0_len);
+}
+
} // namespace
} // namespace test
diff --git a/quiche/quic/test_tools/mock_connection_id_generator.h b/quiche/quic/test_tools/mock_connection_id_generator.h
index 66176f9..42209d6 100644
--- a/quiche/quic/test_tools/mock_connection_id_generator.h
+++ b/quiche/quic/test_tools/mock_connection_id_generator.h
@@ -20,6 +20,9 @@
(const quic::QuicConnectionId& original,
const quic::ParsedQuicVersion& version),
(override));
+
+ MOCK_METHOD(uint8_t, ConnectionIdLength, (uint8_t first_byte),
+ (const, override));
};
} // namespace test