google quic QuicFramer模块原理-核心成员介绍

前言

  • 最近通过对google quiche项目的简单学习,发现其代码实现十分复杂,特别是QuicFramer模块,其内部代码较多,不便于和其他模块串联在一起分析
  • QuicFramer模块可以称得上是google quiche项目中的quic报文的打包和解包引擎模块
  • QuicFramer模块负责quic报文的封包和加密工作,同时也负责对quic报文的解析和解密工作
  • 其中加密和解密QuicFramer模块是通过耦合QuicEncrypter和来实现的
  • 另外google quiche项目中大量使用了Visitor(访问者)设计模式,并提供QuicFramerVisitorInterface接口从而做到QuicFramer对其他QuicConnection模块的访问
  • 本文将简单分析QuicFramer的设计原理,为后续精度google quiche代码做铺垫

QuicFramer核心成员介绍

// Class for parsing and constructing QUIC packets.  It has a
// QuicFramerVisitorInterface that is called when packets are parsed.
class QUIC_EXPORT_PRIVATE QuicFramer {
 public:
  // Set callbacks to be called from the framer.  A visitor must be set, or
  // else the framer will likely crash.  It is acceptable for the visitor
  // to do nothing.  If this is called multiple times, only the last visitor
  // will be used.
  void set_visitor(QuicFramerVisitorInterface* visitor) { visitor_ = visitor; }
    
  void InstallDecrypter(EncryptionLevel level,
                        std::unique_ptr decrypter);    
    
  // Changes the encrypter used for level |level| to |encrypter|.
  void SetEncrypter(EncryptionLevel level,
                    std::unique_ptr encrypter);
    
  void set_data_producer(QuicStreamFrameDataProducer* data_producer) {
    data_producer_ = data_producer;
  }
    
 private:
  ...
  QuicFramerVisitorInterface* visitor_;
  // Decrypters used to decrypt packets during parsing.
  std::unique_ptr decrypter_[NUM_ENCRYPTION_LEVELS];
  // Encrypters used to encrypt packets via EncryptPayload().
  std::unique_ptr encrypter_[NUM_ENCRYPTION_LEVELS];
  // If not null, framer asks data_producer_ to write stream frame data. Not
  // owned. TODO(fayang): Consider add data producer to framer's constructor.
  QuicStreamFrameDataProducer* data_producer_;
};
  • QuicConnection作为QuicFramerVisitorInterface接口的子类,在QuicConnection的构造函数中通过调用framer_.set_visitor(this),使得QuicFramer模块通过其成员visitor_成为QuicConnection的访问者
  • decrypter_成员负责对quic报文进行解密工作
  • encrypter_成员负责对quic报文加密工作
  • data_producer_顾名思义是数据消费者API,QuicSession派生QuicStreamFrameDataProducer接口,QuicFramer作为quic packet打包引擎,封装完头部信息后回通过QuicStreamFrameDataProducer接口bypass到QuicSession进行消费(组装payload,然后给到QuicWriter模块进行发送处理)
  • 接下来我们简单分析以上核心成员的初始化流程

QuicFramer中Initial阶段加解密擎初始化

  • 其中Initial包使用的解密引擎初始化如下:
QuicConnection::QuicConnection(
    QuicConnectionId server_connection_id,
    QuicSocketAddress initial_self_address,
    QuicSocketAddress initial_peer_address,
    QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
    QuicPacketWriter* writer, bool owns_writer, Perspective perspective,
    const ParsedQuicVersionVector& supported_versions,
    ConnectionIdGeneratorInterface& generator)
    : .. {
  ...
  InstallInitialCrypters(default_path_.server_connection_id);
  ...
}
void QuicConnection::InstallInitialCrypters(QuicConnectionId connection_id) {
  CrypterPair crypters;
  CryptoUtils::CreateInitialObfuscators(perspective_, version(), connection_id,
                                        &crypters);
  SetEncrypter(ENCRYPTION_INITIAL, std::move(crypters.encrypter));
  if (version().KnowsWhichDecrypterToUse()) {
    InstallDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter));
  } else {
    SetDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter));
  }
}
  • Initial阶段,统一使用CryptoUtils::CreateInitialObfuscators创建相同算法的加解密引擎
  • 并通过InstallDecrypter和SetEncrypterQuicFramer中对应Level的加解密引擎进行初始化
  • 这样服务端和客户端在发送和接收Initial报文的时候使用该加解密引擎进行加解密

