| // 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_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" | |
| #include "common/platform/api/quiche_command_line_flags.h" | |
| #include "common/platform/api/quiche_mem_slice.h" | |
| #include "common/quiche_buffer_allocator.h" | |
| DEFINE_QUICHE_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 (!IsEncryptionEstablished()) { | |
| // 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_) { | |
| quiche::QuicheMemSlice slice(quiche::QuicheBuffer::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 |