blob: 253d3bd377e6132e252a7873919ffe255d9bd605 [file] [log] [blame] [edit]
// 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 "quiche/quic/core/quic_connection_id_manager.h"
#include <algorithm>
#include <cstdio>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "quiche/quic/core/quic_clock.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/common/platform/api/quiche_logging.h"
namespace quic {
QuicConnectionIdData::QuicConnectionIdData(
const QuicConnectionId& connection_id, uint64_t sequence_number,
const StatelessResetToken& stateless_reset_token)
: connection_id(connection_id),
sequence_number(sequence_number),
stateless_reset_token(stateless_reset_token) {}
namespace {
class RetirePeerIssuedConnectionIdAlarm
: public QuicAlarm::DelegateWithContext {
public:
explicit RetirePeerIssuedConnectionIdAlarm(
QuicConnectionIdManagerVisitorInterface* visitor,
QuicConnectionContext* context)
: QuicAlarm::DelegateWithContext(context), visitor_(visitor) {}
RetirePeerIssuedConnectionIdAlarm(const RetirePeerIssuedConnectionIdAlarm&) =
delete;
RetirePeerIssuedConnectionIdAlarm& operator=(
const RetirePeerIssuedConnectionIdAlarm&) = delete;
void OnAlarm() override { visitor_->OnPeerIssuedConnectionIdRetired(); }
private:
QuicConnectionIdManagerVisitorInterface* visitor_;
};
std::vector<QuicConnectionIdData>::const_iterator FindConnectionIdData(
const std::vector<QuicConnectionIdData>& cid_data_vector,
const QuicConnectionId& cid) {
return std::find_if(cid_data_vector.begin(), cid_data_vector.end(),
[&cid](const QuicConnectionIdData& cid_data) {
return cid == cid_data.connection_id;
});
}
std::vector<QuicConnectionIdData>::iterator FindConnectionIdData(
std::vector<QuicConnectionIdData>* cid_data_vector,
const QuicConnectionId& cid) {
return std::find_if(cid_data_vector->begin(), cid_data_vector->end(),
[&cid](const QuicConnectionIdData& cid_data) {
return cid == cid_data.connection_id;
});
}
} // namespace
QuicPeerIssuedConnectionIdManager::QuicPeerIssuedConnectionIdManager(
size_t active_connection_id_limit,
const QuicConnectionId& initial_peer_issued_connection_id,
const QuicClock* clock, QuicAlarmFactory* alarm_factory,
QuicConnectionIdManagerVisitorInterface* visitor,
QuicConnectionContext* context)
: active_connection_id_limit_(active_connection_id_limit),
clock_(clock),
retire_connection_id_alarm_(alarm_factory->CreateAlarm(
new RetirePeerIssuedConnectionIdAlarm(visitor, context))) {
QUICHE_DCHECK_GE(active_connection_id_limit_, 2u);
QUICHE_DCHECK(!initial_peer_issued_connection_id.IsEmpty());
active_connection_id_data_.emplace_back<const QuicConnectionId&, uint64_t,
const StatelessResetToken&>(
initial_peer_issued_connection_id,
/*sequence_number=*/0u, {});
recent_new_connection_id_sequence_numbers_.Add(0u, 1u);
}
QuicPeerIssuedConnectionIdManager::~QuicPeerIssuedConnectionIdManager() {
retire_connection_id_alarm_->Cancel();
}
bool QuicPeerIssuedConnectionIdManager::IsConnectionIdNew(
const QuicNewConnectionIdFrame& frame) {
auto is_old_connection_id = [&frame](const QuicConnectionIdData& cid_data) {
return cid_data.connection_id == frame.connection_id;
};
if (std::any_of(active_connection_id_data_.begin(),
active_connection_id_data_.end(), is_old_connection_id)) {
return false;
}
if (std::any_of(unused_connection_id_data_.begin(),
unused_connection_id_data_.end(), is_old_connection_id)) {
return false;
}
if (std::any_of(to_be_retired_connection_id_data_.begin(),
to_be_retired_connection_id_data_.end(),
is_old_connection_id)) {
return false;
}
return true;
}
void QuicPeerIssuedConnectionIdManager::PrepareToRetireConnectionIdPriorTo(
uint64_t retire_prior_to,
std::vector<QuicConnectionIdData>* cid_data_vector) {
auto it2 = cid_data_vector->begin();
for (auto it = cid_data_vector->begin(); it != cid_data_vector->end(); ++it) {
if (it->sequence_number >= retire_prior_to) {
*it2++ = *it;
} else {
to_be_retired_connection_id_data_.push_back(*it);
if (!retire_connection_id_alarm_->IsSet()) {
retire_connection_id_alarm_->Set(clock_->ApproximateNow());
}
}
}
cid_data_vector->erase(it2, cid_data_vector->end());
}
QuicErrorCode QuicPeerIssuedConnectionIdManager::OnNewConnectionIdFrame(
const QuicNewConnectionIdFrame& frame, std::string* error_detail,
bool* is_duplicate_frame) {
if (recent_new_connection_id_sequence_numbers_.Contains(
frame.sequence_number)) {
// This frame has a recently seen sequence number. Ignore.
*is_duplicate_frame = true;
return QUIC_NO_ERROR;
}
if (!IsConnectionIdNew(frame)) {
*error_detail =
"Received a NEW_CONNECTION_ID frame that reuses a previously seen Id.";
return IETF_QUIC_PROTOCOL_VIOLATION;
}
recent_new_connection_id_sequence_numbers_.AddOptimizedForAppend(
frame.sequence_number, frame.sequence_number + 1);
if (recent_new_connection_id_sequence_numbers_.Size() >
kMaxNumConnectionIdSequenceNumberIntervals) {
*error_detail =
"Too many disjoint connection Id sequence number intervals.";
return IETF_QUIC_PROTOCOL_VIOLATION;
}
// QuicFramer::ProcessNewConnectionIdFrame guarantees that
// frame.sequence_number >= frame.retire_prior_to, and hence there is no need
// to check that.
if (frame.sequence_number < max_new_connection_id_frame_retire_prior_to_) {
// Later frames have asked for retirement of the current frame.
to_be_retired_connection_id_data_.emplace_back(frame.connection_id,
frame.sequence_number,
frame.stateless_reset_token);
if (!retire_connection_id_alarm_->IsSet()) {
retire_connection_id_alarm_->Set(clock_->ApproximateNow());
}
return QUIC_NO_ERROR;
}
if (frame.retire_prior_to > max_new_connection_id_frame_retire_prior_to_) {
max_new_connection_id_frame_retire_prior_to_ = frame.retire_prior_to;
PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
&active_connection_id_data_);
PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
&unused_connection_id_data_);
}
if (active_connection_id_data_.size() + unused_connection_id_data_.size() >=
active_connection_id_limit_) {
*error_detail = "Peer provides more connection IDs than the limit.";
return QUIC_CONNECTION_ID_LIMIT_ERROR;
}
unused_connection_id_data_.emplace_back(
frame.connection_id, frame.sequence_number, frame.stateless_reset_token);
return QUIC_NO_ERROR;
}
const QuicConnectionIdData*
QuicPeerIssuedConnectionIdManager::ConsumeOneUnusedConnectionId() {
if (unused_connection_id_data_.empty()) {
return nullptr;
}
active_connection_id_data_.push_back(unused_connection_id_data_.back());
unused_connection_id_data_.pop_back();
return &active_connection_id_data_.back();
}
void QuicPeerIssuedConnectionIdManager::PrepareToRetireActiveConnectionId(
const QuicConnectionId& cid) {
auto it = FindConnectionIdData(active_connection_id_data_, cid);
if (it == active_connection_id_data_.end()) {
// The cid has already been retired.
return;
}
to_be_retired_connection_id_data_.push_back(*it);
active_connection_id_data_.erase(it);
if (!retire_connection_id_alarm_->IsSet()) {
retire_connection_id_alarm_->Set(clock_->ApproximateNow());
}
}
void QuicPeerIssuedConnectionIdManager::MaybeRetireUnusedConnectionIds(
const std::vector<QuicConnectionId>& active_connection_ids_on_path) {
std::vector<QuicConnectionId> cids_to_retire;
for (const auto& cid_data : active_connection_id_data_) {
if (std::find(active_connection_ids_on_path.begin(),
active_connection_ids_on_path.end(),
cid_data.connection_id) ==
active_connection_ids_on_path.end()) {
cids_to_retire.push_back(cid_data.connection_id);
}
}
for (const auto& cid : cids_to_retire) {
PrepareToRetireActiveConnectionId(cid);
}
}
bool QuicPeerIssuedConnectionIdManager::IsConnectionIdActive(
const QuicConnectionId& cid) const {
return FindConnectionIdData(active_connection_id_data_, cid) !=
active_connection_id_data_.end();
}
std::vector<uint64_t> QuicPeerIssuedConnectionIdManager::
ConsumeToBeRetiredConnectionIdSequenceNumbers() {
std::vector<uint64_t> result;
for (auto const& cid_data : to_be_retired_connection_id_data_) {
result.push_back(cid_data.sequence_number);
}
to_be_retired_connection_id_data_.clear();
return result;
}
void QuicPeerIssuedConnectionIdManager::ReplaceConnectionId(
const QuicConnectionId& old_connection_id,
const QuicConnectionId& new_connection_id) {
auto it1 =
FindConnectionIdData(&active_connection_id_data_, old_connection_id);
if (it1 != active_connection_id_data_.end()) {
it1->connection_id = new_connection_id;
return;
}
auto it2 = FindConnectionIdData(&to_be_retired_connection_id_data_,
old_connection_id);
if (it2 != to_be_retired_connection_id_data_.end()) {
it2->connection_id = new_connection_id;
}
}
namespace {
class RetireSelfIssuedConnectionIdAlarmDelegate
: public QuicAlarm::DelegateWithContext {
public:
explicit RetireSelfIssuedConnectionIdAlarmDelegate(
QuicSelfIssuedConnectionIdManager* connection_id_manager,
QuicConnectionContext* context)
: QuicAlarm::DelegateWithContext(context),
connection_id_manager_(connection_id_manager) {}
RetireSelfIssuedConnectionIdAlarmDelegate(
const RetireSelfIssuedConnectionIdAlarmDelegate&) = delete;
RetireSelfIssuedConnectionIdAlarmDelegate& operator=(
const RetireSelfIssuedConnectionIdAlarmDelegate&) = delete;
void OnAlarm() override { connection_id_manager_->RetireConnectionId(); }
private:
QuicSelfIssuedConnectionIdManager* connection_id_manager_;
};
} // namespace
QuicSelfIssuedConnectionIdManager::QuicSelfIssuedConnectionIdManager(
size_t active_connection_id_limit,
const QuicConnectionId& initial_connection_id, const QuicClock* clock,
QuicAlarmFactory* alarm_factory,
QuicConnectionIdManagerVisitorInterface* visitor,
QuicConnectionContext* context, ConnectionIdGeneratorInterface& generator)
: active_connection_id_limit_(active_connection_id_limit),
clock_(clock),
visitor_(visitor),
retire_connection_id_alarm_(alarm_factory->CreateAlarm(
new RetireSelfIssuedConnectionIdAlarmDelegate(this, context))),
last_connection_id_(initial_connection_id),
next_connection_id_sequence_number_(1u),
last_connection_id_consumed_by_self_sequence_number_(0u),
connection_id_generator_(generator) {
active_connection_ids_.emplace_back(initial_connection_id, 0u);
}
QuicSelfIssuedConnectionIdManager::~QuicSelfIssuedConnectionIdManager() {
retire_connection_id_alarm_->Cancel();
}
std::optional<QuicNewConnectionIdFrame>
QuicSelfIssuedConnectionIdManager::MaybeIssueNewConnectionId() {
std::optional<QuicConnectionId> new_cid =
connection_id_generator_.GenerateNextConnectionId(last_connection_id_);
if (!new_cid.has_value()) {
return {};
}
if (!visitor_->MaybeReserveConnectionId(*new_cid)) {
return {};
}
QuicNewConnectionIdFrame frame;
frame.connection_id = *new_cid;
frame.sequence_number = next_connection_id_sequence_number_++;
frame.stateless_reset_token =
QuicUtils::GenerateStatelessResetToken(frame.connection_id);
active_connection_ids_.emplace_back(frame.connection_id,
frame.sequence_number);
frame.retire_prior_to = active_connection_ids_.front().second;
last_connection_id_ = frame.connection_id;
return frame;
}
std::optional<QuicNewConnectionIdFrame> QuicSelfIssuedConnectionIdManager::
MaybeIssueNewConnectionIdForPreferredAddress() {
std::optional<QuicNewConnectionIdFrame> frame = MaybeIssueNewConnectionId();
QUICHE_DCHECK(!frame.has_value() || (frame->sequence_number == 1u));
return frame;
}
QuicErrorCode QuicSelfIssuedConnectionIdManager::OnRetireConnectionIdFrame(
const QuicRetireConnectionIdFrame& frame, QuicTime::Delta pto_delay,
std::string* error_detail) {
QUICHE_DCHECK(!active_connection_ids_.empty());
if (frame.sequence_number >= next_connection_id_sequence_number_) {
*error_detail = "To be retired connecton ID is never issued.";
return IETF_QUIC_PROTOCOL_VIOLATION;
}
auto it =
std::find_if(active_connection_ids_.begin(), active_connection_ids_.end(),
[&frame](const std::pair<QuicConnectionId, uint64_t>& p) {
return p.second == frame.sequence_number;
});
// The corresponding connection ID has been retired. Ignore.
if (it == active_connection_ids_.end()) {
return QUIC_NO_ERROR;
}
if (to_be_retired_connection_ids_.size() + active_connection_ids_.size() >=
kMaxNumConnectonIdsInUse) {
// Close connection if the number of connection IDs in use will exeed the
// limit, i.e., peer retires connection ID too fast.
*error_detail = "There are too many connection IDs in use.";
return QUIC_TOO_MANY_CONNECTION_ID_WAITING_TO_RETIRE;
}
QuicTime retirement_time = clock_->ApproximateNow() + 3 * pto_delay;
if (!to_be_retired_connection_ids_.empty()) {
retirement_time =
std::max(retirement_time, to_be_retired_connection_ids_.back().second);
}
to_be_retired_connection_ids_.emplace_back(it->first, retirement_time);
if (!retire_connection_id_alarm_->IsSet()) {
retire_connection_id_alarm_->Set(retirement_time);
}
active_connection_ids_.erase(it);
MaybeSendNewConnectionIds();
return QUIC_NO_ERROR;
}
std::vector<QuicConnectionId>
QuicSelfIssuedConnectionIdManager::GetUnretiredConnectionIds() const {
std::vector<QuicConnectionId> unretired_ids;
for (const auto& cid_pair : to_be_retired_connection_ids_) {
unretired_ids.push_back(cid_pair.first);
}
for (const auto& cid_pair : active_connection_ids_) {
unretired_ids.push_back(cid_pair.first);
}
return unretired_ids;
}
QuicConnectionId QuicSelfIssuedConnectionIdManager::GetOneActiveConnectionId()
const {
QUICHE_DCHECK(!active_connection_ids_.empty());
return active_connection_ids_.front().first;
}
void QuicSelfIssuedConnectionIdManager::RetireConnectionId() {
if (to_be_retired_connection_ids_.empty()) {
QUIC_BUG(quic_bug_12420_1)
<< "retire_connection_id_alarm fired but there is no connection ID "
"to be retired.";
return;
}
QuicTime now = clock_->ApproximateNow();
auto it = to_be_retired_connection_ids_.begin();
do {
visitor_->OnSelfIssuedConnectionIdRetired(it->first);
++it;
} while (it != to_be_retired_connection_ids_.end() && it->second <= now);
to_be_retired_connection_ids_.erase(to_be_retired_connection_ids_.begin(),
it);
// Set the alarm again if there is another connection ID to be removed.
if (!to_be_retired_connection_ids_.empty()) {
retire_connection_id_alarm_->Set(
to_be_retired_connection_ids_.front().second);
}
}
void QuicSelfIssuedConnectionIdManager::MaybeSendNewConnectionIds() {
while (active_connection_ids_.size() < active_connection_id_limit_) {
std::optional<QuicNewConnectionIdFrame> frame = MaybeIssueNewConnectionId();
if (!frame.has_value()) {
break;
}
if (!visitor_->SendNewConnectionId(*frame)) {
break;
}
}
}
bool QuicSelfIssuedConnectionIdManager::HasConnectionIdToConsume() const {
for (const auto& active_cid_data : active_connection_ids_) {
if (active_cid_data.second >
last_connection_id_consumed_by_self_sequence_number_) {
return true;
}
}
return false;
}
std::optional<QuicConnectionId>
QuicSelfIssuedConnectionIdManager::ConsumeOneConnectionId() {
for (const auto& active_cid_data : active_connection_ids_) {
if (active_cid_data.second >
last_connection_id_consumed_by_self_sequence_number_) {
// Since connection IDs in active_connection_ids_ has monotonically
// increasing sequence numbers, the returned connection ID has the
// smallest sequence number among all unconsumed active connection IDs.
last_connection_id_consumed_by_self_sequence_number_ =
active_cid_data.second;
return active_cid_data.first;
}
}
return std::nullopt;
}
bool QuicSelfIssuedConnectionIdManager::IsConnectionIdInUse(
const QuicConnectionId& cid) const {
for (const auto& active_cid_data : active_connection_ids_) {
if (active_cid_data.first == cid) {
return true;
}
}
for (const auto& to_be_retired_cid_data : to_be_retired_connection_ids_) {
if (to_be_retired_cid_data.first == cid) {
return true;
}
}
return false;
}
} // namespace quic