blob: 94180f88e75a2f69e67260b7f98923d6284014cf [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 "quiche/quic/core/quic_coalesced_packet.h"
#include <string>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
namespace quic {
QuicCoalescedPacket::QuicCoalescedPacket()
: length_(0), max_packet_length_(0), ecn_codepoint_(ECN_NOT_ECT) {}
QuicCoalescedPacket::~QuicCoalescedPacket() { Clear(); }
bool QuicCoalescedPacket::MaybeCoalescePacket(
const SerializedPacket& packet, const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
quiche::QuicheBufferAllocator* allocator,
QuicPacketLength current_max_packet_length,
QuicEcnCodepoint ecn_codepoint) {
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 (ecn_codepoint != ecn_codepoint_) {
// Do not coalesce packets with different ECN codepoints.
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);
}
ecn_codepoint_ = ecn_codepoint;
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_ = absl::WrapUnique<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;
}
std::vector<size_t> QuicCoalescedPacket::packet_lengths() const {
std::vector<size_t> lengths;
for (const auto& packet : encrypted_buffers_) {
if (lengths.empty()) {
lengths.push_back(
initial_packet_ == nullptr ? 0 : initial_packet_->encrypted_length);
} else {
lengths.push_back(packet.length());
}
}
return lengths;
}
} // namespace quic