QuicFramer中QuicDecrypter非Initial解密引擎初始化

  • Initial阶段解密引擎的函数调用堆栈如下:
    002.png
  • 上述堆栈为quic服务端的堆栈,当服务端收到客户端的Initial报文的时候会提取client hello信息并将其输入到Boring ssl引擎,之后进行握手工作(生成handshake包的时候),在握手过程中会触发其SSL_QUIC_METHOD结构中的set_read_secret函数指针,最终在TlsHandshaker::SetReadSecret()中创建该解密引擎
bool TlsHandshaker::SetReadSecret(EncryptionLevel level,
                                  const SSL_CIPHER* cipher,
                                  absl::Span read_secret) {
  std::unique_ptr decrypter =
      QuicDecrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
  // 秘钥相关设置...
  const EVP_MD* prf = Prf(cipher);
  CryptoUtils::SetKeyAndIV(prf, read_secret,
                           handshaker_delegate_->parsed_version(),
                           decrypter.get());
  std::vector header_protection_key =
      CryptoUtils::GenerateHeaderProtectionKey(
          prf, read_secret, handshaker_delegate_->parsed_version(),
          decrypter->GetKeySize());
  //设置头部加密秘钥信息
  decrypter->SetHeaderProtectionKey(
      absl::string_view(reinterpret_cast(header_protection_key.data()),
                        header_protection_key.size()));
  if (level == ENCRYPTION_FORWARD_SECURE) {
    QUICHE_DCHECK(latest_read_secret_.empty());
    latest_read_secret_.assign(read_secret.begin(), read_secret.end());
    one_rtt_read_header_protection_key_ = header_protection_key;
  }
  return handshaker_delegate_->OnNewDecryptionKeyAvailable(
      level, std::move(decrypter),
      /*set_alternative_decrypter=*/false,
      /*latch_once_used=*/false);
}
  • 梳理流程如下:


    001.png
  • QuicFramer解析收到的报文后通过QuicDecrypter::DecryptPacket(...)即可完成解密工作

QuicFramer中QuicEncrypter非Initial加密引擎初始化

  • encrypter_成员的初始化和decrypter_成员的函数调用堆栈差不多,都是发生在handshake握手阶段,QuicEncrypter模块的创建是发生在TlsHandshaker::SetWriteSecret当中,其实现如下:
void TlsHandshaker::SetWriteSecret(EncryptionLevel level,
                                   const SSL_CIPHER* cipher,
                                   absl::Span write_secret) {
  std::unique_ptr encrypter =
      QuicEncrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
  const EVP_MD* prf = Prf(cipher);
  CryptoUtils::SetKeyAndIV(prf, write_secret,
                           handshaker_delegate_->parsed_version(),
                           encrypter.get());
  std::vector header_protection_key =
      CryptoUtils::GenerateHeaderProtectionKey(
          prf, write_secret, handshaker_delegate_->parsed_version(),
          encrypter->GetKeySize());
  encrypter->SetHeaderProtectionKey(
      absl::string_view(reinterpret_cast(header_protection_key.data()),
                        header_protection_key.size()));
  if (level == ENCRYPTION_FORWARD_SECURE) {
    QUICHE_DCHECK(latest_write_secret_.empty());
    latest_write_secret_.assign(write_secret.begin(), write_secret.end());
    one_rtt_write_header_protection_key_ = header_protection_key;
  }
  handshaker_delegate_->OnNewEncryptionKeyAvailable(level,
                                                    std::move(encrypter));
}
  • 整理函数调用流程如下:


    003.png
  • QuicFramer封装好报文后通过QuicFramer::EncryptPacket(...)即可完成加密工作

