| // Copyright (c) 2022 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #ifndef QUICHE_QUIC_LOAD_BALANCER_LOAD_BALANCER_ENCODER_H_ | 
 | #define QUICHE_QUIC_LOAD_BALANCER_LOAD_BALANCER_ENCODER_H_ | 
 |  | 
 | #include "quiche/quic/core/connection_id_generator.h" | 
 | #include "quiche/quic/core/crypto/quic_random.h" | 
 | #include "quiche/quic/load_balancer/load_balancer_config.h" | 
 | #include "quiche/quic/load_balancer/load_balancer_server_id.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | namespace test { | 
 | class LoadBalancerEncoderPeer; | 
 | } | 
 |  | 
 | // 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 { | 
 |  public: | 
 |   virtual ~LoadBalancerEncoderVisitorInterface() {} | 
 |  | 
 |   // Called when a config is added where none existed. | 
 |   // | 
 |   // Connections that support address migration should retire unroutable | 
 |   // connection IDs and replace them with routable ones using the new config, | 
 |   // while avoiding sending a sudden storm of packets containing | 
 |   // RETIRE_CONNECTION_ID and NEW_CONNECTION_ID frames. | 
 |   virtual void OnConfigAdded(const uint8_t config_id) = 0; | 
 |   // Called when the config is changed. | 
 |   // | 
 |   // Existing routable connection IDs should be retired before the decoder stops | 
 |   // supporting that config. The timing of this event is deployment-dependent | 
 |   // and might be tied to the arrival of a new config at the encoder. | 
 |   virtual void OnConfigChanged(const uint8_t old_config_id, | 
 |                                const uint8_t new_config_id) = 0; | 
 |   // Called when a config is deleted. The encoder will generate unroutable | 
 |   // connection IDs from now on. | 
 |   // | 
 |   // New connections will not be able to support address migration until a new | 
 |   // config arrives. Existing connections can retain connection IDs that use the | 
 |   // deleted config, which will only become unroutable once the decoder also | 
 |   // deletes it. The time of that deletion is deployment-dependent and might be | 
 |   // tied to the arrival of a new config at the encoder. | 
 |   virtual void OnConfigDeleted(const uint8_t config_id) = 0; | 
 | }; | 
 |  | 
 | // Manages QUIC-LB configurations to properly encode a given server ID in a | 
 | // QUIC Connection ID. | 
 | class QUIC_EXPORT_PRIVATE LoadBalancerEncoder | 
 |     : public ConnectionIdGeneratorInterface { | 
 |  public: | 
 |   LoadBalancerEncoder(QuicRandom& random, | 
 |                       LoadBalancerEncoderVisitorInterface* const visitor, | 
 |                       const bool len_self_encoded) | 
 |       : LoadBalancerEncoder(random, visitor, len_self_encoded, | 
 |                             kLoadBalancerUnroutableLen) {} | 
 |   ~LoadBalancerEncoder() override {} | 
 |  | 
 |   // Returns a newly created encoder with no active config, if | 
 |   // |unroutable_connection_id_length| is valid. |visitor| specifies an optional | 
 |   // interface to receive callbacks when config status changes. | 
 |   // If |len_self_encoded| is true, then the first byte of any generated | 
 |   // connection ids will encode the length. Otherwise, those bits will be | 
 |   // random. |unroutable_connection_id_length| specifies the length of | 
 |   // connection IDs to be generated when there is no active config. It must not | 
 |   // be 0 and must not be larger than the RFC9000 maximum of 20. | 
 |   static absl::optional<LoadBalancerEncoder> Create( | 
 |       QuicRandom& random, LoadBalancerEncoderVisitorInterface* const visitor, | 
 |       const bool len_self_encoded, | 
 |       const uint8_t unroutable_connection_id_len = kLoadBalancerUnroutableLen); | 
 |  | 
 |   // Attempts to replace the current config and server_id with |config| and | 
 |   // |server_id|. If the length |server_id| does not match the server_id_length | 
 |   // of |config| or the ID of |config| matches the ID of the current config, | 
 |   // returns false and leaves the current config unchanged. Otherwise, returns | 
 |   // true. When the encoder runs out of nonces, it will delete the config and | 
 |   // begin generating unroutable connection IDs. | 
 |   bool UpdateConfig(const LoadBalancerConfig& config, | 
 |                     const LoadBalancerServerId server_id); | 
 |  | 
 |   // Delete the current config and generate unroutable connection IDs from now | 
 |   // on. | 
 |   void DeleteConfig(); | 
 |  | 
 |   // Returns the number of additional connection IDs that can be generated with | 
 |   // the current config, or 0 if there is no current config. | 
 |   absl::uint128 num_nonces_left() const { return num_nonces_left_; } | 
 |  | 
 |   // Functions below are declared virtual to enable mocking. | 
 |   // Returns true if there is an active configuration. | 
 |   virtual bool IsEncoding() const { return config_.has_value(); } | 
 |   // Returns true if there is an active configuration that uses encryption. | 
 |   virtual bool IsEncrypted() const { | 
 |     return config_.has_value() && config_->IsEncrypted(); | 
 |   } | 
 |   virtual bool len_self_encoded() const { return len_self_encoded_; } | 
 |  | 
 |   // If there's an active config, generates a connection ID using it. If not, | 
 |   // generates an unroutable connection_id. If there's an error, returns a zero- | 
 |   // length Connection ID. | 
 |   QuicConnectionId GenerateConnectionId(); | 
 |  | 
 |   // Functions from ConnectionIdGeneratorInterface | 
 |   absl::optional<QuicConnectionId> GenerateNextConnectionId( | 
 |       const QuicConnectionId& original) override; | 
 |   absl::optional<QuicConnectionId> MaybeReplaceConnectionId( | 
 |       const QuicConnectionId& original, | 
 |       const ParsedQuicVersion& version) override; | 
 |   uint8_t ConnectionIdLength(uint8_t first_byte) const override; | 
 |  | 
 |  protected: | 
 |   LoadBalancerEncoder(QuicRandom& random, | 
 |                       LoadBalancerEncoderVisitorInterface* const visitor, | 
 |                       const bool len_self_encoded, | 
 |                       const uint8_t unroutable_connection_id_len) | 
 |       : random_(random), | 
 |         len_self_encoded_(len_self_encoded), | 
 |         visitor_(visitor) { | 
 |     std::fill_n(connection_id_lengths_, 4, unroutable_connection_id_len); | 
 |   } | 
 |  | 
 |  private: | 
 |   friend class test::LoadBalancerEncoderPeer; | 
 |  | 
 |   QuicConnectionId MakeUnroutableConnectionId(uint8_t first_byte); | 
 |  | 
 |   QuicRandom& random_; | 
 |   const bool len_self_encoded_; | 
 |   LoadBalancerEncoderVisitorInterface* const visitor_; | 
 |  | 
 |   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 | 
 |  | 
 | #endif  // QUICHE_QUIC_LOAD_BALANCER_LOAD_BALANCER_ENCODER_H_ |