• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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