|  | // 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 <cstdio> | 
|  |  | 
|  | #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 |