| // 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); | |
| if (client_->masque_client()->masque_mode() == | |
| MasqueMode::kConnectUdpBind) { | |
| client_->masque_client() | |
| ->masque_client_session() | |
| ->SendConnectUdpBindPacket( | |
| packet, peer_address, | |
| client_->masque_encapsulated_client_session()); | |
| } else { | |
| 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()); | |
| } | |
| void MasqueEncapsulatedClient::SendDnsAssignCapsule( | |
| const DnsAssignCapsule& capsule) { | |
| masque_client_->masque_client_session()->SendDnsAssignCapsule( | |
| capsule, masque_encapsulated_client_session()); | |
| } | |
| void MasqueEncapsulatedClient::SendPref64Capsule(const Pref64Capsule& capsule) { | |
| masque_client_->masque_client_session()->SendPref64Capsule( | |
| capsule, masque_encapsulated_client_session()); | |
| } | |
| QuicByteCount MaxPacketSizeForEncapsulatedConnections( | |
| MasqueClient* underlying_masque_client) { | |
| QuicByteCount max_packet_size = | |
| underlying_masque_client->masque_client_session() | |
| ->GetGuaranteedLargestDatagramPayload() - | |
| /* 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 |