blob: d3843638f7c87adbce49fdcbc5781490c7882c5a [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 <optional>
#include "quiche/quic/core/quic_utils.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/tools/quic_client_default_network_helper.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*/) 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 {}
absl::optional<int> MessageTooBigErrorCode() const override {
return absl::nullopt;
}
QuicByteCount GetMaxPacketSize(
const QuicSocketAddress& /*peer_address*/) const override {
return kMasqueMaxEncapsulatedPacketSize;
}
bool SupportsReleaseTime() const override { return false; }
bool IsBatchMode() 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
MasqueEncapsulatedClient::MasqueEncapsulatedClient(
QuicSocketAddress server_address, const QuicServerId& server_id,
QuicEventLoop* event_loop, std::unique_ptr<ProofVerifier> proof_verifier,
MasqueClient* masque_client)
: QuicDefaultClient(
server_address, server_id, MasqueSupportedVersions(),
MasqueEncapsulatedConfig(), event_loop,
std::make_unique<MasqueClientDefaultNetworkHelper>(event_loop, this),
std::move(proof_verifier)),
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();
return std::make_unique<MasqueEncapsulatedClientSession>(
*config(), supported_versions, connection, server_id(), crypto_config(),
push_promise_index(), masque_client_->masque_client_session());
}
MasqueEncapsulatedClientSession*
MasqueEncapsulatedClient::masque_encapsulated_client_session() {
return static_cast<MasqueEncapsulatedClientSession*>(
QuicDefaultClient::session());
}
} // namespace quic