blob: 2f2f31e3239747189f936628e68197fa7afd3163 [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 "quic/qbone/qbone_session_base.h"
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <utility>
#include "absl/strings/string_view.h"
#include "quic/core/quic_buffer_allocator.h"
#include "quic/core/quic_data_reader.h"
#include "quic/core/quic_types.h"
#include "quic/platform/api/quic_exported_stats.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/platform/api/quic_testvalue.h"
#include "quic/qbone/platform/icmp_packet.h"
#include "quic/qbone/qbone_constants.h"
DEFINE_QUIC_COMMAND_LINE_FLAG(
bool,
qbone_close_ephemeral_frames,
true,
"If true, we'll call CloseStream even when we receive ephemeral frames.");
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()->SetMaxBidirectionalStreamsToSend(max_streams);
if (VersionHasIetfQuicFrames(transport_version())) {
this->config()->SetMaxUnidirectionalStreamsToSend(max_streams);
}
}
QboneSessionBase::~QboneSessionBase() {}
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::OnStreamFrame(const QuicStreamFrame& frame) {
if (frame.offset == 0 && frame.fin && frame.data_length > 0) {
++num_ephemeral_packets_;
ProcessPacketFromPeer(
absl::string_view(frame.data_buffer, frame.data_length));
flow_controller()->AddBytesConsumed(frame.data_length);
// TODO(b/147817422): Add a counter for how many streams were actually
// closed here.
if (GetQuicFlag(FLAGS_qbone_close_ephemeral_frames)) {
ResetStream(frame.stream_id, QUIC_STREAM_CANCELLED);
}
return;
}
QuicSession::OnStreamFrame(frame);
}
void QboneSessionBase::OnMessageReceived(absl::string_view 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(absl::string_view packet) {
if (crypto_stream_ == nullptr) {
QUIC_BUG(quic_bug_10987_1)
<< "Attempting to send packet before encryption established";
return;
}
if (send_packets_as_messages_) {
QuicMemSlice slice(QuicBuffer::Copy(
connection()->helper()->GetStreamSendBufferAllocator(), packet));
switch (SendMessage(absl::MakeSpan(&slice, 1), /*flush=*/true).status) {
case MESSAGE_STATUS_SUCCESS:
break;
case MESSAGE_STATUS_TOO_LARGE: {
if (packet.size() < sizeof(ip6_hdr)) {
QUIC_BUG(quic_bug_10987_2)
<< "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](absl::string_view icmp_packet) {
writer_->WritePacketToNetwork(icmp_packet.data(),
icmp_packet.size());
});
break;
}
case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED:
QUIC_BUG(quic_bug_10987_3)
<< "MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED";
break;
case MESSAGE_STATUS_UNSUPPORTED:
QUIC_BUG(quic_bug_10987_4) << "MESSAGE_STATUS_UNSUPPORTED";
break;
case MESSAGE_STATUS_BLOCKED:
QUIC_BUG(quic_bug_10987_5) << "MESSAGE_STATUS_BLOCKED";
break;
case MESSAGE_STATUS_INTERNAL_ERROR:
QUIC_BUG(quic_bug_10987_6) << "MESSAGE_STATUS_INTERNAL_ERROR";
break;
}
return;
}
// QBONE streams are ephemeral.
QuicStream* stream = CreateOutgoingStream();
if (!stream) {
QUIC_BUG(quic_bug_10987_7) << "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;
quic::AdjustTestValue("quic_QbonePacketWriter", &writer_);
}
} // namespace quic