blob: a4d83f68233369a79410a5ab98b411a0e5693011 [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_session.h"
#include "absl/strings/string_view.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_ip_address.h"
namespace quic {
MasqueEncapsulatedClientSession::MasqueEncapsulatedClientSession(
const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
QuicConnection* connection, const QuicServerId& server_id,
QuicCryptoClientConfig* crypto_config,
QuicClientPushPromiseIndex* push_promise_index,
MasqueClientSession* masque_client_session)
: QuicSpdyClientSession(config, supported_versions, connection, server_id,
crypto_config, push_promise_index),
masque_client_session_(masque_client_session) {}
void MasqueEncapsulatedClientSession::ProcessPacket(
absl::string_view packet, QuicSocketAddress server_address) {
QuicTime now = connection()->clock()->ApproximateNow();
QuicReceivedPacket received_packet(packet.data(), packet.length(), now);
connection()->ProcessUdpPacket(connection()->self_address(), server_address,
received_packet);
}
void MasqueEncapsulatedClientSession::CloseConnection(
QuicErrorCode error, const std::string& details,
ConnectionCloseBehavior connection_close_behavior) {
connection()->CloseConnection(error, details, connection_close_behavior);
}
void MasqueEncapsulatedClientSession::OnConnectionClosed(
const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) {
QuicSpdyClientSession::OnConnectionClosed(frame, source);
masque_client_session_->CloseConnectUdpStream(this);
}
void MasqueEncapsulatedClientSession::ProcessIpPacket(
absl::string_view packet) {
quiche::QuicheDataReader reader(packet);
uint8_t first_byte;
if (!reader.ReadUInt8(&first_byte)) {
QUIC_DLOG(ERROR) << "Dropping empty CONNECT-IP packet";
return;
}
const uint8_t ip_version = first_byte >> 4;
quiche::QuicheIpAddress server_ip;
if (ip_version == 6) {
if (!reader.Seek(5)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IPv6 start"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
uint8_t next_header = 0;
if (!reader.ReadUInt8(&next_header)) {
QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP next header"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (next_header != 17) {
// Note that this drops packets with IPv6 extension headers, since we
// do not expect to see them in practice.
QUIC_DLOG(ERROR)
<< "Dropping CONNECT-IP packet with unexpected next header "
<< static_cast<int>(next_header) << "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (!reader.Seek(1)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP hop limit"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
absl::string_view source_ip;
if (!reader.ReadStringPiece(&source_ip, 16)) {
QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP source IPv6"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
server_ip.FromPackedString(source_ip.data(), source_ip.length());
if (!reader.Seek(16)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP destination IPv6"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
} else if (ip_version == 4) {
uint8_t ihl = first_byte & 0xF;
if (ihl < 5) {
QUICHE_DLOG(ERROR) << "Dropping CONNECT-IP packet with invalid IHL "
<< static_cast<int>(ihl) << "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (!reader.Seek(8)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IPv4 start"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
uint8_t ip_proto = 0;
if (!reader.ReadUInt8(&ip_proto)) {
QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP ip_proto"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (ip_proto != 17) {
QUIC_DLOG(ERROR) << "Dropping CONNECT-IP packet with unexpected IP proto "
<< static_cast<int>(ip_proto) << "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (!reader.Seek(2)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IP checksum"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
absl::string_view source_ip;
if (!reader.ReadStringPiece(&source_ip, 4)) {
QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP source IPv4"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
server_ip.FromPackedString(source_ip.data(), source_ip.length());
if (!reader.Seek(4)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP destination IPv4"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
uint8_t ip_options_length = (ihl - 5) * 4;
if (!reader.Seek(ip_options_length)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IP options of length "
<< static_cast<int>(ip_options_length) << "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
} else {
QUIC_DLOG(ERROR) << "Dropping CONNECT-IP packet with unexpected IP version "
<< static_cast<int>(ip_version) << "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
// Parse UDP header.
uint16_t server_port;
if (!reader.ReadUInt16(&server_port)) {
QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP source port"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (!reader.Seek(2)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP destination port"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
uint16_t udp_length;
if (!reader.ReadUInt16(&udp_length)) {
QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP UDP length"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (udp_length < 8) {
QUICHE_DLOG(ERROR) << "Dropping CONNECT-IP packet with invalid UDP length "
<< udp_length << "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (!reader.Seek(2)) {
QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP UDP checksum"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
absl::string_view quic_packet;
if (!reader.ReadStringPiece(&quic_packet, udp_length - 8)) {
QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP UDP payload"
<< "\n"
<< quiche::QuicheTextUtils::HexDump(packet);
return;
}
if (!reader.IsDoneReading()) {
QUICHE_DLOG(INFO) << "Received CONNECT-IP UDP packet with "
<< reader.BytesRemaining()
<< " extra bytes after payload\n"
<< quiche::QuicheTextUtils::HexDump(packet);
}
QUIC_DLOG(INFO) << "Received CONNECT-IP encapsulated packet of length "
<< quic_packet.size();
QuicTime now = connection()->clock()->ApproximateNow();
QuicReceivedPacket received_packet(quic_packet.data(), quic_packet.size(),
now);
QuicSocketAddress server_address = QuicSocketAddress(server_ip, server_port);
connection()->ProcessUdpPacket(connection()->self_address(), server_address,
received_packet);
}
void MasqueEncapsulatedClientSession::CloseIpSession(
const std::string& details) {
connection()->CloseConnection(QUIC_CONNECTION_CANCELLED, details,
ConnectionCloseBehavior::SILENT_CLOSE);
}
bool MasqueEncapsulatedClientSession::OnAddressAssignCapsule(
const AddressAssignCapsule& capsule) {
QUIC_DLOG(INFO) << "Received capsule " << capsule.ToString();
for (auto assigned_address : capsule.assigned_addresses) {
if (assigned_address.ip_prefix.address().IsIPv4() &&
!local_v4_address_.IsInitialized()) {
QUIC_LOG(INFO)
<< "MasqueEncapsulatedClientSession saving local IPv4 address "
<< assigned_address.ip_prefix.address();
local_v4_address_ = assigned_address.ip_prefix.address();
} else if (assigned_address.ip_prefix.address().IsIPv6() &&
!local_v6_address_.IsInitialized()) {
QUIC_LOG(INFO)
<< "MasqueEncapsulatedClientSession saving local IPv6 address "
<< assigned_address.ip_prefix.address();
local_v6_address_ = assigned_address.ip_prefix.address();
}
}
return true;
}
bool MasqueEncapsulatedClientSession::OnAddressRequestCapsule(
const AddressRequestCapsule& capsule) {
QUIC_DLOG(INFO) << "Ignoring received capsule " << capsule.ToString();
return true;
}
bool MasqueEncapsulatedClientSession::OnRouteAdvertisementCapsule(
const RouteAdvertisementCapsule& capsule) {
QUIC_DLOG(INFO) << "Ignoring received capsule " << capsule.ToString();
return true;
}
} // namespace quic