mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-04-18 17:24:03 +03:00
Summary: - as title Reviewed By: lnicco Differential Revision: D33513410 fbshipit-source-id: 282b6f512cf83b9abb7990402661135b658f7bd1
553 lines
16 KiB
C++
553 lines
16 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <folly/Portability.h>
|
|
#include <quic/codec/PacketNumber.h>
|
|
#include <quic/codec/QuicInteger.h>
|
|
#include <quic/codec/Types.h>
|
|
#include <quic/common/BufAccessor.h>
|
|
#include <quic/common/BufUtil.h>
|
|
#include <quic/handshake/HandshakeLayer.h>
|
|
|
|
namespace quic {
|
|
|
|
// maximum length of packet length.
|
|
constexpr auto kMaxPacketLenSize = sizeof(uint16_t);
|
|
|
|
// We reserve 2 bytes for packet length in the long headers
|
|
constexpr auto kReservedPacketLenSize = sizeof(uint16_t);
|
|
|
|
// We reserve 4 bytes for packet number in the long headers.
|
|
constexpr auto kReservedPacketNumSize = kMaxPacketNumEncodingSize;
|
|
|
|
// Note a full PacketNum has 64 bits, but LongHeader only uses 32 bits of them
|
|
// This is based on Draft-22
|
|
constexpr auto kLongHeaderHeaderSize = sizeof(uint8_t) /* Type bytes */ +
|
|
sizeof(QuicVersionType) /* Version */ +
|
|
2 * sizeof(uint8_t) /* DCIL + SCIL */ +
|
|
kDefaultConnectionIdSize * 2 /* 2 connection IDs */ +
|
|
kReservedPacketLenSize /* minimal size of length */ +
|
|
kReservedPacketNumSize /* packet number */;
|
|
|
|
// Appender growth byte size for in PacketBuilder:
|
|
constexpr size_t kAppenderGrowthSize = 100;
|
|
|
|
class PacketBuilderInterface {
|
|
public:
|
|
virtual ~PacketBuilderInterface() = default;
|
|
|
|
// TODO: Temporarly let this interface be reusable across different concrete
|
|
// builder types. But this isn't optimized for builder that writes both header
|
|
// and body into a continuous memory.
|
|
struct Packet {
|
|
RegularQuicWritePacket packet;
|
|
Buf header;
|
|
Buf body;
|
|
|
|
Packet(RegularQuicWritePacket packetIn, Buf headerIn, Buf bodyIn)
|
|
: packet(std::move(packetIn)),
|
|
header(std::move(headerIn)),
|
|
body(std::move(bodyIn)) {}
|
|
};
|
|
|
|
FOLLY_NODISCARD virtual uint32_t remainingSpaceInPkt() const = 0;
|
|
|
|
virtual void encodePacketHeader() = 0;
|
|
|
|
// Functions to write bytes to the packet
|
|
virtual void writeBE(uint8_t data) = 0;
|
|
virtual void writeBE(uint16_t data) = 0;
|
|
virtual void writeBE(uint64_t data) = 0;
|
|
virtual void write(const QuicInteger& quicInteger) = 0;
|
|
virtual void appendBytes(PacketNum value, uint8_t byteNumber) = 0;
|
|
virtual void
|
|
appendBytes(BufAppender& appender, PacketNum value, uint8_t byteNumber) = 0;
|
|
virtual void
|
|
appendBytes(BufWriter& writer, PacketNum value, uint8_t byteNumber) = 0;
|
|
virtual void insert(std::unique_ptr<folly::IOBuf> buf) = 0;
|
|
virtual void insert(std::unique_ptr<folly::IOBuf> buf, size_t limit) = 0;
|
|
virtual void insert(const BufQueue& buf, size_t limit) = 0;
|
|
virtual void push(const uint8_t* data, size_t len) = 0;
|
|
|
|
// Append a frame to the packet.
|
|
virtual void appendFrame(QuicWriteFrame frame) = 0;
|
|
|
|
// Returns the packet header for the current packet.
|
|
FOLLY_NODISCARD virtual const PacketHeader& getPacketHeader() const = 0;
|
|
|
|
virtual void accountForCipherOverhead(uint8_t overhead) = 0;
|
|
|
|
/**
|
|
* Whether the packet builder is able to build a packet. This should be
|
|
* checked right after the creation of a packet builder object.
|
|
*/
|
|
FOLLY_NODISCARD virtual bool canBuildPacket() const noexcept = 0;
|
|
|
|
/**
|
|
* Return an estimated header bytes count.
|
|
*
|
|
* For short header, this is the exact header bytes. For long header, since
|
|
* the writing of packet length and packet number field are deferred to the
|
|
* buildPacket() call, this is an estimate header bytes count that's the sum
|
|
* of header bytes already written, the maximum possible packet length field
|
|
* bytes count and packet number field bytes count.
|
|
*/
|
|
FOLLY_NODISCARD virtual uint32_t getHeaderBytes() const = 0;
|
|
|
|
FOLLY_NODISCARD virtual bool hasFramesPending() const = 0;
|
|
|
|
virtual Packet buildPacket() && = 0;
|
|
|
|
virtual void releaseOutputBuffer() && = 0;
|
|
};
|
|
|
|
/**
|
|
* Build packet into user provided IOBuf
|
|
*/
|
|
class InplaceQuicPacketBuilder final : public PacketBuilderInterface {
|
|
public:
|
|
~InplaceQuicPacketBuilder() override;
|
|
|
|
explicit InplaceQuicPacketBuilder(
|
|
BufAccessor& bufAccessor,
|
|
uint32_t remainingBytes,
|
|
PacketHeader header,
|
|
PacketNum largestAckedPacketNum);
|
|
|
|
// PacketBuilderInterface
|
|
FOLLY_NODISCARD uint32_t remainingSpaceInPkt() const override;
|
|
|
|
void encodePacketHeader() override;
|
|
|
|
void writeBE(uint8_t data) override;
|
|
void writeBE(uint16_t data) override;
|
|
void writeBE(uint64_t data) override;
|
|
void write(const QuicInteger& quicInteger) override;
|
|
void appendBytes(PacketNum value, uint8_t byteNumber) override;
|
|
void appendBytes(BufAppender&, PacketNum, uint8_t) override {
|
|
CHECK(false) << "Invalid appender";
|
|
}
|
|
void appendBytes(BufWriter& writer, PacketNum value, uint8_t byteNumber)
|
|
override;
|
|
void insert(std::unique_ptr<folly::IOBuf> buf) override;
|
|
void insert(std::unique_ptr<folly::IOBuf> buf, size_t limit) override;
|
|
void insert(const BufQueue& buf, size_t limit) override;
|
|
void push(const uint8_t* data, size_t len) override;
|
|
|
|
void appendFrame(QuicWriteFrame frame) override;
|
|
FOLLY_NODISCARD const PacketHeader& getPacketHeader() const override;
|
|
|
|
PacketBuilderInterface::Packet buildPacket() && override;
|
|
|
|
FOLLY_NODISCARD bool canBuildPacket() const noexcept override;
|
|
|
|
void accountForCipherOverhead(uint8_t overhead) noexcept override;
|
|
|
|
FOLLY_NODISCARD uint32_t getHeaderBytes() const override;
|
|
|
|
FOLLY_NODISCARD bool hasFramesPending() const override;
|
|
|
|
void releaseOutputBuffer() && override;
|
|
|
|
private:
|
|
void releaseOutputBufferInternal();
|
|
|
|
private:
|
|
BufAccessor& bufAccessor_;
|
|
Buf iobuf_;
|
|
BufWriter bufWriter_;
|
|
uint32_t remainingBytes_;
|
|
PacketNum largestAckedPacketNum_;
|
|
RegularQuicWritePacket packet_;
|
|
uint32_t cipherOverhead_{0};
|
|
folly::Optional<PacketNumEncodingResult> packetNumberEncoding_;
|
|
// The offset in the IOBuf writable area to write Packet Length.
|
|
size_t packetLenOffset_{0};
|
|
// The offset in the IOBuf writable area to write Packet Number.
|
|
size_t packetNumOffset_{0};
|
|
// The position to write body.
|
|
const uint8_t* bodyStart_{nullptr};
|
|
// The position to write header.
|
|
const uint8_t* headerStart_{nullptr};
|
|
};
|
|
|
|
/**
|
|
* Build packet into IOBufs created by Builder
|
|
*/
|
|
class RegularQuicPacketBuilder final : public PacketBuilderInterface {
|
|
public:
|
|
~RegularQuicPacketBuilder() override = default;
|
|
|
|
RegularQuicPacketBuilder(RegularQuicPacketBuilder&&) = default;
|
|
|
|
using Packet = PacketBuilderInterface::Packet;
|
|
|
|
RegularQuicPacketBuilder(
|
|
uint32_t remainingBytes,
|
|
PacketHeader header,
|
|
PacketNum largestAckedPacketNum);
|
|
|
|
FOLLY_NODISCARD uint32_t getHeaderBytes() const override;
|
|
|
|
void encodePacketHeader() override;
|
|
|
|
// PacketBuilderInterface
|
|
FOLLY_NODISCARD uint32_t remainingSpaceInPkt() const override;
|
|
|
|
void writeBE(uint8_t data) override;
|
|
void writeBE(uint16_t data) override;
|
|
void writeBE(uint64_t data) override;
|
|
void write(const QuicInteger& quicInteger) override;
|
|
void appendBytes(PacketNum value, uint8_t byteNumber) override;
|
|
void appendBytes(BufAppender& appender, PacketNum value, uint8_t byteNumber)
|
|
override;
|
|
void appendBytes(BufWriter&, PacketNum, uint8_t) override {
|
|
CHECK(false) << "Invalid BufWriter";
|
|
}
|
|
void insert(std::unique_ptr<folly::IOBuf> buf) override;
|
|
void insert(std::unique_ptr<folly::IOBuf> buf, size_t limit) override;
|
|
void insert(const BufQueue& buf, size_t limit) override;
|
|
|
|
void push(const uint8_t* data, size_t len) override;
|
|
|
|
void appendFrame(QuicWriteFrame frame) override;
|
|
FOLLY_NODISCARD const PacketHeader& getPacketHeader() const override;
|
|
|
|
Packet buildPacket() && override;
|
|
/**
|
|
* Whether the packet builder is able to build a packet. This should be
|
|
* checked right after the creation of a packet builder object.
|
|
*/
|
|
FOLLY_NODISCARD bool canBuildPacket() const noexcept override;
|
|
|
|
void accountForCipherOverhead(uint8_t overhead) noexcept override;
|
|
|
|
FOLLY_NODISCARD bool hasFramesPending() const override;
|
|
|
|
void releaseOutputBuffer() && override;
|
|
|
|
private:
|
|
void encodeLongHeader(
|
|
const LongHeader& longHeader,
|
|
PacketNum largestAckedPacketNum);
|
|
void encodeShortHeader(
|
|
const ShortHeader& shortHeader,
|
|
PacketNum largestAckedPacketNum);
|
|
|
|
private:
|
|
uint32_t remainingBytes_;
|
|
PacketNum largestAckedPacketNum_;
|
|
RegularQuicWritePacket packet_;
|
|
std::unique_ptr<folly::IOBuf> header_;
|
|
std::unique_ptr<folly::IOBuf> body_;
|
|
BufAppender headerAppender_;
|
|
BufAppender bodyAppender_;
|
|
|
|
uint32_t cipherOverhead_{0};
|
|
folly::Optional<PacketNumEncodingResult> packetNumberEncoding_;
|
|
};
|
|
|
|
/**
|
|
* A less involving interface for packet builder, this enables polymorphism
|
|
* for wrapper-like packet builders (e.g. RegularSizeEnforcedPacketBuilder).
|
|
*/
|
|
class WrapperPacketBuilderInterface {
|
|
public:
|
|
using Packet = PacketBuilderInterface::Packet;
|
|
|
|
virtual ~WrapperPacketBuilderInterface() = default;
|
|
|
|
FOLLY_NODISCARD virtual bool canBuildPacket() const noexcept = 0;
|
|
|
|
virtual Packet buildPacket() && = 0;
|
|
};
|
|
|
|
/**
|
|
* This builder will enforce the packet size by appending padding frames for
|
|
* chained memory. This means appending IOBuf at the end of the chain. The
|
|
* caller should ensure canBuildPacket() returns true before constructing the
|
|
* builder.
|
|
*/
|
|
class RegularSizeEnforcedPacketBuilder : public WrapperPacketBuilderInterface {
|
|
public:
|
|
using Packet = PacketBuilderInterface::Packet;
|
|
|
|
explicit RegularSizeEnforcedPacketBuilder(
|
|
Packet packet,
|
|
uint64_t enforcedSize,
|
|
uint32_t cipherOverhead);
|
|
|
|
/**
|
|
* Returns true when packet has short header, and that enforced size >
|
|
* current packet size + cipher overhead, otherwise false
|
|
*/
|
|
FOLLY_NODISCARD bool canBuildPacket() const noexcept override;
|
|
|
|
Packet buildPacket() && override;
|
|
|
|
private:
|
|
RegularQuicWritePacket packet_;
|
|
Buf header_;
|
|
Buf body_;
|
|
BufAppender bodyAppender_;
|
|
uint64_t enforcedSize_;
|
|
uint32_t cipherOverhead_;
|
|
};
|
|
|
|
/**
|
|
* This builder will enforce the packet size by appending padding frames for
|
|
* continuous memory. This means pushing padding frame directly to the current
|
|
* tail offset. The caller should ensure canBuildPacket() returns true before
|
|
* constructing the builder.
|
|
*/
|
|
class InplaceSizeEnforcedPacketBuilder : public WrapperPacketBuilderInterface {
|
|
public:
|
|
using Packet = PacketBuilderInterface::Packet;
|
|
|
|
explicit InplaceSizeEnforcedPacketBuilder(
|
|
BufAccessor& bufAccessor,
|
|
Packet packet,
|
|
uint64_t enforcedSize,
|
|
uint32_t cipherOverhead);
|
|
|
|
/**
|
|
* Returns true when packet has short header, and that enforced size> current
|
|
* packet size + cipher oveahead and that iobuf has enough tailroom,
|
|
* otherwise false
|
|
*/
|
|
FOLLY_NODISCARD bool canBuildPacket() const noexcept override;
|
|
|
|
Packet buildPacket() && override;
|
|
|
|
private:
|
|
BufAccessor& bufAccessor_;
|
|
Buf iobuf_;
|
|
RegularQuicWritePacket packet_;
|
|
Buf header_;
|
|
Buf body_;
|
|
uint64_t enforcedSize_;
|
|
uint32_t cipherOverhead_;
|
|
};
|
|
|
|
class VersionNegotiationPacketBuilder {
|
|
public:
|
|
explicit VersionNegotiationPacketBuilder(
|
|
ConnectionId sourceConnectionId,
|
|
ConnectionId destinationConnectionId,
|
|
const std::vector<QuicVersion>& versions);
|
|
|
|
virtual ~VersionNegotiationPacketBuilder() = default;
|
|
|
|
uint32_t remainingSpaceInPkt();
|
|
std::pair<VersionNegotiationPacket, Buf> buildPacket() &&;
|
|
/**
|
|
* Whether the packet builder is able to build a packet. This should be
|
|
* checked right after the creation of a packet builder object.
|
|
*/
|
|
FOLLY_NODISCARD bool canBuildPacket() const noexcept;
|
|
|
|
private:
|
|
void writeVersionNegotiationPacket(const std::vector<QuicVersion>& versions);
|
|
|
|
FOLLY_NODISCARD uint8_t generateRandomPacketType() const;
|
|
|
|
private:
|
|
uint32_t remainingBytes_;
|
|
VersionNegotiationPacket packet_;
|
|
std::unique_ptr<folly::IOBuf> data_;
|
|
};
|
|
|
|
/*
|
|
* Used to construct a pseudo-retry packet, as described in the QUIC-TLS
|
|
* draft 29.
|
|
*/
|
|
class PseudoRetryPacketBuilder {
|
|
public:
|
|
PseudoRetryPacketBuilder(
|
|
uint8_t initialByte,
|
|
ConnectionId sourceConnectionId,
|
|
ConnectionId destinationConnectionId,
|
|
ConnectionId originalDestinationConnectionId,
|
|
QuicVersion quicVersion,
|
|
Buf&& token);
|
|
|
|
Buf buildPacket() &&;
|
|
|
|
private:
|
|
void writePseudoRetryPacket();
|
|
|
|
Buf packetBuf_;
|
|
|
|
uint8_t initialByte_;
|
|
ConnectionId sourceConnectionId_;
|
|
ConnectionId destinationConnectionId_;
|
|
ConnectionId originalDestinationConnectionId_;
|
|
QuicVersion quicVersion_;
|
|
Buf token_;
|
|
};
|
|
|
|
class RetryPacketBuilder {
|
|
public:
|
|
RetryPacketBuilder(
|
|
ConnectionId sourceConnectionId,
|
|
ConnectionId destinationConnectionId,
|
|
QuicVersion quicVersion,
|
|
std::string&& retryToken,
|
|
Buf&& integrityTag);
|
|
|
|
uint32_t remainingSpaceInPkt();
|
|
|
|
Buf buildPacket() &&;
|
|
|
|
/**
|
|
* Whether the RetryPacketBuilder is able to build a packet. This should be
|
|
* checked right after the creation of the RetryPacketBuilder.
|
|
*/
|
|
FOLLY_NODISCARD bool canBuildPacket() const noexcept;
|
|
|
|
private:
|
|
void writeRetryPacket();
|
|
|
|
Buf packetBuf_;
|
|
|
|
ConnectionId sourceConnectionId_;
|
|
ConnectionId destinationConnectionId_;
|
|
QuicVersion quicVersion_;
|
|
std::string retryToken_;
|
|
Buf integrityTag_;
|
|
|
|
uint32_t remainingBytes_;
|
|
};
|
|
|
|
class StatelessResetPacketBuilder {
|
|
public:
|
|
StatelessResetPacketBuilder(
|
|
uint16_t maxPacketSize,
|
|
const StatelessResetToken& resetToken);
|
|
|
|
Buf buildPacket() &&;
|
|
|
|
private:
|
|
std::unique_ptr<folly::IOBuf> data_;
|
|
};
|
|
|
|
/**
|
|
* A PacketBuilder that wraps in another PacketBuilder that may have a different
|
|
* writableBytes limit. The minimum between the limit will be used to limit the
|
|
* packet it can build.
|
|
*/
|
|
class PacketBuilderWrapper : public PacketBuilderInterface {
|
|
public:
|
|
~PacketBuilderWrapper() override = default;
|
|
|
|
PacketBuilderWrapper(
|
|
PacketBuilderInterface& builderIn,
|
|
uint32_t writableBytes)
|
|
: builder(builderIn),
|
|
diff(
|
|
writableBytes > builder.remainingSpaceInPkt()
|
|
? 0
|
|
: builder.remainingSpaceInPkt() - writableBytes) {}
|
|
|
|
FOLLY_NODISCARD uint32_t remainingSpaceInPkt() const override {
|
|
return builder.remainingSpaceInPkt() > diff
|
|
? builder.remainingSpaceInPkt() - diff
|
|
: 0;
|
|
}
|
|
|
|
void encodePacketHeader() override {
|
|
CHECK(false)
|
|
<< "We only support wrapping builder that has already encoded header";
|
|
}
|
|
|
|
void write(const QuicInteger& quicInteger) override {
|
|
builder.write(quicInteger);
|
|
}
|
|
|
|
void writeBE(uint8_t value) override {
|
|
builder.writeBE(value);
|
|
}
|
|
|
|
void writeBE(uint16_t value) override {
|
|
builder.writeBE(value);
|
|
}
|
|
|
|
void writeBE(uint64_t value) override {
|
|
builder.writeBE(value);
|
|
}
|
|
|
|
void appendBytes(PacketNum value, uint8_t byteNumber) override {
|
|
builder.appendBytes(value, byteNumber);
|
|
}
|
|
|
|
void appendBytes(BufAppender& appender, PacketNum value, uint8_t byteNumber)
|
|
override {
|
|
builder.appendBytes(appender, value, byteNumber);
|
|
}
|
|
|
|
void appendBytes(BufWriter& writer, PacketNum value, uint8_t byteNumber)
|
|
override {
|
|
builder.appendBytes(writer, value, byteNumber);
|
|
}
|
|
|
|
void insert(std::unique_ptr<folly::IOBuf> buf) override {
|
|
builder.insert(std::move(buf));
|
|
}
|
|
|
|
void insert(std::unique_ptr<folly::IOBuf> buf, size_t limit) override {
|
|
builder.insert(std::move(buf), limit);
|
|
}
|
|
|
|
void insert(const BufQueue& buf, size_t limit) override {
|
|
builder.insert(buf, limit);
|
|
}
|
|
|
|
void appendFrame(QuicWriteFrame frame) override {
|
|
builder.appendFrame(std::move(frame));
|
|
}
|
|
|
|
void push(const uint8_t* data, size_t len) override {
|
|
builder.push(data, len);
|
|
}
|
|
|
|
FOLLY_NODISCARD const PacketHeader& getPacketHeader() const override {
|
|
return builder.getPacketHeader();
|
|
}
|
|
|
|
PacketBuilderInterface::Packet buildPacket() && override {
|
|
return std::move(builder).buildPacket();
|
|
}
|
|
|
|
void accountForCipherOverhead(uint8_t overhead) noexcept override {
|
|
builder.accountForCipherOverhead(overhead);
|
|
}
|
|
|
|
FOLLY_NODISCARD bool canBuildPacket() const noexcept override {
|
|
return builder.canBuildPacket();
|
|
}
|
|
|
|
FOLLY_NODISCARD uint32_t getHeaderBytes() const override {
|
|
return builder.getHeaderBytes();
|
|
}
|
|
|
|
FOLLY_NODISCARD bool hasFramesPending() const override {
|
|
return builder.hasFramesPending();
|
|
}
|
|
|
|
void releaseOutputBuffer() && override {
|
|
std::move(builder).releaseOutputBuffer();
|
|
}
|
|
|
|
private:
|
|
PacketBuilderInterface& builder;
|
|
uint32_t diff;
|
|
};
|
|
} // namespace quic
|