blob: 9c8479c91980913c144910c3f388b4fb1b8422a4 [file] [log] [blame]
// Copyright (c) 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 "net/third_party/quiche/src/quic/qbone/qbone_session_base.h"
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <utility>
#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h"
#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h"
#include "net/third_party/quiche/src/quic/qbone/qbone_constants.h"
namespace quic {
#define ENDPOINT \
(perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
QboneSessionBase::QboneSessionBase(
QuicConnection* connection,
Visitor* owner,
const QuicConfig& config,
const ParsedQuicVersionVector& supported_versions,
QbonePacketWriter* writer)
: QuicSession(connection,
owner,
config,
supported_versions,
/*num_expected_unidirectional_static_streams = */ 0) {
set_writer(writer);
const uint32_t max_streams =
(std::numeric_limits<uint32_t>::max() / kMaxAvailableStreamsMultiplier) -
1;
this->config()->SetMaxIncomingBidirectionalStreamsToSend(max_streams);
if (VersionHasIetfQuicFrames(transport_version())) {
ConfigureMaxIncomingDynamicStreamsToSend(max_streams);
}
}
QboneSessionBase::~QboneSessionBase() {
// Clear out the streams before leaving this destructor to avoid calling
// QuicSession::UnregisterStreamPriority
stream_map().clear();
closed_streams()->clear();
}
void QboneSessionBase::Initialize() {
crypto_stream_ = CreateCryptoStream();
QuicSession::Initialize();
}
const QuicCryptoStream* QboneSessionBase::GetCryptoStream() const {
return crypto_stream_.get();
}
QuicCryptoStream* QboneSessionBase::GetMutableCryptoStream() {
return crypto_stream_.get();
}
QuicStream* QboneSessionBase::CreateOutgoingStream() {
return ActivateDataStream(
CreateDataStream(GetNextOutgoingUnidirectionalStreamId()));
}
void QboneSessionBase::CloseStream(QuicStreamId stream_id) {
if (IsClosedStream(stream_id)) {
// When CloseStream has been called recursively (via
// QuicStream::OnClose), the stream is already closed so return.
return;
}
QuicSession::CloseStream(stream_id);
}
void QboneSessionBase::OnStreamFrame(const QuicStreamFrame& frame) {
if (frame.offset == 0 && frame.fin && frame.data_length > 0) {
++num_ephemeral_packets_;
ProcessPacketFromPeer(
QuicStringPiece(frame.data_buffer, frame.data_length));
flow_controller()->AddBytesConsumed(frame.data_length);
return;
}
QuicSession::OnStreamFrame(frame);
}
void QboneSessionBase::OnMessageReceived(QuicStringPiece message) {
++num_message_packets_;
ProcessPacketFromPeer(message);
}
QuicStream* QboneSessionBase::CreateIncomingStream(QuicStreamId id) {
return ActivateDataStream(CreateDataStream(id));
}
QuicStream* QboneSessionBase::CreateIncomingStream(PendingStream* /*pending*/) {
QUIC_NOTREACHED();
return nullptr;
}
bool QboneSessionBase::ShouldKeepConnectionAlive() const {
// Qbone connections stay alive until they're explicitly closed.
return true;
}
std::unique_ptr<QuicStream> QboneSessionBase::CreateDataStream(
QuicStreamId id) {
if (crypto_stream_ == nullptr || !crypto_stream_->encryption_established()) {
// Encryption not active so no stream created
return nullptr;
}
if (IsIncomingStream(id)) {
++num_streamed_packets_;
return std::make_unique<QboneReadOnlyStream>(id, this);
}
return std::make_unique<QboneWriteOnlyStream>(id, this);
}
QuicStream* QboneSessionBase::ActivateDataStream(
std::unique_ptr<QuicStream> stream) {
// Transfer ownership of the data stream to the session via ActivateStream().
QuicStream* raw = stream.get();
if (stream) {
// Make QuicSession take ownership of the stream.
ActivateStream(std::move(stream));
}
return raw;
}
void QboneSessionBase::SendPacketToPeer(QuicStringPiece packet) {
if (crypto_stream_ == nullptr) {
QUIC_BUG << "Attempting to send packet before encryption established";
return;
}
if (send_packets_as_messages_) {
QuicMemSlice slice(connection()->helper()->GetStreamSendBufferAllocator(),
packet.size());
memcpy(const_cast<char*>(slice.data()), packet.data(), packet.size());
switch (SendMessage(QuicMemSliceSpan(&slice)).status) {
case MESSAGE_STATUS_SUCCESS:
break;
case MESSAGE_STATUS_TOO_LARGE: {
if (packet.size() < sizeof(ip6_hdr)) {
QUIC_BUG << "Dropped malformed packet: IPv6 header too short";
break;
}
auto* header = reinterpret_cast<const ip6_hdr*>(packet.begin());
icmp6_hdr icmp_header{};
icmp_header.icmp6_type = ICMP6_PACKET_TOO_BIG;
icmp_header.icmp6_mtu =
connection()->GetGuaranteedLargestMessagePayload();
CreateIcmpPacket(header->ip6_dst, header->ip6_src, icmp_header, packet,
[this](QuicStringPiece icmp_packet) {
writer_->WritePacketToNetwork(icmp_packet.data(),
icmp_packet.size());
});
break;
}
case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED:
QUIC_BUG << "MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED";
break;
case MESSAGE_STATUS_UNSUPPORTED:
QUIC_BUG << "MESSAGE_STATUS_UNSUPPORTED";
break;
case MESSAGE_STATUS_BLOCKED:
QUIC_BUG << "MESSAGE_STATUS_BLOCKED";
break;
case MESSAGE_STATUS_INTERNAL_ERROR:
QUIC_BUG << "MESSAGE_STATUS_INTERNAL_ERROR";
break;
}
return;
}
// Qbone streams are ephemeral.
QuicStream* stream = CreateOutgoingStream();
if (!stream) {
QUIC_BUG << "Failed to create an outgoing QBONE stream.";
return;
}
QboneWriteOnlyStream* qbone_stream =
static_cast<QboneWriteOnlyStream*>(stream);
qbone_stream->WritePacketToQuicStream(packet);
}
uint64_t QboneSessionBase::GetNumEphemeralPackets() const {
return num_ephemeral_packets_;
}
uint64_t QboneSessionBase::GetNumStreamedPackets() const {
return num_streamed_packets_;
}
uint64_t QboneSessionBase::GetNumMessagePackets() const {
return num_message_packets_;
}
uint64_t QboneSessionBase::GetNumFallbackToStream() const {
return num_fallback_to_stream_;
}
void QboneSessionBase::set_writer(QbonePacketWriter* writer) {
writer_ = writer;
testing::testvalue::Adjust("quic_QbonePacketWriter", &writer_);
}
} // namespace quic