| // 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/core/quic_coalesced_packet.h" |
| |
| #include "absl/strings/str_cat.h" |
| #include "quic/platform/api/quic_bug_tracker.h" |
| #include "quic/platform/api/quic_ptr_util.h" |
| |
| namespace quic { |
| |
| QuicCoalescedPacket::QuicCoalescedPacket() |
| : length_(0), max_packet_length_(0) {} |
| |
| QuicCoalescedPacket::~QuicCoalescedPacket() { |
| Clear(); |
| } |
| |
| bool QuicCoalescedPacket::MaybeCoalescePacket( |
| const SerializedPacket& packet, |
| const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| QuicBufferAllocator* allocator, |
| QuicPacketLength current_max_packet_length) { |
| if (packet.encrypted_length == 0) { |
| QUIC_BUG(quic_bug_10611_1) << "Trying to coalesce an empty packet"; |
| return true; |
| } |
| if (length_ == 0) { |
| #ifndef NDEBUG |
| for (const auto& buffer : encrypted_buffers_) { |
| QUICHE_DCHECK(buffer.empty()); |
| } |
| #endif |
| QUICHE_DCHECK(initial_packet_ == nullptr); |
| // This is the first packet, set max_packet_length and self/peer |
| // addresses. |
| max_packet_length_ = current_max_packet_length; |
| self_address_ = self_address; |
| peer_address_ = peer_address; |
| } else { |
| if (self_address_ != self_address || peer_address_ != peer_address) { |
| // Do not coalesce packet with different self/peer addresses. |
| QUIC_DLOG(INFO) |
| << "Cannot coalesce packet because self/peer address changed"; |
| return false; |
| } |
| if (max_packet_length_ != current_max_packet_length) { |
| QUIC_BUG(quic_bug_10611_2) |
| << "Max packet length changes in the middle of the write path"; |
| return false; |
| } |
| if (ContainsPacketOfEncryptionLevel(packet.encryption_level)) { |
| // Do not coalesce packets of the same encryption level. |
| return false; |
| } |
| } |
| |
| if (length_ + packet.encrypted_length > max_packet_length_) { |
| // Packet does not fit. |
| return false; |
| } |
| QUIC_DVLOG(1) << "Successfully coalesced packet: encryption_level: " |
| << packet.encryption_level |
| << ", encrypted_length: " << packet.encrypted_length |
| << ", current length: " << length_ |
| << ", max_packet_length: " << max_packet_length_; |
| if (length_ > 0) { |
| QUIC_CODE_COUNT(QUIC_SUCCESSFULLY_COALESCED_MULTIPLE_PACKETS); |
| } |
| length_ += packet.encrypted_length; |
| transmission_types_[packet.encryption_level] = packet.transmission_type; |
| if (packet.encryption_level == ENCRYPTION_INITIAL) { |
| // Save a copy of ENCRYPTION_INITIAL packet (excluding encrypted buffer, as |
| // the packet will be re-serialized later). |
| initial_packet_ = QuicWrapUnique<SerializedPacket>( |
| CopySerializedPacket(packet, allocator, /*copy_buffer=*/false)); |
| return true; |
| } |
| // Copy encrypted buffer of packets with other encryption levels. |
| encrypted_buffers_[packet.encryption_level] = |
| std::string(packet.encrypted_buffer, packet.encrypted_length); |
| return true; |
| } |
| |
| void QuicCoalescedPacket::Clear() { |
| self_address_ = QuicSocketAddress(); |
| peer_address_ = QuicSocketAddress(); |
| length_ = 0; |
| max_packet_length_ = 0; |
| for (auto& packet : encrypted_buffers_) { |
| packet.clear(); |
| } |
| for (size_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { |
| transmission_types_[i] = NOT_RETRANSMISSION; |
| } |
| initial_packet_ = nullptr; |
| } |
| |
| void QuicCoalescedPacket::NeuterInitialPacket() { |
| if (initial_packet_ == nullptr) { |
| return; |
| } |
| if (length_ < initial_packet_->encrypted_length) { |
| QUIC_BUG(quic_bug_10611_3) |
| << "length_: " << length_ << ", is less than initial packet length: " |
| << initial_packet_->encrypted_length; |
| Clear(); |
| return; |
| } |
| length_ -= initial_packet_->encrypted_length; |
| if (length_ == 0) { |
| Clear(); |
| return; |
| } |
| transmission_types_[ENCRYPTION_INITIAL] = NOT_RETRANSMISSION; |
| initial_packet_ = nullptr; |
| } |
| |
| bool QuicCoalescedPacket::CopyEncryptedBuffers(char* buffer, |
| size_t buffer_len, |
| size_t* length_copied) const { |
| *length_copied = 0; |
| for (const auto& packet : encrypted_buffers_) { |
| if (packet.empty()) { |
| continue; |
| } |
| if (packet.length() > buffer_len) { |
| return false; |
| } |
| memcpy(buffer, packet.data(), packet.length()); |
| buffer += packet.length(); |
| buffer_len -= packet.length(); |
| *length_copied += packet.length(); |
| } |
| return true; |
| } |
| |
| bool QuicCoalescedPacket::ContainsPacketOfEncryptionLevel( |
| EncryptionLevel level) const { |
| return !encrypted_buffers_[level].empty() || |
| (level == ENCRYPTION_INITIAL && initial_packet_ != nullptr); |
| } |
| |
| TransmissionType QuicCoalescedPacket::TransmissionTypeOfPacket( |
| EncryptionLevel level) const { |
| if (!ContainsPacketOfEncryptionLevel(level)) { |
| QUIC_BUG(quic_bug_10611_4) |
| << "Coalesced packet does not contain packet of encryption level: " |
| << EncryptionLevelToString(level); |
| return NOT_RETRANSMISSION; |
| } |
| return transmission_types_[level]; |
| } |
| |
| size_t QuicCoalescedPacket::NumberOfPackets() const { |
| size_t num_of_packets = 0; |
| for (int8_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { |
| if (ContainsPacketOfEncryptionLevel(static_cast<EncryptionLevel>(i))) { |
| ++num_of_packets; |
| } |
| } |
| return num_of_packets; |
| } |
| |
| std::string QuicCoalescedPacket::ToString(size_t serialized_length) const { |
| // Total length and padding size. |
| std::string info = absl::StrCat( |
| "total_length: ", serialized_length, |
| " padding_size: ", serialized_length - length_, " packets: {"); |
| // Packets' encryption levels. |
| bool first_packet = true; |
| for (int8_t i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) { |
| if (ContainsPacketOfEncryptionLevel(static_cast<EncryptionLevel>(i))) { |
| absl::StrAppend(&info, first_packet ? "" : ", ", |
| EncryptionLevelToString(static_cast<EncryptionLevel>(i))); |
| first_packet = false; |
| } |
| } |
| absl::StrAppend(&info, "}"); |
| return info; |
| } |
| |
| } // namespace quic |