quiche/cid.rs
1// Copyright (C) 2022, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10//
11// * Redistributions in binary form must reproduce the above copyright
12// notice, this list of conditions and the following disclaimer in the
13// documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use crate::Error;
28use crate::Result;
29use std::cmp;
30
31use crate::frame;
32
33use crate::packet::ConnectionId;
34
35use std::collections::HashSet;
36use std::collections::VecDeque;
37
38use smallvec::SmallVec;
39
40/// Used to calculate the cap for the queue of retired connection IDs for which
41/// a RETIRED_CONNECTION_ID frame have not been sent, as a multiple of
42/// `active_conn_id_limit` (see RFC 9000, section 5.1.2).
43const RETIRED_CONN_ID_LIMIT_MULTIPLIER: u64 = 3;
44
45#[derive(Default)]
46struct BoundedConnectionIdSeqSet {
47 /// The inner set.
48 inner: HashSet<u64>,
49
50 /// The maximum number of elements that the set can have.
51 capacity: usize,
52}
53
54impl BoundedConnectionIdSeqSet {
55 /// Creates a set bounded by `capacity`.
56 fn new(capacity: usize) -> Self {
57 Self {
58 inner: HashSet::new(),
59 capacity,
60 }
61 }
62
63 fn insert(&mut self, e: u64) -> Result<bool> {
64 if self.inner.len() >= self.capacity {
65 return Err(Error::IdLimit);
66 }
67
68 Ok(self.inner.insert(e))
69 }
70
71 fn remove(&mut self, e: &u64) -> bool {
72 self.inner.remove(e)
73 }
74
75 fn is_empty(&self) -> bool {
76 self.inner.is_empty()
77 }
78}
79
80/// A structure holding a `ConnectionId` and all its related metadata.
81#[derive(Debug, Default)]
82pub struct ConnectionIdEntry {
83 /// The Connection ID.
84 pub cid: ConnectionId<'static>,
85
86 /// Its associated sequence number.
87 pub seq: u64,
88
89 /// Its associated reset token. Initial CIDs may not have any reset token.
90 pub reset_token: Option<u128>,
91
92 /// The path identifier using this CID, if any.
93 pub path_id: Option<usize>,
94}
95
96#[derive(Default)]
97struct BoundedNonEmptyConnectionIdVecDeque {
98 /// The inner `VecDeque`.
99 inner: VecDeque<ConnectionIdEntry>,
100
101 /// The maximum number of elements that the `VecDeque` can have.
102 capacity: usize,
103}
104
105impl BoundedNonEmptyConnectionIdVecDeque {
106 /// Creates a `VecDeque` bounded by `capacity` and inserts
107 /// `initial_entry` in it.
108 fn new(capacity: usize, initial_entry: ConnectionIdEntry) -> Self {
109 let mut inner = VecDeque::with_capacity(1);
110 inner.push_back(initial_entry);
111 Self { inner, capacity }
112 }
113
114 /// Updates the maximum capacity of the inner `VecDeque` to `new_capacity`.
115 /// Does nothing if `new_capacity` is lower or equal to the current
116 /// `capacity`.
117 fn resize(&mut self, new_capacity: usize) {
118 if new_capacity > self.capacity {
119 self.capacity = new_capacity;
120 }
121 }
122
123 /// Returns the oldest inserted entry still present in the `VecDeque`.
124 fn get_oldest(&self) -> &ConnectionIdEntry {
125 self.inner.front().expect("vecdeque is empty")
126 }
127
128 /// Gets a immutable reference to the entry having the provided `seq`.
129 fn get(&self, seq: u64) -> Option<&ConnectionIdEntry> {
130 // We need to iterate over the whole map to find the key.
131 self.inner.iter().find(|e| e.seq == seq)
132 }
133
134 /// Gets a mutable reference to the entry having the provided `seq`.
135 fn get_mut(&mut self, seq: u64) -> Option<&mut ConnectionIdEntry> {
136 // We need to iterate over the whole map to find the key.
137 self.inner.iter_mut().find(|e| e.seq == seq)
138 }
139
140 /// Returns an iterator over the entries in the `VecDeque`.
141 fn iter(&self) -> impl Iterator<Item = &ConnectionIdEntry> {
142 self.inner.iter()
143 }
144
145 /// Returns the number of elements in the `VecDeque`.
146 fn len(&self) -> usize {
147 self.inner.len()
148 }
149
150 /// Inserts the provided entry in the `VecDeque`.
151 ///
152 /// This method ensures the unicity of the `seq` associated to an entry. If
153 /// an entry has the same `seq` than `e`, this method updates the entry in
154 /// the `VecDeque` and the number of stored elements remains unchanged.
155 ///
156 /// If inserting a new element would exceed the collection's capacity, this
157 /// method raises an [`IdLimit`].
158 ///
159 /// [`IdLimit`]: enum.Error.html#IdLimit
160 fn insert(&mut self, e: ConnectionIdEntry) -> Result<()> {
161 // Ensure we don't have duplicates.
162 match self.get_mut(e.seq) {
163 Some(oe) => *oe = e,
164 None => {
165 if self.inner.len() >= self.capacity {
166 return Err(Error::IdLimit);
167 }
168 self.inner.push_back(e);
169 },
170 };
171 Ok(())
172 }
173
174 /// Removes all the elements in the collection and inserts the provided one.
175 fn clear_and_insert(&mut self, e: ConnectionIdEntry) {
176 self.inner.clear();
177 self.inner.push_back(e);
178 }
179
180 /// Removes the element in the collection having the provided `seq`.
181 ///
182 /// If this method is called when there remains a single element in the
183 /// collection, this method raises an [`OutOfIdentifiers`].
184 ///
185 /// Returns `Some` if the element was in the collection and removed, or
186 /// `None` if it was not and nothing was modified.
187 ///
188 /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
189 fn remove(&mut self, seq: u64) -> Result<Option<ConnectionIdEntry>> {
190 if self.inner.len() <= 1 {
191 return Err(Error::OutOfIdentifiers);
192 }
193
194 Ok(self
195 .inner
196 .iter()
197 .position(|e| e.seq == seq)
198 .and_then(|index| self.inner.remove(index)))
199 }
200}
201
202#[derive(Default)]
203pub struct ConnectionIdentifiers {
204 /// All the Destination Connection IDs provided by our peer.
205 dcids: BoundedNonEmptyConnectionIdVecDeque,
206
207 /// All the Source Connection IDs we provide to our peer.
208 scids: BoundedNonEmptyConnectionIdVecDeque,
209
210 /// Source Connection IDs that should be announced to the peer.
211 advertise_new_scid_seqs: VecDeque<u64>,
212
213 /// Retired Destination Connection IDs that should be announced to the peer.
214 retire_dcid_seqs: BoundedConnectionIdSeqSet,
215
216 /// Retired Source Connection IDs that should be notified to the
217 /// application.
218 retired_scids: VecDeque<ConnectionId<'static>>,
219
220 /// Largest "Retire Prior To" we received from the peer.
221 largest_peer_retire_prior_to: u64,
222
223 /// Largest sequence number we received from the peer.
224 largest_destination_seq: u64,
225
226 /// Next sequence number to use.
227 next_scid_seq: u64,
228
229 /// "Retire Prior To" value to advertise to the peer.
230 retire_prior_to: u64,
231
232 /// The maximum number of source Connection IDs our peer allows us.
233 source_conn_id_limit: usize,
234
235 /// Does the host use zero-length source Connection ID.
236 zero_length_scid: bool,
237
238 /// Does the host use zero-length destination Connection ID.
239 zero_length_dcid: bool,
240}
241
242impl ConnectionIdentifiers {
243 /// Creates a new `ConnectionIdentifiers` with the specified destination
244 /// connection ID limit and initial source Connection ID. The destination
245 /// Connection ID is set to the empty one.
246 pub fn new(
247 mut destination_conn_id_limit: usize, initial_scid: &ConnectionId,
248 initial_path_id: usize, reset_token: Option<u128>,
249 ) -> ConnectionIdentifiers {
250 // It must be at least 2.
251 if destination_conn_id_limit < 2 {
252 destination_conn_id_limit = 2;
253 }
254
255 // Initially, the limit of active source connection IDs is 2.
256 let source_conn_id_limit = 2;
257
258 // Record the zero-length SCID status.
259 let zero_length_scid = initial_scid.is_empty();
260
261 let initial_scid =
262 ConnectionId::from_ref(initial_scid.as_ref()).into_owned();
263
264 // We need to track up to (2 * source_conn_id_limit - 1) source
265 // Connection IDs when the host wants to force their renewal.
266 let scids = BoundedNonEmptyConnectionIdVecDeque::new(
267 2 * source_conn_id_limit - 1,
268 ConnectionIdEntry {
269 cid: initial_scid,
270 seq: 0,
271 reset_token,
272 path_id: Some(initial_path_id),
273 },
274 );
275
276 let dcids = BoundedNonEmptyConnectionIdVecDeque::new(
277 destination_conn_id_limit,
278 ConnectionIdEntry {
279 cid: ConnectionId::default(),
280 seq: 0,
281 reset_token: None,
282 path_id: Some(initial_path_id),
283 },
284 );
285
286 // Guard against overflow.
287 let value =
288 (destination_conn_id_limit as u64) * RETIRED_CONN_ID_LIMIT_MULTIPLIER;
289 let size = cmp::min(usize::MAX as u64, value) as usize;
290 // Because we already inserted the initial SCID.
291 let next_scid_seq = 1;
292 ConnectionIdentifiers {
293 scids,
294 dcids,
295 retire_dcid_seqs: BoundedConnectionIdSeqSet::new(size),
296 next_scid_seq,
297 source_conn_id_limit,
298 zero_length_scid,
299 ..Default::default()
300 }
301 }
302
303 /// Sets the maximum number of source connection IDs our peer allows us.
304 pub fn set_source_conn_id_limit(&mut self, v: u64) {
305 // Bound conn id limit so our scids queue sizing is valid.
306 let v = cmp::min(v, (usize::MAX / 2) as u64) as usize;
307
308 // It must be at least 2.
309 if v >= 2 {
310 self.source_conn_id_limit = v;
311 // We need to track up to (2 * source_conn_id_limit - 1) source
312 // Connection IDs when the host wants to force their renewal.
313 self.scids.resize(2 * v - 1);
314 }
315 }
316
317 /// Gets the destination Connection ID associated with the provided sequence
318 /// number.
319 #[inline]
320 pub fn get_dcid(&self, seq_num: u64) -> Result<&ConnectionIdEntry> {
321 self.dcids.get(seq_num).ok_or(Error::InvalidState)
322 }
323
324 /// Gets the source Connection ID associated with the provided sequence
325 /// number.
326 #[inline]
327 pub fn get_scid(&self, seq_num: u64) -> Result<&ConnectionIdEntry> {
328 self.scids.get(seq_num).ok_or(Error::InvalidState)
329 }
330
331 /// Adds a new source identifier, and indicates whether it should be
332 /// advertised through a `NEW_CONNECTION_ID` frame or not.
333 ///
334 /// At any time, the peer cannot have more Destination Connection IDs than
335 /// the maximum number of active Connection IDs it negotiated. In such case
336 /// (i.e., when [`active_source_cids()`] - `peer_active_conn_id_limit` = 0,
337 /// if the caller agrees to request the removal of previous connection IDs,
338 /// it sets the `retire_if_needed` parameter. Otherwise, an [`IdLimit`] is
339 /// returned.
340 ///
341 /// Note that setting `retire_if_needed` does not prevent this function from
342 /// returning an [`IdLimit`] in the case the caller wants to retire still
343 /// unannounced Connection IDs.
344 ///
345 /// When setting the initial Source Connection ID, the `reset_token` may be
346 /// `None`. However, other Source CIDs must have an associated
347 /// `reset_token`. Providing `None` as the `reset_token` for non-initial
348 /// SCIDs raises an [`InvalidState`].
349 ///
350 /// In the case the provided `cid` is already present, it does not add it.
351 /// If the provided `reset_token` differs from the one already registered,
352 /// returns an `InvalidState`.
353 ///
354 /// Returns the sequence number associated to that new source identifier.
355 ///
356 /// [`active_source_cids()`]: struct.ConnectionIdentifiers.html#method.active_source_cids
357 /// [`InvalidState`]: enum.Error.html#InvalidState
358 /// [`IdLimit`]: enum.Error.html#IdLimit
359 pub fn new_scid(
360 &mut self, cid: ConnectionId<'static>, reset_token: Option<u128>,
361 advertise: bool, path_id: Option<usize>, retire_if_needed: bool,
362 ) -> Result<u64> {
363 if self.zero_length_scid {
364 return Err(Error::InvalidState);
365 }
366
367 // Check whether the number of source Connection IDs does not exceed the
368 // limit. If the host agrees to retire old CIDs, it can store up to
369 // (2 * source_active_conn_id - 1) source CIDs. This limit is enforced
370 // when calling `self.scids.insert()`.
371 if self.scids.len() >= self.source_conn_id_limit {
372 if !retire_if_needed {
373 return Err(Error::IdLimit);
374 }
375
376 // We need to retire the lowest one.
377 self.retire_prior_to = self.lowest_usable_scid_seq()? + 1;
378 }
379
380 let seq = self.next_scid_seq;
381
382 if reset_token.is_none() && seq != 0 {
383 return Err(Error::InvalidState);
384 }
385
386 // Check first that the SCID has not been inserted before.
387 if let Some(e) = self.scids.iter().find(|e| e.cid == cid) {
388 if e.reset_token != reset_token {
389 return Err(Error::InvalidState);
390 }
391 return Ok(e.seq);
392 }
393
394 self.scids.insert(ConnectionIdEntry {
395 cid,
396 seq,
397 reset_token,
398 path_id,
399 })?;
400 self.next_scid_seq += 1;
401
402 self.mark_advertise_new_scid_seq(seq, advertise);
403
404 Ok(seq)
405 }
406
407 /// Sets the initial destination identifier.
408 pub fn set_initial_dcid(
409 &mut self, cid: ConnectionId<'static>, reset_token: Option<u128>,
410 path_id: Option<usize>,
411 ) {
412 // Record the zero-length DCID status.
413 self.zero_length_dcid = cid.is_empty();
414 self.dcids.clear_and_insert(ConnectionIdEntry {
415 cid,
416 seq: 0,
417 reset_token,
418 path_id,
419 });
420 }
421
422 /// Adds a new Destination Connection ID (originating from a
423 /// NEW_CONNECTION_ID frame) and process all its related metadata.
424 ///
425 /// Returns an error if the provided Connection ID or its metadata are
426 /// invalid.
427 ///
428 /// Returns a list of tuples (DCID sequence number, Path ID), containing the
429 /// sequence number of retired DCIDs that were linked to their respective
430 /// Path ID.
431 pub fn new_dcid(
432 &mut self, cid: ConnectionId<'static>, seq: u64, reset_token: u128,
433 retire_prior_to: u64, retired_path_ids: &mut SmallVec<[(u64, usize); 1]>,
434 ) -> Result<()> {
435 if self.zero_length_dcid {
436 return Err(Error::InvalidState);
437 }
438
439 // If an endpoint receives a NEW_CONNECTION_ID frame that repeats a
440 // previously issued connection ID with a different Stateless Reset
441 // Token field value or a different Sequence Number field value, or if a
442 // sequence number is used for different connection IDs, the endpoint
443 // MAY treat that receipt as a connection error of type
444 // PROTOCOL_VIOLATION.
445 if let Some(e) = self.dcids.iter().find(|e| e.cid == cid || e.seq == seq)
446 {
447 if e.cid != cid || e.seq != seq || e.reset_token != Some(reset_token)
448 {
449 return Err(Error::InvalidFrame);
450 }
451 // The identifier is already there, nothing to do.
452 return Ok(());
453 }
454
455 // The value in the Retire Prior To field MUST be less than or equal to
456 // the value in the Sequence Number field. Receiving a value in the
457 // Retire Prior To field that is greater than that in the Sequence
458 // Number field MUST be treated as a connection error of type
459 // FRAME_ENCODING_ERROR.
460 if retire_prior_to > seq {
461 return Err(Error::InvalidFrame);
462 }
463
464 // An endpoint that receives a NEW_CONNECTION_ID frame with a sequence
465 // number smaller than the Retire Prior To field of a previously
466 // received NEW_CONNECTION_ID frame MUST send a corresponding
467 // RETIRE_CONNECTION_ID frame that retires the newly received connection
468 // ID, unless it has already done so for that sequence number.
469 if seq < self.largest_peer_retire_prior_to {
470 self.mark_retire_dcid_seq(seq, true)?;
471 return Ok(());
472 }
473
474 if seq > self.largest_destination_seq {
475 self.largest_destination_seq = seq;
476 }
477
478 let new_entry = ConnectionIdEntry {
479 cid: cid.clone(),
480 seq,
481 reset_token: Some(reset_token),
482 path_id: None,
483 };
484
485 let mut retired_dcid_queue_err = None;
486
487 // A receiver MUST ignore any Retire Prior To fields that do not
488 // increase the largest received Retire Prior To value.
489 //
490 // After processing a NEW_CONNECTION_ID frame and adding and retiring
491 // active connection IDs, if the number of active connection IDs exceeds
492 // the value advertised in its active_connection_id_limit transport
493 // parameter, an endpoint MUST close the connection with an error of type
494 // CONNECTION_ID_LIMIT_ERROR.
495 if retire_prior_to > self.largest_peer_retire_prior_to {
496 let retired = &mut self.retire_dcid_seqs;
497
498 // The insert entry MUST have a sequence higher or equal to the ones
499 // being retired.
500 if new_entry.seq < retire_prior_to {
501 return Err(Error::OutOfIdentifiers);
502 }
503
504 // To avoid exceeding the capacity of the inner `VecDeque`, we first
505 // remove the elements and then insert the new one.
506 let index = self
507 .dcids
508 .inner
509 .partition_point(|e| e.seq < retire_prior_to);
510
511 for e in self.dcids.inner.drain(..index) {
512 if let Some(pid) = e.path_id {
513 retired_path_ids.push((e.seq, pid));
514 }
515
516 if let Err(e) = retired.insert(e.seq) {
517 // Delay propagating the error as we need to try to insert
518 // the new DCID first.
519 retired_dcid_queue_err = Some(e);
520 break;
521 }
522 }
523
524 self.largest_peer_retire_prior_to = retire_prior_to;
525 }
526
527 // Note that if no element has been retired and the `VecDeque` reaches
528 // its capacity limit, this will raise an `IdLimit`.
529 self.dcids.insert(new_entry)?;
530
531 // Propagate the error triggered when inserting a retired DCID seq to
532 // the queue.
533 if let Some(e) = retired_dcid_queue_err {
534 return Err(e);
535 }
536
537 Ok(())
538 }
539
540 /// Retires the Source Connection ID having the provided sequence number.
541 ///
542 /// In case the retired Connection ID is the same as the one used by the
543 /// packet requesting the retiring, or if the retired sequence number is
544 /// greater than any previously advertised sequence numbers, it returns an
545 /// [`InvalidState`].
546 ///
547 /// Returns the path ID that was associated to the retired CID, if any.
548 ///
549 /// [`InvalidState`]: enum.Error.html#InvalidState
550 pub fn retire_scid(
551 &mut self, seq: u64, pkt_dcid: &ConnectionId,
552 ) -> Result<Option<usize>> {
553 if seq >= self.next_scid_seq {
554 return Err(Error::InvalidState);
555 }
556
557 let pid = if let Some(e) = self.scids.remove(seq)? {
558 if e.cid == *pkt_dcid {
559 return Err(Error::InvalidState);
560 }
561
562 // Notifies the application.
563 self.retired_scids.push_back(e.cid);
564
565 // Retiring this SCID may increase the retire prior to.
566 let lowest_scid_seq = self.lowest_usable_scid_seq()?;
567 self.retire_prior_to = lowest_scid_seq;
568
569 e.path_id
570 } else {
571 None
572 };
573
574 Ok(pid)
575 }
576
577 /// Retires the Destination Connection ID having the provided sequence
578 /// number.
579 ///
580 /// If the caller tries to retire the last destination Connection ID, this
581 /// method triggers an [`OutOfIdentifiers`].
582 ///
583 /// If the caller tries to retire a non-existing Destination Connection
584 /// ID sequence number, this method returns an [`InvalidState`].
585 ///
586 /// Returns the path ID that was associated to the retired CID, if any.
587 ///
588 /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
589 /// [`InvalidState`]: enum.Error.html#InvalidState
590 pub fn retire_dcid(&mut self, seq: u64) -> Result<Option<usize>> {
591 if self.zero_length_dcid {
592 return Err(Error::InvalidState);
593 }
594
595 let e = self.dcids.remove(seq)?.ok_or(Error::InvalidState)?;
596
597 self.mark_retire_dcid_seq(seq, true)?;
598
599 Ok(e.path_id)
600 }
601
602 /// Returns an iterator over the source connection IDs.
603 pub fn scids_iter(&self) -> impl Iterator<Item = &ConnectionId<'_>> {
604 self.scids.iter().map(|e| &e.cid)
605 }
606
607 /// Updates the Source Connection ID entry with the provided sequence number
608 /// to indicate that it is now linked to the provided path ID.
609 pub fn link_scid_to_path_id(
610 &mut self, dcid_seq: u64, path_id: usize,
611 ) -> Result<()> {
612 let e = self.scids.get_mut(dcid_seq).ok_or(Error::InvalidState)?;
613 e.path_id = Some(path_id);
614 Ok(())
615 }
616
617 /// Updates the Destination Connection ID entry with the provided sequence
618 /// number to indicate that it is now linked to the provided path ID.
619 pub fn link_dcid_to_path_id(
620 &mut self, dcid_seq: u64, path_id: usize,
621 ) -> Result<()> {
622 let e = self.dcids.get_mut(dcid_seq).ok_or(Error::InvalidState)?;
623 e.path_id = Some(path_id);
624 Ok(())
625 }
626
627 /// Gets the minimum Source Connection ID sequence number whose removal has
628 /// not been requested yet.
629 #[inline]
630 pub fn lowest_usable_scid_seq(&self) -> Result<u64> {
631 self.scids
632 .iter()
633 .filter_map(|e| {
634 if e.seq >= self.retire_prior_to {
635 Some(e.seq)
636 } else {
637 None
638 }
639 })
640 .min()
641 .ok_or(Error::InvalidState)
642 }
643
644 /// Gets the lowest Destination Connection ID sequence number that is not
645 /// associated to a path.
646 #[inline]
647 pub fn lowest_available_dcid_seq(&self) -> Option<u64> {
648 self.dcids
649 .iter()
650 .filter_map(|e| {
651 if e.path_id.is_none() {
652 Some(e.seq)
653 } else {
654 None
655 }
656 })
657 .min()
658 }
659
660 /// Finds the sequence number of the Source Connection ID having the
661 /// provided value and the identifier of the path using it, if any.
662 #[inline]
663 pub fn find_scid_seq(
664 &self, scid: &ConnectionId,
665 ) -> Option<(u64, Option<usize>)> {
666 self.scids.iter().find_map(|e| {
667 if e.cid == *scid {
668 Some((e.seq, e.path_id))
669 } else {
670 None
671 }
672 })
673 }
674
675 /// Returns the number of Source Connection IDs that have not been
676 /// assigned to a path yet.
677 ///
678 /// Note that this function is only meaningful if the host uses non-zero
679 /// length Source Connection IDs.
680 #[inline]
681 pub fn available_scids(&self) -> usize {
682 self.scids.iter().filter(|e| e.path_id.is_none()).count()
683 }
684
685 /// Returns the number of Destination Connection IDs that have not been
686 /// assigned to a path yet.
687 ///
688 /// Note that this function returns 0 if the host uses zero length
689 /// Destination Connection IDs.
690 #[inline]
691 pub fn available_dcids(&self) -> usize {
692 if self.zero_length_dcid() {
693 return 0;
694 }
695 self.dcids.iter().filter(|e| e.path_id.is_none()).count()
696 }
697
698 /// Returns the oldest active source Connection ID of this connection.
699 #[inline]
700 pub fn oldest_scid(&self) -> &ConnectionIdEntry {
701 self.scids.get_oldest()
702 }
703
704 /// Returns the oldest known active destination Connection ID of this
705 /// connection.
706 ///
707 /// Note that due to e.g., reordering at reception side, the oldest known
708 /// active destination Connection ID is not necessarily the one having the
709 /// lowest sequence.
710 #[inline]
711 pub fn oldest_dcid(&self) -> &ConnectionIdEntry {
712 self.dcids.get_oldest()
713 }
714
715 /// Adds or remove the source Connection ID sequence number from the
716 /// source Connection ID set that need to be advertised to the peer through
717 /// NEW_CONNECTION_ID frames.
718 #[inline]
719 pub fn mark_advertise_new_scid_seq(
720 &mut self, scid_seq: u64, advertise: bool,
721 ) {
722 if advertise {
723 self.advertise_new_scid_seqs.push_back(scid_seq);
724 } else if let Some(index) = self
725 .advertise_new_scid_seqs
726 .iter()
727 .position(|s| *s == scid_seq)
728 {
729 self.advertise_new_scid_seqs.remove(index);
730 }
731 }
732
733 /// Adds or remove the destination Connection ID sequence number from the
734 /// retired destination Connection ID set that need to be advertised to the
735 /// peer through RETIRE_CONNECTION_ID frames.
736 #[inline]
737 pub fn mark_retire_dcid_seq(
738 &mut self, dcid_seq: u64, retire: bool,
739 ) -> Result<()> {
740 if retire {
741 self.retire_dcid_seqs.insert(dcid_seq)?;
742 } else {
743 self.retire_dcid_seqs.remove(&dcid_seq);
744 }
745
746 Ok(())
747 }
748
749 /// Gets a source Connection ID's sequence number requiring advertising it
750 /// to the peer through NEW_CONNECTION_ID frame, if any.
751 ///
752 /// If `Some`, it always returns the same value until it has been removed
753 /// using `mark_advertise_new_scid_seq`.
754 #[inline]
755 pub fn next_advertise_new_scid_seq(&self) -> Option<u64> {
756 self.advertise_new_scid_seqs.front().copied()
757 }
758
759 /// Returns a copy of the set of destination Connection IDs's sequence
760 /// numbers to send RETIRE_CONNECTION_ID frames.
761 ///
762 /// Note that the set includes sequence numbers at the time the copy was
763 /// created. To account for newly inserted or removed sequence numbers, a
764 /// new copy needs to be created.
765 #[inline]
766 pub fn retire_dcid_seqs(&self) -> HashSet<u64> {
767 self.retire_dcid_seqs.inner.clone()
768 }
769
770 /// Returns true if there are new source Connection IDs to advertise.
771 #[inline]
772 pub fn has_new_scids(&self) -> bool {
773 !self.advertise_new_scid_seqs.is_empty()
774 }
775
776 /// Returns true if there are retired destination Connection IDs to\
777 /// advertise.
778 #[inline]
779 pub fn has_retire_dcids(&self) -> bool {
780 !self.retire_dcid_seqs.is_empty()
781 }
782
783 /// Returns whether zero-length source CIDs are used.
784 #[inline]
785 pub fn zero_length_scid(&self) -> bool {
786 self.zero_length_scid
787 }
788
789 /// Returns whether zero-length destination CIDs are used.
790 #[inline]
791 pub fn zero_length_dcid(&self) -> bool {
792 self.zero_length_dcid
793 }
794
795 /// Gets the NEW_CONNECTION_ID frame related to the source connection ID
796 /// with sequence `seq_num`.
797 pub fn get_new_connection_id_frame_for(
798 &self, seq_num: u64,
799 ) -> Result<frame::Frame> {
800 let e = self.scids.get(seq_num).ok_or(Error::InvalidState)?;
801 Ok(frame::Frame::NewConnectionId {
802 seq_num,
803 retire_prior_to: self.retire_prior_to,
804 conn_id: e.cid.to_vec(),
805 reset_token: e.reset_token.ok_or(Error::InvalidState)?.to_be_bytes(),
806 })
807 }
808
809 /// Returns the number of source Connection IDs that are active. This is
810 /// only meaningful if the host uses non-zero length Source Connection IDs.
811 #[inline]
812 pub fn active_source_cids(&self) -> usize {
813 self.scids.len()
814 }
815
816 /// Returns the number of source Connection IDs that are retired. This is
817 /// only meaningful if the host uses non-zero length Source Connection IDs.
818 #[inline]
819 pub fn retired_source_cids(&self) -> usize {
820 self.retired_scids.len()
821 }
822
823 pub fn pop_retired_scid(&mut self) -> Option<ConnectionId<'static>> {
824 self.retired_scids.pop_front()
825 }
826}
827
828#[cfg(test)]
829mod tests {
830 use super::*;
831 use crate::test_utils::create_cid_and_reset_token;
832
833 #[test]
834 fn ids_new_scids() {
835 let (scid, _) = create_cid_and_reset_token(16);
836 let (dcid, _) = create_cid_and_reset_token(16);
837
838 let mut ids = ConnectionIdentifiers::new(2, &scid, 0, None);
839 ids.set_source_conn_id_limit(3);
840 ids.set_initial_dcid(dcid, None, Some(0));
841
842 assert_eq!(ids.available_dcids(), 0);
843 assert_eq!(ids.available_scids(), 0);
844 assert!(!ids.has_new_scids());
845 assert_eq!(ids.next_advertise_new_scid_seq(), None);
846
847 let (scid2, rt2) = create_cid_and_reset_token(16);
848
849 assert_eq!(ids.new_scid(scid2, Some(rt2), true, None, false), Ok(1));
850 assert_eq!(ids.available_dcids(), 0);
851 assert_eq!(ids.available_scids(), 1);
852 assert!(ids.has_new_scids());
853 assert_eq!(ids.next_advertise_new_scid_seq(), Some(1));
854
855 let (scid3, rt3) = create_cid_and_reset_token(16);
856
857 assert_eq!(ids.new_scid(scid3, Some(rt3), true, None, false), Ok(2));
858 assert_eq!(ids.available_dcids(), 0);
859 assert_eq!(ids.available_scids(), 2);
860 assert!(ids.has_new_scids());
861 assert_eq!(ids.next_advertise_new_scid_seq(), Some(1));
862
863 // If now we give another CID, it reports an error since it exceeds the
864 // limit of active CIDs.
865 let (scid4, rt4) = create_cid_and_reset_token(16);
866
867 assert_eq!(
868 ids.new_scid(scid4, Some(rt4), true, None, false),
869 Err(Error::IdLimit),
870 );
871 assert_eq!(ids.available_dcids(), 0);
872 assert_eq!(ids.available_scids(), 2);
873 assert!(ids.has_new_scids());
874 assert_eq!(ids.next_advertise_new_scid_seq(), Some(1));
875
876 // Assume we sent one of them.
877 ids.mark_advertise_new_scid_seq(1, false);
878 assert_eq!(ids.available_dcids(), 0);
879 assert_eq!(ids.available_scids(), 2);
880 assert!(ids.has_new_scids());
881 assert_eq!(ids.next_advertise_new_scid_seq(), Some(2));
882
883 // Send the other.
884 ids.mark_advertise_new_scid_seq(2, false);
885
886 assert_eq!(ids.available_dcids(), 0);
887 assert_eq!(ids.available_scids(), 2);
888 assert!(!ids.has_new_scids());
889 assert_eq!(ids.next_advertise_new_scid_seq(), None);
890 }
891
892 #[test]
893 fn new_dcid_event() {
894 let (scid, _) = create_cid_and_reset_token(16);
895 let (dcid, _) = create_cid_and_reset_token(16);
896
897 let mut retired_path_ids = SmallVec::new();
898
899 let mut ids = ConnectionIdentifiers::new(2, &scid, 0, None);
900 ids.set_initial_dcid(dcid, None, Some(0));
901
902 assert_eq!(ids.available_dcids(), 0);
903 assert_eq!(ids.dcids.len(), 1);
904
905 let (dcid2, rt2) = create_cid_and_reset_token(16);
906
907 assert_eq!(
908 ids.new_dcid(dcid2, 1, rt2, 0, &mut retired_path_ids),
909 Ok(()),
910 );
911 assert_eq!(retired_path_ids, SmallVec::from_buf([]));
912 assert_eq!(ids.available_dcids(), 1);
913 assert_eq!(ids.dcids.len(), 2);
914
915 // Now we assume that the client wants to advertise more source
916 // Connection IDs than the advertised limit. This is valid if it
917 // requests its peer to retire enough Connection IDs to fit within the
918 // limits.
919 let (dcid3, rt3) = create_cid_and_reset_token(16);
920 assert_eq!(
921 ids.new_dcid(dcid3, 2, rt3, 1, &mut retired_path_ids),
922 Ok(())
923 );
924 assert_eq!(retired_path_ids, SmallVec::from_buf([(0, 0)]));
925 // The CID module does not handle path replacing. Fake it now.
926 ids.link_dcid_to_path_id(1, 0).unwrap();
927 assert_eq!(ids.available_dcids(), 1);
928 assert_eq!(ids.dcids.len(), 2);
929 assert!(ids.has_retire_dcids());
930 assert_eq!(ids.retire_dcid_seqs().iter().next(), Some(&0));
931
932 // Fake RETIRE_CONNECTION_ID sending.
933 let _ = ids.mark_retire_dcid_seq(0, false);
934 assert!(!ids.has_retire_dcids());
935 assert_eq!(ids.retire_dcid_seqs().iter().next(), None);
936
937 // Now tries to experience CID retirement. If the server tries to remove
938 // non-existing DCIDs, it fails.
939 assert_eq!(ids.retire_dcid(0), Err(Error::InvalidState));
940 assert_eq!(ids.retire_dcid(3), Err(Error::InvalidState));
941 assert!(!ids.has_retire_dcids());
942 assert_eq!(ids.dcids.len(), 2);
943
944 // Now it removes DCID with sequence 1.
945 assert_eq!(ids.retire_dcid(1), Ok(Some(0)));
946 // The CID module does not handle path replacing. Fake it now.
947 ids.link_dcid_to_path_id(2, 0).unwrap();
948 assert_eq!(ids.available_dcids(), 0);
949 assert!(ids.has_retire_dcids());
950 assert_eq!(ids.retire_dcid_seqs().iter().next(), Some(&1));
951 assert_eq!(ids.dcids.len(), 1);
952
953 // Fake RETIRE_CONNECTION_ID sending.
954 let _ = ids.mark_retire_dcid_seq(1, false);
955 assert!(!ids.has_retire_dcids());
956 assert_eq!(ids.retire_dcid_seqs().iter().next(), None);
957
958 // Trying to remove the last DCID triggers an error.
959 assert_eq!(ids.retire_dcid(2), Err(Error::OutOfIdentifiers));
960 assert_eq!(ids.available_dcids(), 0);
961 assert!(!ids.has_retire_dcids());
962 assert_eq!(ids.dcids.len(), 1);
963 }
964
965 #[test]
966 fn new_dcid_reordered() {
967 let (scid, _) = create_cid_and_reset_token(16);
968 let (dcid, _) = create_cid_and_reset_token(16);
969
970 let mut retired_path_ids = SmallVec::new();
971
972 let mut ids = ConnectionIdentifiers::new(2, &scid, 0, None);
973 ids.set_initial_dcid(dcid, None, Some(0));
974
975 assert_eq!(ids.available_dcids(), 0);
976 assert_eq!(ids.dcids.len(), 1);
977
978 // Skip DCID #1 (e.g due to packet loss) and insert DCID #2.
979 let (dcid, rt) = create_cid_and_reset_token(16);
980 assert!(ids.new_dcid(dcid, 2, rt, 1, &mut retired_path_ids).is_ok());
981 assert_eq!(ids.dcids.len(), 1);
982
983 let (dcid, rt) = create_cid_and_reset_token(16);
984 assert!(ids.new_dcid(dcid, 3, rt, 2, &mut retired_path_ids).is_ok());
985 assert_eq!(ids.dcids.len(), 2);
986
987 let (dcid, rt) = create_cid_and_reset_token(16);
988 assert!(ids.new_dcid(dcid, 4, rt, 3, &mut retired_path_ids).is_ok());
989 assert_eq!(ids.dcids.len(), 2);
990
991 // Insert DCID #1 (e.g due to packet reordering).
992 let (dcid, rt) = create_cid_and_reset_token(16);
993 assert!(ids.new_dcid(dcid, 1, rt, 0, &mut retired_path_ids).is_ok());
994 assert_eq!(ids.dcids.len(), 2);
995
996 // Try inserting DCID #1 again (e.g. due to retransmission).
997 let (dcid, rt) = create_cid_and_reset_token(16);
998 assert!(ids.new_dcid(dcid, 1, rt, 0, &mut retired_path_ids).is_ok());
999 assert_eq!(ids.dcids.len(), 2);
1000 }
1001
1002 #[test]
1003 fn new_dcid_partial_retire_prior_to() {
1004 let (scid, _) = create_cid_and_reset_token(16);
1005 let (dcid, _) = create_cid_and_reset_token(16);
1006
1007 let mut retired_path_ids = SmallVec::new();
1008
1009 let mut ids = ConnectionIdentifiers::new(5, &scid, 0, None);
1010 ids.set_initial_dcid(dcid, None, Some(0));
1011
1012 assert_eq!(ids.available_dcids(), 0);
1013 assert_eq!(ids.dcids.len(), 1);
1014
1015 let (dcid, rt) = create_cid_and_reset_token(16);
1016 assert!(ids.new_dcid(dcid, 1, rt, 0, &mut retired_path_ids).is_ok());
1017 assert_eq!(ids.dcids.len(), 2);
1018
1019 let (dcid, rt) = create_cid_and_reset_token(16);
1020 assert!(ids.new_dcid(dcid, 2, rt, 0, &mut retired_path_ids).is_ok());
1021 assert_eq!(ids.dcids.len(), 3);
1022
1023 let (dcid, rt) = create_cid_and_reset_token(16);
1024 assert!(ids.new_dcid(dcid, 3, rt, 0, &mut retired_path_ids).is_ok());
1025 assert_eq!(ids.dcids.len(), 4);
1026
1027 let (dcid, rt) = create_cid_and_reset_token(16);
1028 assert!(ids.new_dcid(dcid, 4, rt, 0, &mut retired_path_ids).is_ok());
1029 assert_eq!(ids.dcids.len(), 5);
1030
1031 // Retire a DCID from the middle of the list
1032 assert!(ids.retire_dcid(3).is_ok());
1033
1034 // Retire prior to DCID that was just retired.
1035 //
1036 // This is largely to test that the `partition_point()` call above
1037 // returns a meaningful value even if the actual sequence that is
1038 // searched isn't present in the list.
1039 let (dcid, rt) = create_cid_and_reset_token(16);
1040 assert!(ids.new_dcid(dcid, 5, rt, 3, &mut retired_path_ids).is_ok());
1041 assert_eq!(ids.dcids.len(), 2);
1042 }
1043
1044 #[test]
1045 fn retire_scids() {
1046 let (scid, _) = create_cid_and_reset_token(16);
1047 let (dcid, _) = create_cid_and_reset_token(16);
1048
1049 let mut ids = ConnectionIdentifiers::new(3, &scid, 0, None);
1050 ids.set_initial_dcid(dcid, None, Some(0));
1051 ids.set_source_conn_id_limit(3);
1052
1053 let (scid2, rt2) = create_cid_and_reset_token(16);
1054 let (scid3, rt3) = create_cid_and_reset_token(16);
1055
1056 assert_eq!(
1057 ids.new_scid(scid2.clone(), Some(rt2), true, None, false),
1058 Ok(1),
1059 );
1060 assert_eq!(ids.scids.len(), 2);
1061 assert_eq!(
1062 ids.new_scid(scid3.clone(), Some(rt3), true, None, false),
1063 Ok(2),
1064 );
1065 assert_eq!(ids.scids.len(), 3);
1066
1067 assert_eq!(ids.pop_retired_scid(), None);
1068
1069 assert_eq!(ids.retire_scid(0, &scid2), Ok(Some(0)));
1070
1071 assert_eq!(ids.pop_retired_scid(), Some(scid));
1072 assert_eq!(ids.pop_retired_scid(), None);
1073
1074 assert_eq!(ids.retire_scid(1, &scid3), Ok(None));
1075
1076 assert_eq!(ids.pop_retired_scid(), Some(scid2));
1077 assert_eq!(ids.pop_retired_scid(), None);
1078 }
1079}