1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/quic_connection_id_manager.h"
6
7 #include <cstdio>
8
9 #include "quiche/quic/core/quic_clock.h"
10 #include "quiche/quic/core/quic_connection_id.h"
11 #include "quiche/quic/core/quic_error_codes.h"
12 #include "quiche/quic/core/quic_utils.h"
13 #include "quiche/quic/platform/api/quic_flag_utils.h"
14 #include "quiche/quic/platform/api/quic_flags.h"
15 #include "quiche/common/platform/api/quiche_logging.h"
16
17 namespace quic {
18
QuicConnectionIdData(const QuicConnectionId & connection_id,uint64_t sequence_number,const StatelessResetToken & stateless_reset_token)19 QuicConnectionIdData::QuicConnectionIdData(
20 const QuicConnectionId& connection_id, uint64_t sequence_number,
21 const StatelessResetToken& stateless_reset_token)
22 : connection_id(connection_id),
23 sequence_number(sequence_number),
24 stateless_reset_token(stateless_reset_token) {}
25
26 namespace {
27
28 class RetirePeerIssuedConnectionIdAlarm
29 : public QuicAlarm::DelegateWithContext {
30 public:
RetirePeerIssuedConnectionIdAlarm(QuicConnectionIdManagerVisitorInterface * visitor,QuicConnectionContext * context)31 explicit RetirePeerIssuedConnectionIdAlarm(
32 QuicConnectionIdManagerVisitorInterface* visitor,
33 QuicConnectionContext* context)
34 : QuicAlarm::DelegateWithContext(context), visitor_(visitor) {}
35 RetirePeerIssuedConnectionIdAlarm(const RetirePeerIssuedConnectionIdAlarm&) =
36 delete;
37 RetirePeerIssuedConnectionIdAlarm& operator=(
38 const RetirePeerIssuedConnectionIdAlarm&) = delete;
39
OnAlarm()40 void OnAlarm() override { visitor_->OnPeerIssuedConnectionIdRetired(); }
41
42 private:
43 QuicConnectionIdManagerVisitorInterface* visitor_;
44 };
45
FindConnectionIdData(const std::vector<QuicConnectionIdData> & cid_data_vector,const QuicConnectionId & cid)46 std::vector<QuicConnectionIdData>::const_iterator FindConnectionIdData(
47 const std::vector<QuicConnectionIdData>& cid_data_vector,
48 const QuicConnectionId& cid) {
49 return std::find_if(cid_data_vector.begin(), cid_data_vector.end(),
50 [&cid](const QuicConnectionIdData& cid_data) {
51 return cid == cid_data.connection_id;
52 });
53 }
54
FindConnectionIdData(std::vector<QuicConnectionIdData> * cid_data_vector,const QuicConnectionId & cid)55 std::vector<QuicConnectionIdData>::iterator FindConnectionIdData(
56 std::vector<QuicConnectionIdData>* cid_data_vector,
57 const QuicConnectionId& cid) {
58 return std::find_if(cid_data_vector->begin(), cid_data_vector->end(),
59 [&cid](const QuicConnectionIdData& cid_data) {
60 return cid == cid_data.connection_id;
61 });
62 }
63
64 } // namespace
65
QuicPeerIssuedConnectionIdManager(size_t active_connection_id_limit,const QuicConnectionId & initial_peer_issued_connection_id,const QuicClock * clock,QuicAlarmFactory * alarm_factory,QuicConnectionIdManagerVisitorInterface * visitor,QuicConnectionContext * context)66 QuicPeerIssuedConnectionIdManager::QuicPeerIssuedConnectionIdManager(
67 size_t active_connection_id_limit,
68 const QuicConnectionId& initial_peer_issued_connection_id,
69 const QuicClock* clock, QuicAlarmFactory* alarm_factory,
70 QuicConnectionIdManagerVisitorInterface* visitor,
71 QuicConnectionContext* context)
72 : active_connection_id_limit_(active_connection_id_limit),
73 clock_(clock),
74 retire_connection_id_alarm_(alarm_factory->CreateAlarm(
75 new RetirePeerIssuedConnectionIdAlarm(visitor, context))) {
76 QUICHE_DCHECK_GE(active_connection_id_limit_, 2u);
77 QUICHE_DCHECK(!initial_peer_issued_connection_id.IsEmpty());
78 active_connection_id_data_.emplace_back<const QuicConnectionId&, uint64_t,
79 const StatelessResetToken&>(
80 initial_peer_issued_connection_id,
81 /*sequence_number=*/0u, {});
82 recent_new_connection_id_sequence_numbers_.Add(0u, 1u);
83 }
84
~QuicPeerIssuedConnectionIdManager()85 QuicPeerIssuedConnectionIdManager::~QuicPeerIssuedConnectionIdManager() {
86 retire_connection_id_alarm_->Cancel();
87 }
88
IsConnectionIdNew(const QuicNewConnectionIdFrame & frame)89 bool QuicPeerIssuedConnectionIdManager::IsConnectionIdNew(
90 const QuicNewConnectionIdFrame& frame) {
91 auto is_old_connection_id = [&frame](const QuicConnectionIdData& cid_data) {
92 return cid_data.connection_id == frame.connection_id;
93 };
94 if (std::any_of(active_connection_id_data_.begin(),
95 active_connection_id_data_.end(), is_old_connection_id)) {
96 return false;
97 }
98 if (std::any_of(unused_connection_id_data_.begin(),
99 unused_connection_id_data_.end(), is_old_connection_id)) {
100 return false;
101 }
102 if (std::any_of(to_be_retired_connection_id_data_.begin(),
103 to_be_retired_connection_id_data_.end(),
104 is_old_connection_id)) {
105 return false;
106 }
107 return true;
108 }
109
PrepareToRetireConnectionIdPriorTo(uint64_t retire_prior_to,std::vector<QuicConnectionIdData> * cid_data_vector)110 void QuicPeerIssuedConnectionIdManager::PrepareToRetireConnectionIdPriorTo(
111 uint64_t retire_prior_to,
112 std::vector<QuicConnectionIdData>* cid_data_vector) {
113 auto it2 = cid_data_vector->begin();
114 for (auto it = cid_data_vector->begin(); it != cid_data_vector->end(); ++it) {
115 if (it->sequence_number >= retire_prior_to) {
116 *it2++ = *it;
117 } else {
118 to_be_retired_connection_id_data_.push_back(*it);
119 if (!retire_connection_id_alarm_->IsSet()) {
120 retire_connection_id_alarm_->Set(clock_->ApproximateNow());
121 }
122 }
123 }
124 cid_data_vector->erase(it2, cid_data_vector->end());
125 }
126
OnNewConnectionIdFrame(const QuicNewConnectionIdFrame & frame,std::string * error_detail)127 QuicErrorCode QuicPeerIssuedConnectionIdManager::OnNewConnectionIdFrame(
128 const QuicNewConnectionIdFrame& frame, std::string* error_detail) {
129 if (recent_new_connection_id_sequence_numbers_.Contains(
130 frame.sequence_number)) {
131 // This frame has a recently seen sequence number. Ignore.
132 return QUIC_NO_ERROR;
133 }
134 if (!IsConnectionIdNew(frame)) {
135 *error_detail =
136 "Received a NEW_CONNECTION_ID frame that reuses a previously seen Id.";
137 return IETF_QUIC_PROTOCOL_VIOLATION;
138 }
139
140 recent_new_connection_id_sequence_numbers_.AddOptimizedForAppend(
141 frame.sequence_number, frame.sequence_number + 1);
142
143 if (recent_new_connection_id_sequence_numbers_.Size() >
144 kMaxNumConnectionIdSequenceNumberIntervals) {
145 *error_detail =
146 "Too many disjoint connection Id sequence number intervals.";
147 return IETF_QUIC_PROTOCOL_VIOLATION;
148 }
149
150 // QuicFramer::ProcessNewConnectionIdFrame guarantees that
151 // frame.sequence_number >= frame.retire_prior_to, and hence there is no need
152 // to check that.
153 if (frame.sequence_number < max_new_connection_id_frame_retire_prior_to_) {
154 // Later frames have asked for retirement of the current frame.
155 to_be_retired_connection_id_data_.emplace_back(frame.connection_id,
156 frame.sequence_number,
157 frame.stateless_reset_token);
158 if (!retire_connection_id_alarm_->IsSet()) {
159 retire_connection_id_alarm_->Set(clock_->ApproximateNow());
160 }
161 return QUIC_NO_ERROR;
162 }
163 if (frame.retire_prior_to > max_new_connection_id_frame_retire_prior_to_) {
164 max_new_connection_id_frame_retire_prior_to_ = frame.retire_prior_to;
165 PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
166 &active_connection_id_data_);
167 PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
168 &unused_connection_id_data_);
169 }
170
171 if (active_connection_id_data_.size() + unused_connection_id_data_.size() >=
172 active_connection_id_limit_) {
173 *error_detail = "Peer provides more connection IDs than the limit.";
174 return QUIC_CONNECTION_ID_LIMIT_ERROR;
175 }
176
177 unused_connection_id_data_.emplace_back(
178 frame.connection_id, frame.sequence_number, frame.stateless_reset_token);
179 return QUIC_NO_ERROR;
180 }
181
182 const QuicConnectionIdData*
ConsumeOneUnusedConnectionId()183 QuicPeerIssuedConnectionIdManager::ConsumeOneUnusedConnectionId() {
184 if (unused_connection_id_data_.empty()) {
185 return nullptr;
186 }
187 active_connection_id_data_.push_back(unused_connection_id_data_.back());
188 unused_connection_id_data_.pop_back();
189 return &active_connection_id_data_.back();
190 }
191
PrepareToRetireActiveConnectionId(const QuicConnectionId & cid)192 void QuicPeerIssuedConnectionIdManager::PrepareToRetireActiveConnectionId(
193 const QuicConnectionId& cid) {
194 auto it = FindConnectionIdData(active_connection_id_data_, cid);
195 if (it == active_connection_id_data_.end()) {
196 // The cid has already been retired.
197 return;
198 }
199 to_be_retired_connection_id_data_.push_back(*it);
200 active_connection_id_data_.erase(it);
201 if (!retire_connection_id_alarm_->IsSet()) {
202 retire_connection_id_alarm_->Set(clock_->ApproximateNow());
203 }
204 }
205
MaybeRetireUnusedConnectionIds(const std::vector<QuicConnectionId> & active_connection_ids_on_path)206 void QuicPeerIssuedConnectionIdManager::MaybeRetireUnusedConnectionIds(
207 const std::vector<QuicConnectionId>& active_connection_ids_on_path) {
208 std::vector<QuicConnectionId> cids_to_retire;
209 for (const auto& cid_data : active_connection_id_data_) {
210 if (std::find(active_connection_ids_on_path.begin(),
211 active_connection_ids_on_path.end(),
212 cid_data.connection_id) ==
213 active_connection_ids_on_path.end()) {
214 cids_to_retire.push_back(cid_data.connection_id);
215 }
216 }
217 for (const auto& cid : cids_to_retire) {
218 PrepareToRetireActiveConnectionId(cid);
219 }
220 }
221
IsConnectionIdActive(const QuicConnectionId & cid) const222 bool QuicPeerIssuedConnectionIdManager::IsConnectionIdActive(
223 const QuicConnectionId& cid) const {
224 return FindConnectionIdData(active_connection_id_data_, cid) !=
225 active_connection_id_data_.end();
226 }
227
228 std::vector<uint64_t> QuicPeerIssuedConnectionIdManager::
ConsumeToBeRetiredConnectionIdSequenceNumbers()229 ConsumeToBeRetiredConnectionIdSequenceNumbers() {
230 std::vector<uint64_t> result;
231 for (auto const& cid_data : to_be_retired_connection_id_data_) {
232 result.push_back(cid_data.sequence_number);
233 }
234 to_be_retired_connection_id_data_.clear();
235 return result;
236 }
237
ReplaceConnectionId(const QuicConnectionId & old_connection_id,const QuicConnectionId & new_connection_id)238 void QuicPeerIssuedConnectionIdManager::ReplaceConnectionId(
239 const QuicConnectionId& old_connection_id,
240 const QuicConnectionId& new_connection_id) {
241 auto it1 =
242 FindConnectionIdData(&active_connection_id_data_, old_connection_id);
243 if (it1 != active_connection_id_data_.end()) {
244 it1->connection_id = new_connection_id;
245 return;
246 }
247 auto it2 = FindConnectionIdData(&to_be_retired_connection_id_data_,
248 old_connection_id);
249 if (it2 != to_be_retired_connection_id_data_.end()) {
250 it2->connection_id = new_connection_id;
251 }
252 }
253
254 namespace {
255
256 class RetireSelfIssuedConnectionIdAlarmDelegate
257 : public QuicAlarm::DelegateWithContext {
258 public:
RetireSelfIssuedConnectionIdAlarmDelegate(QuicSelfIssuedConnectionIdManager * connection_id_manager,QuicConnectionContext * context)259 explicit RetireSelfIssuedConnectionIdAlarmDelegate(
260 QuicSelfIssuedConnectionIdManager* connection_id_manager,
261 QuicConnectionContext* context)
262 : QuicAlarm::DelegateWithContext(context),
263 connection_id_manager_(connection_id_manager) {}
264 RetireSelfIssuedConnectionIdAlarmDelegate(
265 const RetireSelfIssuedConnectionIdAlarmDelegate&) = delete;
266 RetireSelfIssuedConnectionIdAlarmDelegate& operator=(
267 const RetireSelfIssuedConnectionIdAlarmDelegate&) = delete;
268
OnAlarm()269 void OnAlarm() override { connection_id_manager_->RetireConnectionId(); }
270
271 private:
272 QuicSelfIssuedConnectionIdManager* connection_id_manager_;
273 };
274
275 } // namespace
276
QuicSelfIssuedConnectionIdManager(size_t active_connection_id_limit,const QuicConnectionId & initial_connection_id,const QuicClock * clock,QuicAlarmFactory * alarm_factory,QuicConnectionIdManagerVisitorInterface * visitor,QuicConnectionContext * context,ConnectionIdGeneratorInterface & generator)277 QuicSelfIssuedConnectionIdManager::QuicSelfIssuedConnectionIdManager(
278 size_t active_connection_id_limit,
279 const QuicConnectionId& initial_connection_id, const QuicClock* clock,
280 QuicAlarmFactory* alarm_factory,
281 QuicConnectionIdManagerVisitorInterface* visitor,
282 QuicConnectionContext* context, ConnectionIdGeneratorInterface& generator)
283 : active_connection_id_limit_(active_connection_id_limit),
284 clock_(clock),
285 visitor_(visitor),
286 retire_connection_id_alarm_(alarm_factory->CreateAlarm(
287 new RetireSelfIssuedConnectionIdAlarmDelegate(this, context))),
288 last_connection_id_(initial_connection_id),
289 next_connection_id_sequence_number_(1u),
290 last_connection_id_consumed_by_self_sequence_number_(0u),
291 connection_id_generator_(generator) {
292 active_connection_ids_.emplace_back(initial_connection_id, 0u);
293 }
294
~QuicSelfIssuedConnectionIdManager()295 QuicSelfIssuedConnectionIdManager::~QuicSelfIssuedConnectionIdManager() {
296 retire_connection_id_alarm_->Cancel();
297 }
298
299 absl::optional<QuicNewConnectionIdFrame>
MaybeIssueNewConnectionId()300 QuicSelfIssuedConnectionIdManager::MaybeIssueNewConnectionId() {
301 const bool check_cid_collision_when_issue_new_cid =
302 GetQuicReloadableFlag(quic_check_cid_collision_when_issue_new_cid);
303 absl::optional<QuicConnectionId> new_cid =
304 connection_id_generator_.GenerateNextConnectionId(last_connection_id_);
305 if (!new_cid.has_value()) {
306 return {};
307 }
308 if (check_cid_collision_when_issue_new_cid) {
309 QUIC_RELOADABLE_FLAG_COUNT_N(quic_check_cid_collision_when_issue_new_cid, 1,
310 2);
311 if (!visitor_->MaybeReserveConnectionId(*new_cid)) {
312 QUIC_RELOADABLE_FLAG_COUNT_N(quic_check_cid_collision_when_issue_new_cid,
313 2, 2);
314 return {};
315 }
316 }
317 QuicNewConnectionIdFrame frame;
318 frame.connection_id = *new_cid;
319 frame.sequence_number = next_connection_id_sequence_number_++;
320 frame.stateless_reset_token =
321 QuicUtils::GenerateStatelessResetToken(frame.connection_id);
322 if (!check_cid_collision_when_issue_new_cid) {
323 visitor_->MaybeReserveConnectionId(frame.connection_id);
324 }
325 active_connection_ids_.emplace_back(frame.connection_id,
326 frame.sequence_number);
327 frame.retire_prior_to = active_connection_ids_.front().second;
328 last_connection_id_ = frame.connection_id;
329 return frame;
330 }
331
332 absl::optional<QuicNewConnectionIdFrame> QuicSelfIssuedConnectionIdManager::
MaybeIssueNewConnectionIdForPreferredAddress()333 MaybeIssueNewConnectionIdForPreferredAddress() {
334 absl::optional<QuicNewConnectionIdFrame> frame = MaybeIssueNewConnectionId();
335 QUICHE_DCHECK(!frame.has_value() || (frame->sequence_number == 1u));
336 return frame;
337 }
338
OnRetireConnectionIdFrame(const QuicRetireConnectionIdFrame & frame,QuicTime::Delta pto_delay,std::string * error_detail)339 QuicErrorCode QuicSelfIssuedConnectionIdManager::OnRetireConnectionIdFrame(
340 const QuicRetireConnectionIdFrame& frame, QuicTime::Delta pto_delay,
341 std::string* error_detail) {
342 QUICHE_DCHECK(!active_connection_ids_.empty());
343 if (GetQuicReloadableFlag(
344 quic_check_retire_cid_with_next_cid_sequence_number)) {
345 QUIC_RELOADABLE_FLAG_COUNT(
346 quic_check_retire_cid_with_next_cid_sequence_number);
347 if (frame.sequence_number >= next_connection_id_sequence_number_) {
348 *error_detail = "To be retired connecton ID is never issued.";
349 return IETF_QUIC_PROTOCOL_VIOLATION;
350 }
351 } else {
352 if (frame.sequence_number > active_connection_ids_.back().second) {
353 *error_detail = "To be retired connecton ID is never issued.";
354 return IETF_QUIC_PROTOCOL_VIOLATION;
355 }
356 }
357
358 auto it =
359 std::find_if(active_connection_ids_.begin(), active_connection_ids_.end(),
360 [&frame](const std::pair<QuicConnectionId, uint64_t>& p) {
361 return p.second == frame.sequence_number;
362 });
363 // The corresponding connection ID has been retired. Ignore.
364 if (it == active_connection_ids_.end()) {
365 return QUIC_NO_ERROR;
366 }
367
368 if (to_be_retired_connection_ids_.size() + active_connection_ids_.size() >=
369 kMaxNumConnectonIdsInUse) {
370 // Close connection if the number of connection IDs in use will exeed the
371 // limit, i.e., peer retires connection ID too fast.
372 *error_detail = "There are too many connection IDs in use.";
373 return QUIC_TOO_MANY_CONNECTION_ID_WAITING_TO_RETIRE;
374 }
375
376 QuicTime retirement_time = clock_->ApproximateNow() + 3 * pto_delay;
377 if (!to_be_retired_connection_ids_.empty()) {
378 retirement_time =
379 std::max(retirement_time, to_be_retired_connection_ids_.back().second);
380 }
381
382 to_be_retired_connection_ids_.emplace_back(it->first, retirement_time);
383 if (!retire_connection_id_alarm_->IsSet()) {
384 retire_connection_id_alarm_->Set(retirement_time);
385 }
386
387 active_connection_ids_.erase(it);
388 MaybeSendNewConnectionIds();
389
390 return QUIC_NO_ERROR;
391 }
392
393 std::vector<QuicConnectionId>
GetUnretiredConnectionIds() const394 QuicSelfIssuedConnectionIdManager::GetUnretiredConnectionIds() const {
395 std::vector<QuicConnectionId> unretired_ids;
396 for (const auto& cid_pair : to_be_retired_connection_ids_) {
397 unretired_ids.push_back(cid_pair.first);
398 }
399 for (const auto& cid_pair : active_connection_ids_) {
400 unretired_ids.push_back(cid_pair.first);
401 }
402 return unretired_ids;
403 }
404
GetOneActiveConnectionId() const405 QuicConnectionId QuicSelfIssuedConnectionIdManager::GetOneActiveConnectionId()
406 const {
407 QUICHE_DCHECK(!active_connection_ids_.empty());
408 return active_connection_ids_.front().first;
409 }
410
RetireConnectionId()411 void QuicSelfIssuedConnectionIdManager::RetireConnectionId() {
412 if (to_be_retired_connection_ids_.empty()) {
413 QUIC_BUG(quic_bug_12420_1)
414 << "retire_connection_id_alarm fired but there is no connection ID "
415 "to be retired.";
416 return;
417 }
418 QuicTime now = clock_->ApproximateNow();
419 auto it = to_be_retired_connection_ids_.begin();
420 do {
421 visitor_->OnSelfIssuedConnectionIdRetired(it->first);
422 ++it;
423 } while (it != to_be_retired_connection_ids_.end() && it->second <= now);
424 to_be_retired_connection_ids_.erase(to_be_retired_connection_ids_.begin(),
425 it);
426 // Set the alarm again if there is another connection ID to be removed.
427 if (!to_be_retired_connection_ids_.empty()) {
428 retire_connection_id_alarm_->Set(
429 to_be_retired_connection_ids_.front().second);
430 }
431 }
432
MaybeSendNewConnectionIds()433 void QuicSelfIssuedConnectionIdManager::MaybeSendNewConnectionIds() {
434 while (active_connection_ids_.size() < active_connection_id_limit_) {
435 absl::optional<QuicNewConnectionIdFrame> frame =
436 MaybeIssueNewConnectionId();
437 if (!frame.has_value()) {
438 break;
439 }
440 if (!visitor_->SendNewConnectionId(*frame)) {
441 break;
442 }
443 }
444 }
445
HasConnectionIdToConsume() const446 bool QuicSelfIssuedConnectionIdManager::HasConnectionIdToConsume() const {
447 for (const auto& active_cid_data : active_connection_ids_) {
448 if (active_cid_data.second >
449 last_connection_id_consumed_by_self_sequence_number_) {
450 return true;
451 }
452 }
453 return false;
454 }
455
456 absl::optional<QuicConnectionId>
ConsumeOneConnectionId()457 QuicSelfIssuedConnectionIdManager::ConsumeOneConnectionId() {
458 for (const auto& active_cid_data : active_connection_ids_) {
459 if (active_cid_data.second >
460 last_connection_id_consumed_by_self_sequence_number_) {
461 // Since connection IDs in active_connection_ids_ has monotonically
462 // increasing sequence numbers, the returned connection ID has the
463 // smallest sequence number among all unconsumed active connection IDs.
464 last_connection_id_consumed_by_self_sequence_number_ =
465 active_cid_data.second;
466 return active_cid_data.first;
467 }
468 }
469 return absl::nullopt;
470 }
471
IsConnectionIdInUse(const QuicConnectionId & cid) const472 bool QuicSelfIssuedConnectionIdManager::IsConnectionIdInUse(
473 const QuicConnectionId& cid) const {
474 for (const auto& active_cid_data : active_connection_ids_) {
475 if (active_cid_data.first == cid) {
476 return true;
477 }
478 }
479 for (const auto& to_be_retired_cid_data : to_be_retired_connection_ids_) {
480 if (to_be_retired_cid_data.first == cid) {
481 return true;
482 }
483 }
484 return false;
485 }
486
487 } // namespace quic
488