blob: 78ff0873aef99608b0ca6b7f11cf15cf7a66d98c [file] [log] [blame]
// Copyright 2019 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.
#include "quiche/quic/masque/masque_encapsulated_client.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "quiche/quic/core/crypto/proof_verifier.h"
#include "quiche/quic/core/io/quic_event_loop.h"
#include "quiche/quic/core/quic_connection.h"
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_packet_writer.h"
#include "quiche/quic/core/quic_server_id.h"
#include "quiche/quic/core/quic_session.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/masque/masque_client.h"
#include "quiche/quic/masque/masque_client_session.h"
#include "quiche/quic/masque/masque_encapsulated_client_session.h"
#include "quiche/quic/masque/masque_utils.h"
#include "quiche/quic/platform/api/quic_ip_address.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/quic/tools/quic_client_default_network_helper.h"
#include "quiche/quic/tools/quic_default_client.h"
#include "quiche/quic/tools/quic_name_lookup.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_data_reader.h"
#include "quiche/common/quiche_data_writer.h"
namespace quic {
namespace {
class ChecksumWriter {
public:
explicit ChecksumWriter(quiche::QuicheDataWriter& writer) : writer_(writer) {}
void IngestUInt16(uint16_t val) { accumulator_ += val; }
void IngestUInt8(uint8_t val) {
uint16_t val16 = odd_ ? val : (val << 8);
accumulator_ += val16;
odd_ = !odd_;
}
bool IngestData(size_t offset, size_t length) {
quiche::QuicheDataReader reader(
writer_.data(), std::min<size_t>(offset + length, writer_.capacity()));
if (!reader.Seek(offset) || reader.BytesRemaining() < length) {
return false;
}
// Handle any potentially off first byte.
uint8_t first_byte;
if (odd_ && reader.ReadUInt8(&first_byte)) {
IngestUInt8(first_byte);
}
// Handle each 16-bit word at a time.
while (reader.BytesRemaining() >= sizeof(uint16_t)) {
uint16_t word;
if (!reader.ReadUInt16(&word)) {
return false;
}
IngestUInt16(word);
}
// Handle any leftover odd byte.
uint8_t last_byte;
if (reader.ReadUInt8(&last_byte)) {
IngestUInt8(last_byte);
}
return true;
}
bool WriteChecksumAtOffset(size_t offset) {
while (accumulator_ >> 16 > 0) {
accumulator_ = (accumulator_ & 0xffff) + (accumulator_ >> 16);
}
accumulator_ = 0xffff & ~accumulator_;
quiche::QuicheDataWriter writer2(writer_.capacity(), writer_.data());
return writer2.Seek(offset) && writer2.WriteUInt16(accumulator_);
}
private:
quiche::QuicheDataWriter& writer_;
uint32_t accumulator_ = 0xffff;
bool odd_ = false;
};
// Custom packet writer that allows getting all of a connection's outgoing
// packets.
class MasquePacketWriter : public QuicPacketWriter {
public:
explicit MasquePacketWriter(MasqueEncapsulatedClient* client)
: client_(client) {}
WriteResult WritePacket(const char* buffer, size_t buf_len,
const QuicIpAddress& /*self_address*/,
const QuicSocketAddress& peer_address,
PerPacketOptions* /*options*/,
const QuicPacketWriterParams& /*params*/) override {
QUICHE_DCHECK(peer_address.IsInitialized());
QUIC_DVLOG(1) << "MasquePacketWriter trying to write " << buf_len
<< " bytes to " << peer_address;
if (client_->masque_client()->masque_mode() == MasqueMode::kConnectIp) {
constexpr size_t kIPv4HeaderSize = 20;
constexpr size_t kIPv4ChecksumOffset = 10;
constexpr size_t kIPv6HeaderSize = 40;
constexpr size_t kUdpHeaderSize = 8;
const size_t udp_length = kUdpHeaderSize + buf_len;
std::string packet;
packet.resize(
(peer_address.host().IsIPv6() ? kIPv6HeaderSize : kIPv4HeaderSize) +
udp_length);
quiche::QuicheDataWriter writer(packet.size(), packet.data());
if (peer_address.host().IsIPv6()) {
// Write IPv6 header.
QUICHE_CHECK(writer.WriteUInt8(0x60)); // Version = 6 and DSCP.
QUICHE_CHECK(writer.WriteUInt8(0)); // DSCP/ECN and flow label.
QUICHE_CHECK(writer.WriteUInt16(0)); // Flow label.
QUICHE_CHECK(writer.WriteUInt16(udp_length)); // Payload Length.
QUICHE_CHECK(writer.WriteUInt8(17)); // Next header = UDP.
QUICHE_CHECK(writer.WriteUInt8(64)); // Hop limit = 64.
in6_addr source_address = {};
if (client_->masque_encapsulated_client_session()
->local_v6_address()
.IsIPv6()) {
source_address = client_->masque_encapsulated_client_session()
->local_v6_address()
.GetIPv6();
}
QUICHE_CHECK(
writer.WriteBytes(&source_address, sizeof(source_address)));
in6_addr destination_address = peer_address.host().GetIPv6();
QUICHE_CHECK(writer.WriteBytes(&destination_address,
sizeof(destination_address)));
} else {
// Write IPv4 header.
QUICHE_CHECK(writer.WriteUInt8(0x45)); // Version = 4, IHL = 5.
QUICHE_CHECK(writer.WriteUInt8(0)); // DSCP/ECN.
QUICHE_CHECK(writer.WriteUInt16(packet.size())); // Total Length.
QUICHE_CHECK(writer.WriteUInt32(0)); // No fragmentation.
QUICHE_CHECK(writer.WriteUInt8(64)); // TTL = 64.
QUICHE_CHECK(writer.WriteUInt8(17)); // IP Protocol = UDP.
QUICHE_CHECK(writer.WriteUInt16(0)); // Checksum = 0 initially.
in_addr source_address = {};
if (client_->masque_encapsulated_client_session()
->local_v4_address()
.IsIPv4()) {
source_address = client_->masque_encapsulated_client_session()
->local_v4_address()
.GetIPv4();
}
QUICHE_CHECK(
writer.WriteBytes(&source_address, sizeof(source_address)));
in_addr destination_address = peer_address.host().GetIPv4();
QUICHE_CHECK(writer.WriteBytes(&destination_address,
sizeof(destination_address)));
ChecksumWriter ip_checksum_writer(writer);
QUICHE_CHECK(ip_checksum_writer.IngestData(0, kIPv4HeaderSize));
QUICHE_CHECK(
ip_checksum_writer.WriteChecksumAtOffset(kIPv4ChecksumOffset));
}
// Write UDP header.
QUICHE_CHECK(writer.WriteUInt16(0x1234)); // Source port.
QUICHE_CHECK(
writer.WriteUInt16(peer_address.port())); // Destination port.
QUICHE_CHECK(writer.WriteUInt16(udp_length)); // UDP length.
QUICHE_CHECK(writer.WriteUInt16(0)); // Checksum = 0 initially.
// Write UDP payload.
QUICHE_CHECK(writer.WriteBytes(buffer, buf_len));
ChecksumWriter udp_checksum_writer(writer);
if (peer_address.host().IsIPv6()) {
QUICHE_CHECK(udp_checksum_writer.IngestData(8, 32)); // IP addresses.
udp_checksum_writer.IngestUInt16(0); // High bits of UDP length.
udp_checksum_writer.IngestUInt16(
udp_length); // Low bits of UDP length.
udp_checksum_writer.IngestUInt16(0); // Zeroes.
udp_checksum_writer.IngestUInt8(0); // Zeroes.
udp_checksum_writer.IngestUInt8(17); // Next header = UDP.
QUICHE_CHECK(udp_checksum_writer.IngestData(
kIPv6HeaderSize, udp_length)); // UDP header and data.
QUICHE_CHECK(
udp_checksum_writer.WriteChecksumAtOffset(kIPv6HeaderSize + 6));
} else {
QUICHE_CHECK(udp_checksum_writer.IngestData(12, 8)); // IP addresses.
udp_checksum_writer.IngestUInt8(0); // Zeroes.
udp_checksum_writer.IngestUInt8(17); // IP Protocol = UDP.
udp_checksum_writer.IngestUInt16(udp_length); // UDP length.
QUICHE_CHECK(udp_checksum_writer.IngestData(
kIPv4HeaderSize, udp_length)); // UDP header and data.
QUICHE_CHECK(
udp_checksum_writer.WriteChecksumAtOffset(kIPv4HeaderSize + 6));
}
client_->masque_client()->masque_client_session()->SendIpPacket(
packet, client_->masque_encapsulated_client_session());
} else {
absl::string_view packet(buffer, buf_len);
client_->masque_client()->masque_client_session()->SendPacket(
packet, peer_address, client_->masque_encapsulated_client_session());
}
return WriteResult(WRITE_STATUS_OK, buf_len);
}
bool IsWriteBlocked() const override { return false; }
void SetWritable() override {}
std::optional<int> MessageTooBigErrorCode() const override {
return std::nullopt;
}
QuicByteCount GetMaxPacketSize(
const QuicSocketAddress& /*peer_address*/) const override {
// This is only used as a min against the other limits, so we set it to the
// maximum value so it doesn't reduce the MTU.
return kDefaultMaxPacketSizeForTunnels;
}
bool SupportsReleaseTime() const override { return false; }
bool IsBatchMode() const override { return false; }
bool SupportsEcn() const override { return false; }
QuicPacketBuffer GetNextWriteLocation(
const QuicIpAddress& /*self_address*/,
const QuicSocketAddress& /*peer_address*/) override {
return {nullptr, nullptr};
}
WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); }
private:
MasqueEncapsulatedClient* client_; // Unowned.
};
// Custom network helper that allows injecting a custom packet writer in order
// to get all of a connection's outgoing packets.
class MasqueClientDefaultNetworkHelper : public QuicClientDefaultNetworkHelper {
public:
MasqueClientDefaultNetworkHelper(QuicEventLoop* event_loop,
MasqueEncapsulatedClient* client)
: QuicClientDefaultNetworkHelper(event_loop, client), client_(client) {}
QuicPacketWriter* CreateQuicPacketWriter() override {
return new MasquePacketWriter(client_);
}
private:
MasqueEncapsulatedClient* client_; // Unowned.
};
} // namespace
// static
std::unique_ptr<MasqueEncapsulatedClient> MasqueEncapsulatedClient::Create(
QuicSocketAddress server_address, const QuicServerId& server_id,
const std::string& uri_template, MasqueMode masque_mode,
QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier,
MasqueClient* underlying_masque_client) {
// Use absl::WrapUnique instead of std::make_unique because constructor is
// private and therefore not accessible from make_unique.
auto masque_client = absl::WrapUnique(new MasqueEncapsulatedClient(
server_address, server_id, masque_mode, event_loop,
std::move(proof_verifier), underlying_masque_client, uri_template));
if (masque_client == nullptr) {
QUIC_LOG(ERROR) << "Failed to create masque_client";
return nullptr;
}
if (!masque_client->Prepare(
MaxPacketSizeForEncapsulatedConnections(underlying_masque_client))) {
return nullptr;
}
return masque_client;
}
MasqueEncapsulatedClient::MasqueEncapsulatedClient(
QuicSocketAddress server_address, const QuicServerId& server_id,
QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier,
MasqueClient* masque_client)
: MasqueClient(
server_address, server_id, event_loop,
MasqueEncapsulatedConfig(masque_client),
std::make_unique<MasqueClientDefaultNetworkHelper>(event_loop, this),
std::move(proof_verifier)),
masque_client_(masque_client) {}
MasqueEncapsulatedClient::MasqueEncapsulatedClient(
QuicSocketAddress server_address, const QuicServerId& server_id,
MasqueMode masque_mode, QuicEventLoop* event_loop,
std::unique_ptr<ProofVerifier> proof_verifier, MasqueClient* masque_client,
const std::string& uri_template)
: MasqueClient(
server_address, server_id, masque_mode, event_loop,
MasqueEncapsulatedConfig(masque_client),
std::make_unique<MasqueClientDefaultNetworkHelper>(event_loop, this),
std::move(proof_verifier), uri_template),
masque_client_(masque_client) {}
MasqueEncapsulatedClient::~MasqueEncapsulatedClient() {
masque_client_->masque_client_session()->CloseConnectUdpStream(
masque_encapsulated_client_session());
}
std::unique_ptr<QuicSession> MasqueEncapsulatedClient::CreateQuicClientSession(
const ParsedQuicVersionVector& supported_versions,
QuicConnection* connection) {
QUIC_DLOG(INFO) << "Creating MASQUE encapsulated session for "
<< connection->connection_id();
if (!uri_template().empty()) {
return std::make_unique<MasqueEncapsulatedClientSession>(
masque_mode(), uri_template(), *config(), supported_versions,
connection, server_id(), crypto_config(),
masque_client_->masque_client_session(), this);
}
return std::make_unique<MasqueEncapsulatedClientSession>(
*config(), supported_versions, connection, server_id(), crypto_config(),
masque_client_->masque_client_session(), this);
}
MasqueEncapsulatedClientSession*
MasqueEncapsulatedClient::masque_encapsulated_client_session() {
return static_cast<MasqueEncapsulatedClientSession*>(
QuicDefaultClient::session());
}
QuicByteCount MaxPacketSizeForEncapsulatedConnections(
MasqueClient* underlying_masque_client) {
QuicByteCount max_packet_size =
underlying_masque_client->masque_client_session()
->GetGuaranteedLargestMessagePayload() -
/* max length of quarter stream ID */ sizeof(QuicStreamId) -
/* context ID set to zero */ sizeof(uint8_t);
QUICHE_CHECK_GE(max_packet_size, 1200u)
<< "RFC 9000 requires QUIC max packet size to be above 1200 bytes";
return max_packet_size;
}
QuicConfig MasqueEncapsulatedConfig(MasqueClient* underlying_masque_client) {
QuicConfig config;
config.SetMaxPacketSizeToSend(
MaxPacketSizeForEncapsulatedConnections(underlying_masque_client));
return config;
}
} // namespace quic