QuicFramer中QuicStreamFrameDataProducer初始化

void QuicSession::Initialize() {
  .....
  connection_->SetDataProducer(this);
  ...
}
void QuicConnection::SetDataProducer(
    QuicStreamFrameDataProducer* data_producer) {
  framer_.set_data_producer(data_producer);
}
  • 以此可以看出QuicSessionQuicFramer模块数据封包后的数据消费者

QuicFramer中QuicFramerVisitorInterface初始化

QuicConnection::QuicConnection(
    QuicConnectionId server_connection_id,
    QuicSocketAddress initial_self_address,
    QuicSocketAddress initial_peer_address,
    QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
    QuicPacketWriter* writer, bool owns_writer, Perspective perspective,
    const ParsedQuicVersionVector& supported_versions,
    ConnectionIdGeneratorInterface& generator)
    : framer_(supported_versions, helper->GetClock()->ApproximateNow(),
              perspective, server_connection_id.length()),
      .. {
 
  framer_.set_visitor(this);
 ...
}

  • QuicFramer作为QuicConnection模块的成员变量,在QuicConnection构造函数中会实例化QuicFramer,并且会把自己作为QuicFramerVisitorInterface的子类设置到QuicFramer模块中
  • QuicFramer模块对收到的quic 报文处理完后可以通过该接口访问QuicConnection模块

QuicFramer 公共Api介绍

class QUIC_EXPORT_PRIVATE QuicFramer {
 public:
  // Serializes a packet containing |frames| into |buffer|.
  // Returns the length of the packet, which must not be longer than
  // |packet_length|.  Returns 0 if it fails to serialize.
  size_t BuildDataPacket(const QuicPacketHeader& header,
                         const QuicFrames& frames, char* buffer,
                         size_t packet_length, EncryptionLevel level);
  // Pass a UDP packet into the framer for parsing.
  // Return true if the packet was processed successfully. |packet| must be a
  // single, complete UDP packet (not a frame of a packet).  This packet
  // might be null padded past the end of the payload, which will be correctly
  // ignored.
  bool ProcessPacket(const QuicEncryptedPacket& packet);    
  // Encrypts a payload in |buffer|.  |ad_len| is the length of the associated
  // data. |total_len| is the length of the associated data plus plaintext.
  // |buffer_len| is the full length of the allocated buffer.
  size_t EncryptInPlace(EncryptionLevel level, QuicPacketNumber packet_number,
                        size_t ad_len, size_t total_len, size_t buffer_len,
                        char* buffer);

  // Returns the length of the data encrypted into |buffer| if |buffer_len| is
  // long enough, and otherwise 0.
  size_t EncryptPayload(EncryptionLevel level, QuicPacketNumber packet_number,
                        const QuicPacket& packet, char* buffer,
                        size_t buffer_len);
};
  • BuildDataPacket()函数作为封包函数的入口,当需要封装quic报文的时候调用该函数
  • ProcessPacket()函数负责解析收到的quic报文
  • EncryptPayload()函数负责给payload加密处理

总结:

  • 本文从初始化流程阐述QuicFramer的核心成员和其工作原理
  • QuicFramer对其他模块提供了大量的Api,主要包括加密、解密、封装报文、解析报文等核心函数
  • 同时通过访问者设计模式,在QuicFramer模块解析数据包后,在不同的阶段会通过visitor_成员访问QuicConnection模块从而进行相应的业务处理

参考文献:

Visitor(访问者)设计模式

你可能感兴趣的:(google quic QuicFramer模块原理-核心成员介绍)