blob: d2218880fff69878b60744236abe170f19753627 [file] [log] [blame]
// Copyright (c) 2012 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/test_tools/simulator/quic_endpoint_base.h"
#include <memory>
#include <utility>
#include "absl/strings/str_cat.h"
#include "quic/core/crypto/crypto_handshake_message.h"
#include "quic/core/crypto/crypto_protocol.h"
#include "quic/core/quic_connection.h"
#include "quic/core/quic_data_writer.h"
#include "quic/platform/api/quic_test_output.h"
#include "quic/test_tools/quic_connection_peer.h"
#include "quic/test_tools/quic_test_utils.h"
#include "quic/test_tools/simulator/simulator.h"
#include "common/platform/api/quiche_str_cat.h"
namespace quic {
namespace simulator {
// Takes a SHA-1 hash of the name and converts it into five 32-bit integers.
static std::vector<uint32_t> HashNameIntoFive32BitIntegers(std::string name) {
const std::string hash = test::Sha1Hash(name);
std::vector<uint32_t> output;
uint32_t current_number = 0;
for (size_t i = 0; i < hash.size(); i++) {
current_number = (current_number << 8) + hash[i];
if (i % 4 == 3) {
output.push_back(i);
current_number = 0;
}
}
return output;
}
QuicSocketAddress GetAddressFromName(std::string name) {
const std::vector<uint32_t> hash = HashNameIntoFive32BitIntegers(name);
// Generate a random port between 1025 and 65535.
const uint16_t port = 1025 + hash[0] % (65535 - 1025 + 1);
// Generate a random 10.x.x.x address, where x is between 1 and 254.
std::string ip_address{"\xa\0\0\0", 4};
for (size_t i = 1; i < 4; i++) {
ip_address[i] = 1 + hash[i] % 254;
}
QuicIpAddress host;
host.FromPackedString(ip_address.c_str(), ip_address.length());
return QuicSocketAddress(host, port);
}
QuicEndpointBase::QuicEndpointBase(Simulator* simulator,
std::string name,
std::string peer_name)
: Endpoint(simulator, name),
peer_name_(peer_name),
writer_(this),
nic_tx_queue_(simulator,
quiche::QuicheStringPrintf("%s (TX Queue)", name.c_str()),
kMaxOutgoingPacketSize * kTxQueueSize),
connection_(nullptr),
write_blocked_count_(0),
drop_next_packet_(false) {
nic_tx_queue_.set_listener_interface(this);
}
QuicEndpointBase::~QuicEndpointBase() {
if (trace_visitor_ != nullptr) {
const char* perspective_prefix =
connection_->perspective() == Perspective::IS_CLIENT ? "C" : "S";
std::string identifier = absl::StrCat(
perspective_prefix, connection_->connection_id().ToString());
QuicRecordTrace(identifier, trace_visitor_->trace()->SerializeAsString());
}
}
void QuicEndpointBase::DropNextIncomingPacket() {
drop_next_packet_ = true;
}
void QuicEndpointBase::RecordTrace() {
trace_visitor_ = std::make_unique<QuicTraceVisitor>(connection_.get());
connection_->set_debug_visitor(trace_visitor_.get());
}
void QuicEndpointBase::AcceptPacket(std::unique_ptr<Packet> packet) {
if (packet->destination != name_) {
return;
}
if (drop_next_packet_) {
drop_next_packet_ = false;
return;
}
QuicReceivedPacket received_packet(packet->contents.data(),
packet->contents.size(), clock_->Now());
connection_->ProcessUdpPacket(connection_->self_address(),
connection_->peer_address(), received_packet);
}
UnconstrainedPortInterface* QuicEndpointBase::GetRxPort() {
return this;
}
void QuicEndpointBase::SetTxPort(ConstrainedPortInterface* port) {
// Any egress done by the endpoint is actually handled by a queue on an NIC.
nic_tx_queue_.set_tx_port(port);
}
void QuicEndpointBase::OnPacketDequeued() {
if (writer_.IsWriteBlocked() &&
(nic_tx_queue_.capacity() - nic_tx_queue_.bytes_queued()) >=
kMaxOutgoingPacketSize) {
writer_.SetWritable();
connection_->OnCanWrite();
}
}
QuicEndpointBase::Writer::Writer(QuicEndpointBase* endpoint)
: endpoint_(endpoint), is_blocked_(false) {}
QuicEndpointBase::Writer::~Writer() {}
WriteResult QuicEndpointBase::Writer::WritePacket(
const char* buffer,
size_t buf_len,
const QuicIpAddress& /*self_address*/,
const QuicSocketAddress& /*peer_address*/,
PerPacketOptions* options) {
DCHECK(!IsWriteBlocked());
DCHECK(options == nullptr);
DCHECK(buf_len <= kMaxOutgoingPacketSize);
// Instead of losing a packet, become write-blocked when the egress queue is
// full.
if (endpoint_->nic_tx_queue_.packets_queued() > kTxQueueSize) {
is_blocked_ = true;
endpoint_->write_blocked_count_++;
return WriteResult(WRITE_STATUS_BLOCKED, 0);
}
auto packet = std::make_unique<Packet>();
packet->source = endpoint_->name();
packet->destination = endpoint_->peer_name_;
packet->tx_timestamp = endpoint_->clock_->Now();
packet->contents = std::string(buffer, buf_len);
packet->size = buf_len;
endpoint_->nic_tx_queue_.AcceptPacket(std::move(packet));
return WriteResult(WRITE_STATUS_OK, buf_len);
}
bool QuicEndpointBase::Writer::IsWriteBlocked() const {
return is_blocked_;
}
void QuicEndpointBase::Writer::SetWritable() {
is_blocked_ = false;
}
QuicByteCount QuicEndpointBase::Writer::GetMaxPacketSize(
const QuicSocketAddress& /*peer_address*/) const {
return kMaxOutgoingPacketSize;
}
bool QuicEndpointBase::Writer::SupportsReleaseTime() const {
return false;
}
bool QuicEndpointBase::Writer::IsBatchMode() const {
return false;
}
QuicPacketBuffer QuicEndpointBase::Writer::GetNextWriteLocation(
const QuicIpAddress& /*self_address*/,
const QuicSocketAddress& /*peer_address*/) {
return {nullptr, nullptr};
}
WriteResult QuicEndpointBase::Writer::Flush() {
return WriteResult(WRITE_STATUS_OK, 0);
}
QuicEndpointMultiplexer::QuicEndpointMultiplexer(
std::string name,
const std::vector<QuicEndpointBase*>& endpoints)
: Endpoint((*endpoints.begin())->simulator(), name) {
for (QuicEndpointBase* endpoint : endpoints) {
mapping_.insert(std::make_pair(endpoint->name(), endpoint));
}
}
QuicEndpointMultiplexer::~QuicEndpointMultiplexer() {}
void QuicEndpointMultiplexer::AcceptPacket(std::unique_ptr<Packet> packet) {
auto key_value_pair_it = mapping_.find(packet->destination);
if (key_value_pair_it == mapping_.end()) {
return;
}
key_value_pair_it->second->GetRxPort()->AcceptPacket(std::move(packet));
}
UnconstrainedPortInterface* QuicEndpointMultiplexer::GetRxPort() {
return this;
}
void QuicEndpointMultiplexer::SetTxPort(ConstrainedPortInterface* port) {
for (auto& key_value_pair : mapping_) {
key_value_pair.second->SetTxPort(port);
}
}
} // namespace simulator
} // namespace quic