| // 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 |