Skip to main content

quiche/
path.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 std::collections::BTreeMap;
28use std::collections::VecDeque;
29
30use std::net::SocketAddr;
31
32use std::time::Duration;
33use std::time::Instant;
34
35use smallvec::SmallVec;
36
37use slab::Slab;
38
39use crate::Config;
40use crate::Error;
41use crate::Result;
42use crate::StartupExit;
43
44use crate::pmtud;
45use crate::recovery;
46use crate::recovery::Bandwidth;
47use crate::recovery::HandshakeStatus;
48use crate::recovery::OnLossDetectionTimeoutOutcome;
49use crate::recovery::RecoveryOps;
50
51/// The different states of the path validation.
52#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
53pub enum PathState {
54    /// The path failed its validation.
55    Failed,
56
57    /// The path exists, but no path validation has been performed.
58    Unknown,
59
60    /// The path is under validation.
61    Validating,
62
63    /// The remote address has been validated, but not the path MTU.
64    ValidatingMTU,
65
66    /// The path has been validated.
67    Validated,
68}
69
70impl PathState {
71    #[cfg(feature = "ffi")]
72    pub fn to_c(self) -> libc::ssize_t {
73        match self {
74            PathState::Failed => -1,
75            PathState::Unknown => 0,
76            PathState::Validating => 1,
77            PathState::ValidatingMTU => 2,
78            PathState::Validated => 3,
79        }
80    }
81}
82
83/// A path-specific event.
84#[derive(Clone, Debug, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum PathEvent {
87    /// A new network path (local address, peer address) has been seen on a
88    /// received packet. Note that this event is only triggered for servers, as
89    /// the client is responsible from initiating new paths. The application may
90    /// then probe this new path, if desired.
91    New(SocketAddr, SocketAddr),
92
93    /// The related network path between local `SocketAddr` and peer
94    /// `SocketAddr` has been validated.
95    Validated(SocketAddr, SocketAddr),
96
97    /// The related network path between local `SocketAddr` and peer
98    /// `SocketAddr` failed to be validated. This network path will not be used
99    /// anymore, unless the application requests probing this path again.
100    FailedValidation(SocketAddr, SocketAddr),
101
102    /// The related network path between local `SocketAddr` and peer
103    /// `SocketAddr` has been closed and is now unusable on this connection.
104    Closed(SocketAddr, SocketAddr),
105
106    /// The stack observes that the Source Connection ID with the given sequence
107    /// number, initially used by the peer over the first pair of `SocketAddr`s,
108    /// is now reused over the second pair of `SocketAddr`s.
109    ReusedSourceConnectionId(
110        u64,
111        (SocketAddr, SocketAddr),
112        (SocketAddr, SocketAddr),
113    ),
114
115    /// The connection observed that the peer migrated over the network path
116    /// denoted by the pair of `SocketAddr`, i.e., non-probing packets have been
117    /// received on this network path. This is a server side only event.
118    ///
119    /// Note that this event is only raised if the path has been validated.
120    PeerMigrated(SocketAddr, SocketAddr),
121
122    /// The validated PMTU available for normal application traffic changed on
123    /// the network path between `local` and `peer`.
124    ///
125    /// This event is generated only while PMTUD is enabled. It reports the
126    /// largest successfully probed size, falling back to QUIC's minimum packet
127    /// size while no larger size is validated. Unvalidated probe sizes and the
128    /// initial minimum set when the path is created are not reported.
129    PmtuUpdated {
130        /// The path's local address.
131        local: SocketAddr,
132
133        /// The path's peer address.
134        peer: SocketAddr,
135
136        /// The current validated PMTU limit for normal application traffic.
137        pmtu: usize,
138    },
139}
140
141pub(crate) fn pmtu_event(
142    local: SocketAddr, peer: SocketAddr, old: usize, new: usize,
143) -> Option<PathEvent> {
144    (old != new).then_some(PathEvent::PmtuUpdated {
145        local,
146        peer,
147        pmtu: new,
148    })
149}
150
151/// A network path on which QUIC packets can be sent.
152#[derive(Debug)]
153pub struct Path {
154    /// The local address.
155    local_addr: SocketAddr,
156
157    /// The remote address.
158    peer_addr: SocketAddr,
159
160    /// Source CID sequence number used over that path.
161    pub active_scid_seq: Option<u64>,
162
163    /// Destination CID sequence number used over that path.
164    pub active_dcid_seq: Option<u64>,
165
166    /// The current validation state of the path.
167    state: PathState,
168
169    /// Is this path used to send non-probing packets.
170    active: bool,
171
172    /// Loss recovery and congestion control state.
173    pub recovery: recovery::Recovery,
174
175    /// Path MTU discovery state. None if PMTUD is disabled on the path.
176    pub pmtud: Option<pmtud::Pmtud>,
177
178    /// Pending challenge data with the size of the packet containing them and
179    /// when they were sent.
180    in_flight_challenges: VecDeque<([u8; 8], usize, Instant)>,
181
182    /// The maximum challenge size that got acknowledged.
183    max_challenge_size: usize,
184
185    /// Number of consecutive (spaced by at least 1 RTT) probing packets lost.
186    probing_lost: usize,
187
188    /// Last instant when a probing packet got lost.
189    last_probe_lost_time: Option<Instant>,
190
191    /// Received challenge data.
192    received_challenges: VecDeque<[u8; 8]>,
193
194    /// Max length of received challenges queue.
195    received_challenges_max_len: usize,
196
197    /// Number of packets sent on this path.
198    pub sent_count: usize,
199
200    /// Number of packets received on this path.
201    pub recv_count: usize,
202
203    /// Total number of packets sent with data retransmitted from this path.
204    pub retrans_count: usize,
205
206    /// Total number of times PTO (probe timeout) fired.
207    ///
208    /// Loss usually happens in a burst so the number of packets lost will
209    /// depend on the volume of inflight packets at the time of loss (which
210    /// can be arbitrary). PTO count measures the number of loss events and
211    /// provides a normalized loss metric.
212    pub total_pto_count: usize,
213
214    /// Number of DATAGRAM frames sent on this path.
215    pub dgram_sent_count: usize,
216
217    /// Number of DATAGRAM frames marked lost on this path.
218    pub dgram_lost_count: usize,
219
220    /// Number of DATAGRAM frames received on this path.
221    pub dgram_recv_count: usize,
222
223    /// Total number of sent bytes over this path.
224    pub sent_bytes: u64,
225
226    /// Total number of bytes received over this path.
227    pub recv_bytes: u64,
228
229    /// Total number of bytes retransmitted from this path.
230    /// This counts only STREAM and CRYPTO data.
231    pub stream_retrans_bytes: u64,
232
233    /// Total number of bytes the server can send before the peer's address
234    /// is verified.
235    pub max_send_bytes: usize,
236
237    /// Whether the peer's address has been verified.
238    pub verified_peer_address: bool,
239
240    /// Whether the peer has verified our address.
241    pub peer_verified_local_address: bool,
242
243    /// Does it requires sending PATH_CHALLENGE?
244    challenge_requested: bool,
245
246    /// Whether the failure of this path was notified.
247    failure_notified: bool,
248
249    /// Whether the connection tries to migrate to this path, but it still needs
250    /// to be validated.
251    migrating: bool,
252
253    /// Whether or not we should force eliciting of an ACK (e.g. via PING frame)
254    pub needs_ack_eliciting: bool,
255}
256
257impl Path {
258    /// Create a new Path instance with the provided addresses, the remaining of
259    /// the fields being set to their default value.
260    pub fn new(
261        local_addr: SocketAddr, peer_addr: SocketAddr,
262        recovery_config: &recovery::RecoveryConfig,
263        path_challenge_recv_max_queue_len: usize, is_initial: bool,
264        config: Option<&Config>,
265    ) -> Self {
266        let (state, active_scid_seq, active_dcid_seq) = if is_initial {
267            (PathState::Validated, Some(0), Some(0))
268        } else {
269            (PathState::Unknown, None, None)
270        };
271
272        let pmtud = config.and_then(|c| {
273            if c.pmtud {
274                let maximum_supported_mtu: usize = std::cmp::min(
275                    // if the max_udp_payload_size doesn't fit into a usize,
276                    // then max_send_udp_payload_size must be smaller so use
277                    // that
278                    c.local_transport_params
279                        .max_udp_payload_size
280                        .try_into()
281                        .unwrap_or(c.max_send_udp_payload_size),
282                    c.max_send_udp_payload_size,
283                );
284                Some(pmtud::Pmtud::new(maximum_supported_mtu, c.pmtud_max_probes))
285            } else {
286                None
287            }
288        });
289
290        Self {
291            local_addr,
292            peer_addr,
293            active_scid_seq,
294            active_dcid_seq,
295            state,
296            active: false,
297            recovery: recovery::Recovery::new_with_config(recovery_config),
298            pmtud,
299            in_flight_challenges: VecDeque::new(),
300            max_challenge_size: 0,
301            probing_lost: 0,
302            last_probe_lost_time: None,
303            received_challenges: VecDeque::with_capacity(
304                path_challenge_recv_max_queue_len,
305            ),
306            received_challenges_max_len: path_challenge_recv_max_queue_len,
307            sent_count: 0,
308            recv_count: 0,
309            retrans_count: 0,
310            total_pto_count: 0,
311            dgram_sent_count: 0,
312            dgram_lost_count: 0,
313            dgram_recv_count: 0,
314            sent_bytes: 0,
315            recv_bytes: 0,
316            stream_retrans_bytes: 0,
317            max_send_bytes: 0,
318            verified_peer_address: false,
319            peer_verified_local_address: false,
320            challenge_requested: false,
321            failure_notified: false,
322            migrating: false,
323            needs_ack_eliciting: false,
324        }
325    }
326
327    /// Returns the local address on which this path operates.
328    #[inline]
329    pub fn local_addr(&self) -> SocketAddr {
330        self.local_addr
331    }
332
333    /// Returns the peer address on which this path operates.
334    #[inline]
335    pub fn peer_addr(&self) -> SocketAddr {
336        self.peer_addr
337    }
338
339    /// Returns whether the path is working (i.e., not failed).
340    #[inline]
341    fn working(&self) -> bool {
342        self.state > PathState::Failed
343    }
344
345    /// Returns whether the path is active.
346    #[inline]
347    pub fn active(&self) -> bool {
348        self.active && self.working() && self.active_dcid_seq.is_some()
349    }
350
351    /// Returns whether the path can be used to send non-probing packets.
352    #[inline]
353    pub fn usable(&self) -> bool {
354        self.active() ||
355            (self.state == PathState::Validated &&
356                self.active_dcid_seq.is_some())
357    }
358
359    /// Returns whether the path is unused.
360    #[inline]
361    fn unused(&self) -> bool {
362        // FIXME: we should check that there is nothing in the sent queue.
363        !self.active() && self.active_dcid_seq.is_none()
364    }
365
366    /// Returns whether the path requires sending a probing packet.
367    #[inline]
368    pub fn probing_required(&self) -> bool {
369        !self.received_challenges.is_empty() || self.validation_requested()
370    }
371
372    /// Promotes the path to the provided state only if the new state is greater
373    /// than the current one.
374    fn promote_to(&mut self, state: PathState) {
375        if self.state < state {
376            self.state = state;
377        }
378    }
379
380    /// Returns whether the path is validated.
381    #[inline]
382    pub fn validated(&self) -> bool {
383        self.state == PathState::Validated
384    }
385
386    /// Returns whether this path failed its validation.
387    #[inline]
388    fn validation_failed(&self) -> bool {
389        self.state == PathState::Failed
390    }
391
392    // Returns whether this path is under path validation process.
393    #[inline]
394    pub fn under_validation(&self) -> bool {
395        matches!(self.state, PathState::Validating | PathState::ValidatingMTU)
396    }
397
398    /// Requests path validation.
399    #[inline]
400    pub fn request_validation(&mut self) {
401        self.challenge_requested = true;
402    }
403
404    /// Returns whether a validation is requested.
405    #[inline]
406    pub fn validation_requested(&self) -> bool {
407        self.challenge_requested
408    }
409
410    pub fn should_send_pmtu_probe(
411        &mut self, hs_confirmed: bool, hs_done: bool, out_len: usize,
412        is_closing: bool, frames_empty: bool,
413    ) -> bool {
414        let Some(pmtud) = self.pmtud.as_mut() else {
415            return false;
416        };
417
418        (hs_confirmed && hs_done) &&
419            self.recovery.cwnd_available() > pmtud.get_probe_size() &&
420            out_len >= pmtud.get_probe_size() &&
421            pmtud.should_probe() &&
422            !is_closing &&
423            frames_empty
424    }
425
426    pub fn on_challenge_sent(&mut self) {
427        self.promote_to(PathState::Validating);
428        self.challenge_requested = false;
429    }
430
431    /// Handles the sending of PATH_CHALLENGE.
432    pub fn add_challenge_sent(
433        &mut self, data: [u8; 8], pkt_size: usize, sent_time: Instant,
434    ) {
435        self.on_challenge_sent();
436        self.in_flight_challenges
437            .push_back((data, pkt_size, sent_time));
438    }
439
440    pub fn on_challenge_received(&mut self, data: [u8; 8]) {
441        // Discard challenges that would cause us to queue more than we want.
442        if self.received_challenges.len() == self.received_challenges_max_len {
443            return;
444        }
445
446        self.received_challenges.push_back(data);
447        self.peer_verified_local_address = true;
448    }
449
450    pub fn has_pending_challenge(&self, data: [u8; 8]) -> bool {
451        self.in_flight_challenges.iter().any(|(d, ..)| *d == data)
452    }
453
454    /// Returns whether the path is now validated.
455    pub fn on_response_received(&mut self, data: [u8; 8]) -> bool {
456        self.verified_peer_address = true;
457        self.probing_lost = 0;
458
459        let mut challenge_size = 0;
460        self.in_flight_challenges.retain(|(d, s, _)| {
461            if *d == data {
462                challenge_size = *s;
463                false
464            } else {
465                true
466            }
467        });
468
469        // The 4-tuple is reachable, but we didn't check Path MTU yet.
470        self.promote_to(PathState::ValidatingMTU);
471
472        self.max_challenge_size =
473            std::cmp::max(self.max_challenge_size, challenge_size);
474
475        if self.state == PathState::ValidatingMTU {
476            if self.max_challenge_size >= crate::MIN_CLIENT_INITIAL_LEN {
477                // Path MTU is sufficient for QUIC traffic.
478                self.promote_to(PathState::Validated);
479                return true;
480            }
481
482            // If the MTU was not validated, probe again.
483            self.request_validation();
484        }
485
486        false
487    }
488
489    fn on_failed_validation(&mut self) {
490        self.state = PathState::Failed;
491        self.active = false;
492    }
493
494    #[inline]
495    pub fn pop_received_challenge(&mut self) -> Option<[u8; 8]> {
496        self.received_challenges.pop_front()
497    }
498
499    pub fn on_loss_detection_timeout(
500        &mut self, handshake_status: HandshakeStatus, now: Instant,
501        is_server: bool, trace_id: &str,
502    ) -> OnLossDetectionTimeoutOutcome {
503        let outcome = self.recovery.on_loss_detection_timeout(
504            handshake_status,
505            now,
506            trace_id,
507        );
508
509        let mut lost_probe_time = None;
510        self.in_flight_challenges.retain(|(_, _, sent_time)| {
511            if *sent_time <= now {
512                if lost_probe_time.is_none() {
513                    lost_probe_time = Some(*sent_time);
514                }
515                false
516            } else {
517                true
518            }
519        });
520
521        // If we lost probing packets, check if the path failed
522        // validation.
523        if let Some(lost_probe_time) = lost_probe_time {
524            self.last_probe_lost_time = match self.last_probe_lost_time {
525                Some(last) => {
526                    // Count a loss if at least 1-RTT happened.
527                    if lost_probe_time - last >= self.recovery.rtt() {
528                        self.probing_lost += 1;
529                        Some(lost_probe_time)
530                    } else {
531                        Some(last)
532                    }
533                },
534                None => {
535                    self.probing_lost += 1;
536                    Some(lost_probe_time)
537                },
538            };
539            // As a server, if requesting a challenge is not
540            // possible due to the amplification attack, declare the
541            // validation as failed.
542            if self.probing_lost >= crate::MAX_PROBING_TIMEOUTS ||
543                (is_server && self.max_send_bytes < crate::MIN_PROBING_SIZE)
544            {
545                self.on_failed_validation();
546            } else {
547                self.request_validation();
548            }
549        }
550
551        // Track PTO timeout event
552        self.total_pto_count += 1;
553
554        outcome
555    }
556
557    /// Returns true if the path's recovery module hasn't processed any non-ACK
558    /// packets, and it is still OK to fully reinitialize the recovery module to
559    /// pickup changes to congestion control config.
560    pub fn can_reinit_recovery(&self) -> bool {
561        // Recovery can be reinitialized until the connection sends in-flight
562        // data. The congestion controller has no relevant state before then.
563        // ACK-only packets sent before the full ClientHello arrives should not
564        // prevent reinitialization.
565        self.recovery.bytes_in_flight() == 0 &&
566            self.recovery.bytes_in_flight_duration() == Duration::ZERO
567    }
568
569    pub fn reinit_recovery(
570        &mut self, recovery_config: &recovery::RecoveryConfig,
571    ) {
572        self.recovery = recovery::Recovery::new_with_config(recovery_config)
573    }
574
575    pub fn stats(&self) -> PathStats {
576        let pmtu = match self.pmtud.as_ref().map(|p| p.get_current_mtu()) {
577            Some(v) => v,
578
579            None => self.recovery.max_datagram_size(),
580        };
581
582        PathStats {
583            local_addr: self.local_addr,
584            peer_addr: self.peer_addr,
585            validation_state: self.state,
586            active: self.active,
587            recv: self.recv_count,
588            sent: self.sent_count,
589            lost: self.recovery.lost_count(),
590            retrans: self.retrans_count,
591            total_pto_count: self.total_pto_count,
592            dgram_recv: self.dgram_recv_count,
593            dgram_sent: self.dgram_sent_count,
594            dgram_lost: self.dgram_lost_count,
595            rtt: self.recovery.rtt(),
596            min_rtt: self.recovery.min_rtt(),
597            max_rtt: self.recovery.max_rtt(),
598            rttvar: self.recovery.rttvar(),
599            cwnd: self.recovery.cwnd(),
600            sent_bytes: self.sent_bytes,
601            recv_bytes: self.recv_bytes,
602            lost_bytes: self.recovery.bytes_lost(),
603            stream_retrans_bytes: self.stream_retrans_bytes,
604            pmtu,
605            delivery_rate: self.recovery.delivery_rate().to_bytes_per_second(),
606            max_bandwidth: self
607                .recovery
608                .max_bandwidth()
609                .map(Bandwidth::to_bytes_per_second),
610            rtt_persistent_jump_count: self.recovery.rtt_persistent_jump_count(),
611            startup_exit: self.recovery.startup_exit(),
612        }
613    }
614
615    pub fn bytes_in_flight_duration(&self) -> Duration {
616        self.recovery.bytes_in_flight_duration()
617    }
618}
619
620/// An iterator over SocketAddr.
621#[derive(Default)]
622pub struct SocketAddrIter {
623    pub(crate) sockaddrs: SmallVec<[SocketAddr; 8]>,
624    pub(crate) index: usize,
625}
626
627impl Iterator for SocketAddrIter {
628    type Item = SocketAddr;
629
630    #[inline]
631    fn next(&mut self) -> Option<Self::Item> {
632        let v = self.sockaddrs.get(self.index)?;
633        self.index += 1;
634        Some(*v)
635    }
636}
637
638impl ExactSizeIterator for SocketAddrIter {
639    #[inline]
640    fn len(&self) -> usize {
641        self.sockaddrs.len() - self.index
642    }
643}
644
645/// All path-related information.
646pub struct PathMap {
647    /// The paths of the connection. Each of them has an internal identifier
648    /// that is used by `addrs_to_paths` and `ConnectionEntry`.
649    paths: Slab<Path>,
650
651    /// The maximum number of concurrent paths allowed.
652    max_concurrent_paths: usize,
653
654    /// The mapping from the (local `SocketAddr`, peer `SocketAddr`) to the
655    /// `Path` structure identifier.
656    addrs_to_paths: BTreeMap<(SocketAddr, SocketAddr), usize>,
657
658    /// Path-specific events to be notified to the application.
659    events: VecDeque<PathEvent>,
660
661    /// Whether this manager serves a connection as a server.
662    is_server: bool,
663}
664
665impl PathMap {
666    /// Creates a new `PathMap` with the initial provided `path` and a
667    /// capacity limit.
668    pub fn new(
669        mut initial_path: Path, max_concurrent_paths: usize, is_server: bool,
670    ) -> Self {
671        // Most connections only have one path.
672        let mut paths = Slab::with_capacity(1);
673        let mut addrs_to_paths = BTreeMap::new();
674
675        let local_addr = initial_path.local_addr;
676        let peer_addr = initial_path.peer_addr;
677
678        // As it is the first path, it is active by default.
679        initial_path.active = true;
680
681        let active_path_id = paths.insert(initial_path);
682        addrs_to_paths.insert((local_addr, peer_addr), active_path_id);
683
684        Self {
685            paths,
686            max_concurrent_paths,
687            addrs_to_paths,
688            events: VecDeque::new(),
689            is_server,
690        }
691    }
692
693    /// Gets an immutable reference to the path identified by `path_id`. If the
694    /// provided `path_id` does not identify any current `Path`, returns an
695    /// [`InvalidState`].
696    ///
697    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
698    #[inline]
699    pub fn get(&self, path_id: usize) -> Result<&Path> {
700        self.paths.get(path_id).ok_or(Error::InvalidState)
701    }
702
703    /// Gets a mutable reference to the path identified by `path_id`. If the
704    /// provided `path_id` does not identify any current `Path`, returns an
705    /// [`InvalidState`].
706    ///
707    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
708    #[inline]
709    pub fn get_mut(&mut self, path_id: usize) -> Result<&mut Path> {
710        self.paths.get_mut(path_id).ok_or(Error::InvalidState)
711    }
712
713    #[inline]
714    /// Gets an immutable reference to the active path with the value of the
715    /// lowest identifier. If there is no active path, returns `None`.
716    pub fn get_active_with_pid(&self) -> Option<(usize, &Path)> {
717        self.paths.iter().find(|(_, p)| p.active())
718    }
719
720    /// Gets an immutable reference to the active path with the lowest
721    /// identifier. If there is no active path, returns an [`InvalidState`].
722    ///
723    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
724    #[inline]
725    pub fn get_active(&self) -> Result<&Path> {
726        self.get_active_with_pid()
727            .map(|(_, p)| p)
728            .ok_or(Error::InvalidState)
729    }
730
731    /// Gets the lowest active path identifier. If there is no active path,
732    /// returns an [`InvalidState`].
733    ///
734    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
735    #[inline]
736    pub fn get_active_path_id(&self) -> Result<usize> {
737        self.get_active_with_pid()
738            .map(|(pid, _)| pid)
739            .ok_or(Error::InvalidState)
740    }
741
742    /// Gets an mutable reference to the active path with the lowest identifier.
743    /// If there is no active path, returns an [`InvalidState`].
744    ///
745    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
746    #[inline]
747    pub fn get_active_mut(&mut self) -> Result<&mut Path> {
748        self.paths
749            .iter_mut()
750            .map(|(_, p)| p)
751            .find(|p| p.active())
752            .ok_or(Error::InvalidState)
753    }
754
755    /// Returns an iterator over all existing paths.
756    #[inline]
757    pub fn iter(&self) -> slab::Iter<'_, Path> {
758        self.paths.iter()
759    }
760
761    /// Returns a mutable iterator over all existing paths.
762    #[inline]
763    pub fn iter_mut(&mut self) -> slab::IterMut<'_, Path> {
764        self.paths.iter_mut()
765    }
766
767    /// Returns a mutable iterator over all existing paths and the path event
768    /// queue.
769    #[inline]
770    pub(crate) fn iter_mut_and_events(
771        &mut self,
772    ) -> (slab::IterMut<'_, Path>, &mut VecDeque<PathEvent>) {
773        (self.paths.iter_mut(), &mut self.events)
774    }
775
776    /// Returns the number of existing paths.
777    #[inline]
778    pub fn len(&self) -> usize {
779        self.paths.len()
780    }
781
782    /// Returns the `Path` identifier related to the provided `addrs`.
783    #[inline]
784    pub fn path_id_from_addrs(
785        &self, addrs: &(SocketAddr, SocketAddr),
786    ) -> Option<usize> {
787        self.addrs_to_paths.get(addrs).copied()
788    }
789
790    /// Checks if creating a new path will not exceed the current `self.paths`
791    /// capacity. If yes, this method tries to remove one unused path. If it
792    /// fails to do so, returns [`Done`].
793    ///
794    /// [`Done`]: enum.Error.html#variant.Done
795    fn make_room_for_new_path(&mut self) -> Result<()> {
796        if self.paths.len() < self.max_concurrent_paths {
797            return Ok(());
798        }
799
800        let (pid_to_remove, _) = self
801            .paths
802            .iter()
803            .find(|(_, p)| p.unused())
804            .ok_or(Error::Done)?;
805
806        let path = self.paths.remove(pid_to_remove);
807        self.addrs_to_paths
808            .remove(&(path.local_addr, path.peer_addr));
809
810        self.notify_event(PathEvent::Closed(path.local_addr, path.peer_addr));
811
812        Ok(())
813    }
814
815    /// Records the provided `Path` and returns its assigned identifier.
816    ///
817    /// On success, this method takes care of creating a notification to the
818    /// serving application, if it serves a server-side connection.
819    ///
820    /// If there are already `max_concurrent_paths` currently recorded, this
821    /// method tries to remove an unused `Path` first. If it fails to do so,
822    /// it returns [`Done`].
823    ///
824    /// [`Done`]: enum.Error.html#variant.Done
825    pub fn insert_path(&mut self, path: Path, is_server: bool) -> Result<usize> {
826        self.make_room_for_new_path()?;
827
828        let local_addr = path.local_addr;
829        let peer_addr = path.peer_addr;
830
831        let pid = self.paths.insert(path);
832        self.addrs_to_paths.insert((local_addr, peer_addr), pid);
833
834        // Notifies the application if we are in server mode.
835        if is_server {
836            self.notify_event(PathEvent::New(local_addr, peer_addr));
837        }
838
839        Ok(pid)
840    }
841
842    /// Notifies a path event to the application served by the connection.
843    pub fn notify_event(&mut self, ev: PathEvent) {
844        self.events.push_back(ev);
845    }
846
847    /// Gets the first path event to be notified to the application.
848    pub fn pop_event(&mut self) -> Option<PathEvent> {
849        self.events.pop_front()
850    }
851
852    /// Notifies all failed validations to the application.
853    pub fn notify_failed_validations(&mut self) {
854        let validation_failed = self
855            .paths
856            .iter_mut()
857            .filter(|(_, p)| p.validation_failed() && !p.failure_notified);
858
859        for (_, p) in validation_failed {
860            self.events.push_back(PathEvent::FailedValidation(
861                p.local_addr,
862                p.peer_addr,
863            ));
864
865            p.failure_notified = true;
866        }
867    }
868
869    /// Finds a path candidate to be active and returns its identifier.
870    pub fn find_candidate_path(&self) -> Option<usize> {
871        // TODO: also consider unvalidated paths if there are no more validated.
872        self.paths
873            .iter()
874            .find(|(_, p)| p.usable())
875            .map(|(pid, _)| pid)
876    }
877
878    /// Handles incoming PATH_RESPONSE data.
879    pub fn on_response_received(&mut self, data: [u8; 8]) -> Result<()> {
880        let active_pid = self.get_active_path_id()?;
881
882        let challenge_pending =
883            self.iter_mut().find(|(_, p)| p.has_pending_challenge(data));
884
885        if let Some((pid, p)) = challenge_pending {
886            if p.on_response_received(data) {
887                let local_addr = p.local_addr;
888                let peer_addr = p.peer_addr;
889                let was_migrating = p.migrating;
890
891                p.migrating = false;
892
893                // Notifies the application.
894                self.notify_event(PathEvent::Validated(local_addr, peer_addr));
895
896                // If this path was the candidate for migration, notifies the
897                // application.
898                if pid == active_pid && was_migrating {
899                    self.notify_event(PathEvent::PeerMigrated(
900                        local_addr, peer_addr,
901                    ));
902                }
903            }
904        }
905        Ok(())
906    }
907
908    /// Sets the path with identifier 'path_id' to be active.
909    ///
910    /// There can be exactly one active path on which non-probing packets can be
911    /// sent. If another path is marked as active, it will be superseded by the
912    /// one having `path_id` as identifier.
913    ///
914    /// A server should always ensure that the active path is validated. If it
915    /// is already the case, it notifies the application that the connection
916    /// migrated. Otherwise, it triggers a path validation and defers the
917    /// notification once it is actually validated.
918    pub fn set_active_path(&mut self, path_id: usize) -> Result<()> {
919        let is_server = self.is_server;
920
921        if let Ok(old_active_path) = self.get_active_mut() {
922            old_active_path.active = false;
923        }
924
925        let new_active_path = self.get_mut(path_id)?;
926        new_active_path.active = true;
927
928        if is_server {
929            if new_active_path.validated() {
930                let local_addr = new_active_path.local_addr();
931                let peer_addr = new_active_path.peer_addr();
932
933                self.notify_event(PathEvent::PeerMigrated(local_addr, peer_addr));
934            } else {
935                new_active_path.migrating = true;
936
937                // Requests path validation if needed.
938                if !new_active_path.under_validation() {
939                    new_active_path.request_validation();
940                }
941            }
942        }
943
944        Ok(())
945    }
946
947    /// Configures path MTU discovery on all existing paths.
948    pub fn set_discover_pmtu_on_existing_paths(
949        &mut self, discover: bool, max_send_udp_payload_size: usize,
950        pmtud_max_probes: u8,
951    ) {
952        for (_, path) in self.paths.iter_mut() {
953            let old_pmtu = path
954                .pmtud
955                .as_ref()
956                .map_or(path.recovery.max_datagram_size(), |pmtud| {
957                    pmtud.get_current_mtu()
958                });
959
960            path.pmtud = if discover {
961                Some(pmtud::Pmtud::new(
962                    max_send_udp_payload_size,
963                    pmtud_max_probes,
964                ))
965            } else {
966                None
967            };
968
969            if let Some(pmtud) = path.pmtud.as_ref() {
970                if let Some(event) = pmtu_event(
971                    path.local_addr,
972                    path.peer_addr,
973                    old_pmtu,
974                    pmtud.get_current_mtu(),
975                ) {
976                    self.events.push_back(event);
977                }
978            }
979        }
980    }
981}
982
983/// Statistics about the path of a connection.
984///
985/// A connection’s path statistics can be collected using the [`path_stats()`]
986/// method.
987///
988/// [`path_stats()`]: struct.Connection.html#method.path_stats
989#[derive(Clone)]
990#[non_exhaustive]
991pub struct PathStats {
992    /// The local address of the path.
993    pub local_addr: SocketAddr,
994
995    /// The peer address of the path.
996    pub peer_addr: SocketAddr,
997
998    /// The path validation state.
999    pub validation_state: PathState,
1000
1001    /// Whether the path is marked as active.
1002    pub active: bool,
1003
1004    /// The number of QUIC packets received.
1005    pub recv: usize,
1006
1007    /// The number of QUIC packets sent.
1008    pub sent: usize,
1009
1010    /// The number of QUIC packets that were lost.
1011    pub lost: usize,
1012
1013    /// The number of sent QUIC packets with retransmitted data.
1014    pub retrans: usize,
1015
1016    /// The number of times PTO (probe timeout) fired.
1017    ///
1018    /// Loss usually happens in a burst so the number of packets lost will
1019    /// depend on the volume of inflight packets at the time of loss (which
1020    /// can be arbitrary). PTO count measures the number of loss events and
1021    /// provides a normalized loss metric.
1022    pub total_pto_count: usize,
1023
1024    /// The number of DATAGRAM frames received.
1025    pub dgram_recv: usize,
1026
1027    /// The number of DATAGRAM frames sent.
1028    pub dgram_sent: usize,
1029
1030    /// The number of DATAGRAM frames lost.
1031    pub dgram_lost: usize,
1032
1033    /// The estimated round-trip time of the connection.
1034    pub rtt: Duration,
1035
1036    /// The minimum round-trip time observed.
1037    pub min_rtt: Option<Duration>,
1038
1039    /// The maximum round-trip time observed.
1040    pub max_rtt: Option<Duration>,
1041
1042    /// The estimated round-trip time variation in samples using a mean
1043    /// variation.
1044    pub rttvar: Duration,
1045
1046    /// The size of the connection's congestion window in bytes.
1047    pub cwnd: usize,
1048
1049    /// The number of sent bytes.
1050    pub sent_bytes: u64,
1051
1052    /// The number of received bytes.
1053    pub recv_bytes: u64,
1054
1055    /// The number of bytes lost.
1056    pub lost_bytes: u64,
1057
1058    /// The number of stream bytes retransmitted.
1059    pub stream_retrans_bytes: u64,
1060
1061    /// The current PMTU for the connection.
1062    pub pmtu: usize,
1063
1064    /// The most recent data delivery rate estimate in bytes/s.
1065    ///
1066    /// Note that this value could be inaccurate if the application does not
1067    /// respect pacing hints (see [`SendInfo.at`] and [Pacing] for more
1068    /// details).
1069    ///
1070    /// [`SendInfo.at`]: struct.SendInfo.html#structfield.at
1071    /// [Pacing]: index.html#pacing
1072    pub delivery_rate: u64,
1073
1074    /// The maximum bandwidth estimate for the connection in bytes/s.
1075    ///
1076    /// Note: not all congestion control algorithms provide this metric;
1077    /// it is currently only implemented for bbr2_gcongestion.
1078    pub max_bandwidth: Option<u64>,
1079
1080    /// The total number of confirmed persistent RTT jump episodes.
1081    pub rtt_persistent_jump_count: u64,
1082
1083    /// Statistics from when a CCA first exited the startup phase.
1084    pub startup_exit: Option<StartupExit>,
1085}
1086
1087impl std::fmt::Debug for PathStats {
1088    #[inline]
1089    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1090        write!(
1091            f,
1092            "local_addr={:?} peer_addr={:?} ",
1093            self.local_addr, self.peer_addr,
1094        )?;
1095        write!(
1096            f,
1097            "validation_state={:?} active={} ",
1098            self.validation_state, self.active,
1099        )?;
1100        write!(
1101            f,
1102            "recv={} sent={} lost={} retrans={} rtt={:?} min_rtt={:?} rttvar={:?} cwnd={}",
1103            self.recv, self.sent, self.lost, self.retrans, self.rtt, self.min_rtt, self.rttvar, self.cwnd,
1104        )?;
1105
1106        write!(
1107            f,
1108            " sent_bytes={} recv_bytes={} lost_bytes={}",
1109            self.sent_bytes, self.recv_bytes, self.lost_bytes,
1110        )?;
1111
1112        write!(
1113            f,
1114            " stream_retrans_bytes={} pmtu={} delivery_rate={} rtt_persistent_jump_count={}",
1115            self.stream_retrans_bytes,
1116            self.pmtu,
1117            self.delivery_rate,
1118            self.rtt_persistent_jump_count,
1119        )
1120    }
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125    use crate::rand;
1126    use crate::test_utils;
1127    use crate::MIN_CLIENT_INITIAL_LEN;
1128
1129    use crate::recovery::RecoveryConfig;
1130    use crate::Config;
1131
1132    use super::*;
1133
1134    #[test]
1135    fn pmtud_probe_respects_cwnd_during_pto() {
1136        let local = "127.0.0.1:1234".parse().unwrap();
1137        let peer = "127.0.0.1:4321".parse().unwrap();
1138        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1139        config.discover_pmtu(true);
1140        config.set_max_send_udp_payload_size(1400);
1141        let recovery_config = RecoveryConfig::from_config(&config);
1142        let mut path = Path::new(
1143            local,
1144            peer,
1145            &recovery_config,
1146            config.path_challenge_recv_max_queue_len,
1147            true,
1148            Some(&config),
1149        );
1150        let now = Instant::now();
1151        let cwnd = path.recovery.cwnd();
1152
1153        path.recovery.on_packet_sent(
1154            test_utils::helper_packet_sent(0, now, cwnd),
1155            crate::packet::Epoch::Application,
1156            HandshakeStatus {
1157                has_handshake_keys: false,
1158                peer_verified_address: true,
1159                completed: true,
1160            },
1161            now,
1162            "",
1163        );
1164        path.recovery
1165            .inc_loss_probes(crate::packet::Epoch::Application);
1166
1167        assert!(path.pmtud.as_ref().unwrap().should_probe());
1168        assert!(!path.should_send_pmtu_probe(true, true, 1400, false, true));
1169    }
1170
1171    #[test]
1172    fn reinitializing_pmtud_notifies_fallback_limit() {
1173        let local = "127.0.0.1:1234".parse().unwrap();
1174        let peer = "127.0.0.1:4321".parse().unwrap();
1175        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1176        config.discover_pmtu(true);
1177        config.set_max_send_udp_payload_size(1400);
1178        let recovery_config = RecoveryConfig::from_config(&config);
1179        let mut path = Path::new(
1180            local,
1181            peer,
1182            &recovery_config,
1183            config.path_challenge_recv_max_queue_len,
1184            true,
1185            Some(&config),
1186        );
1187        path.pmtud.as_mut().unwrap().successful_probe(1400);
1188        let mut paths = PathMap::new(path, 1, false);
1189
1190        paths.set_discover_pmtu_on_existing_paths(false, 1400, 1);
1191        assert_eq!(paths.pop_event(), None);
1192
1193        paths.set_discover_pmtu_on_existing_paths(true, 1400, 1);
1194        assert_eq!(
1195            paths.pop_event(),
1196            Some(PathEvent::PmtuUpdated {
1197                local,
1198                peer,
1199                pmtu: MIN_CLIENT_INITIAL_LEN,
1200            })
1201        );
1202    }
1203
1204    #[test]
1205    fn path_validation_limited_mtu() {
1206        let client_addr = "127.0.0.1:1234".parse().unwrap();
1207        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
1208        let server_addr = "127.0.0.1:4321".parse().unwrap();
1209
1210        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1211        let recovery_config = RecoveryConfig::from_config(&config);
1212
1213        let path = Path::new(
1214            client_addr,
1215            server_addr,
1216            &recovery_config,
1217            config.path_challenge_recv_max_queue_len,
1218            true,
1219            None,
1220        );
1221        let mut path_mgr = PathMap::new(path, 2, false);
1222
1223        let probed_path = Path::new(
1224            client_addr_2,
1225            server_addr,
1226            &recovery_config,
1227            config.path_challenge_recv_max_queue_len,
1228            false,
1229            None,
1230        );
1231        path_mgr.insert_path(probed_path, false).unwrap();
1232
1233        let pid = path_mgr
1234            .path_id_from_addrs(&(client_addr_2, server_addr))
1235            .unwrap();
1236        path_mgr.get_mut(pid).unwrap().request_validation();
1237        assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1238        assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1239
1240        // Send `PathChallenge` one byte below `MIN_CLIENT_INITIAL_LEN`.
1241        let data = rand::rand_u64().to_be_bytes();
1242        path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1243            data,
1244            MIN_CLIENT_INITIAL_LEN - 1,
1245            Instant::now(),
1246        );
1247
1248        assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1249        assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1250        assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1251        assert!(!path_mgr.get_mut(pid).unwrap().validated());
1252        assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validating);
1253        assert_eq!(path_mgr.pop_event(), None);
1254
1255        // Receives the response. The path is reachable, but the MTU is not
1256        // validated yet.
1257        path_mgr.on_response_received(data).unwrap();
1258
1259        assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1260        assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1261        assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1262        assert!(!path_mgr.get_mut(pid).unwrap().validated());
1263        assert_eq!(
1264            path_mgr.get_mut(pid).unwrap().state,
1265            PathState::ValidatingMTU
1266        );
1267        assert_eq!(path_mgr.pop_event(), None);
1268
1269        // Fake sending of PathChallenge in a packet of MIN_CLIENT_INITIAL_LEN
1270        // bytes.
1271        let data = rand::rand_u64().to_be_bytes();
1272        path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1273            data,
1274            MIN_CLIENT_INITIAL_LEN,
1275            Instant::now(),
1276        );
1277
1278        path_mgr.on_response_received(data).unwrap();
1279
1280        assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1281        assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1282        assert!(!path_mgr.get_mut(pid).unwrap().under_validation());
1283        assert!(path_mgr.get_mut(pid).unwrap().validated());
1284        assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validated);
1285        assert_eq!(
1286            path_mgr.pop_event(),
1287            Some(PathEvent::Validated(client_addr_2, server_addr))
1288        );
1289    }
1290
1291    #[test]
1292    fn multiple_probes() {
1293        let client_addr = "127.0.0.1:1234".parse().unwrap();
1294        let server_addr = "127.0.0.1:4321".parse().unwrap();
1295
1296        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1297        let recovery_config = RecoveryConfig::from_config(&config);
1298
1299        let path = Path::new(
1300            client_addr,
1301            server_addr,
1302            &recovery_config,
1303            config.path_challenge_recv_max_queue_len,
1304            true,
1305            None,
1306        );
1307        let mut client_path_mgr = PathMap::new(path, 2, false);
1308        let mut server_path = Path::new(
1309            server_addr,
1310            client_addr,
1311            &recovery_config,
1312            config.path_challenge_recv_max_queue_len,
1313            false,
1314            None,
1315        );
1316
1317        let client_pid = client_path_mgr
1318            .path_id_from_addrs(&(client_addr, server_addr))
1319            .unwrap();
1320
1321        // First probe.
1322        let data = rand::rand_u64().to_be_bytes();
1323
1324        client_path_mgr
1325            .get_mut(client_pid)
1326            .unwrap()
1327            .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1328
1329        // Second probe.
1330        let data_2 = rand::rand_u64().to_be_bytes();
1331
1332        client_path_mgr
1333            .get_mut(client_pid)
1334            .unwrap()
1335            .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1336        assert_eq!(
1337            client_path_mgr
1338                .get(client_pid)
1339                .unwrap()
1340                .in_flight_challenges
1341                .len(),
1342            2
1343        );
1344
1345        // If we receive multiple challenges, we can store them.
1346        server_path.on_challenge_received(data);
1347        assert_eq!(server_path.received_challenges.len(), 1);
1348        server_path.on_challenge_received(data_2);
1349        assert_eq!(server_path.received_challenges.len(), 2);
1350
1351        // Response for first probe.
1352        client_path_mgr.on_response_received(data).unwrap();
1353        assert_eq!(
1354            client_path_mgr
1355                .get(client_pid)
1356                .unwrap()
1357                .in_flight_challenges
1358                .len(),
1359            1
1360        );
1361
1362        // Response for second probe.
1363        client_path_mgr.on_response_received(data_2).unwrap();
1364        assert_eq!(
1365            client_path_mgr
1366                .get(client_pid)
1367                .unwrap()
1368                .in_flight_challenges
1369                .len(),
1370            0
1371        );
1372    }
1373
1374    #[test]
1375    fn too_many_probes() {
1376        let client_addr = "127.0.0.1:1234".parse().unwrap();
1377        let server_addr = "127.0.0.1:4321".parse().unwrap();
1378
1379        // Default to DEFAULT_MAX_PATH_CHALLENGE_RX_QUEUE_LEN
1380        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1381        let recovery_config = RecoveryConfig::from_config(&config);
1382
1383        let path = Path::new(
1384            client_addr,
1385            server_addr,
1386            &recovery_config,
1387            config.path_challenge_recv_max_queue_len,
1388            true,
1389            None,
1390        );
1391        let mut client_path_mgr = PathMap::new(path, 2, false);
1392        let mut server_path = Path::new(
1393            server_addr,
1394            client_addr,
1395            &recovery_config,
1396            config.path_challenge_recv_max_queue_len,
1397            false,
1398            None,
1399        );
1400
1401        let client_pid = client_path_mgr
1402            .path_id_from_addrs(&(client_addr, server_addr))
1403            .unwrap();
1404
1405        // First probe.
1406        let data = rand::rand_u64().to_be_bytes();
1407
1408        client_path_mgr
1409            .get_mut(client_pid)
1410            .unwrap()
1411            .add_challenge_sent(data, MIN_CLIENT_INITIAL_LEN, Instant::now());
1412
1413        // Second probe.
1414        let data_2 = rand::rand_u64().to_be_bytes();
1415
1416        client_path_mgr
1417            .get_mut(client_pid)
1418            .unwrap()
1419            .add_challenge_sent(data_2, MIN_CLIENT_INITIAL_LEN, Instant::now());
1420        assert_eq!(
1421            client_path_mgr
1422                .get(client_pid)
1423                .unwrap()
1424                .in_flight_challenges
1425                .len(),
1426            2
1427        );
1428
1429        // Third probe.
1430        let data_3 = rand::rand_u64().to_be_bytes();
1431
1432        client_path_mgr
1433            .get_mut(client_pid)
1434            .unwrap()
1435            .add_challenge_sent(data_3, MIN_CLIENT_INITIAL_LEN, Instant::now());
1436        assert_eq!(
1437            client_path_mgr
1438                .get(client_pid)
1439                .unwrap()
1440                .in_flight_challenges
1441                .len(),
1442            3
1443        );
1444
1445        // Fourth probe.
1446        let data_4 = rand::rand_u64().to_be_bytes();
1447
1448        client_path_mgr
1449            .get_mut(client_pid)
1450            .unwrap()
1451            .add_challenge_sent(data_4, MIN_CLIENT_INITIAL_LEN, Instant::now());
1452        assert_eq!(
1453            client_path_mgr
1454                .get(client_pid)
1455                .unwrap()
1456                .in_flight_challenges
1457                .len(),
1458            4
1459        );
1460
1461        // If we receive multiple challenges, we can store them up to our queue
1462        // size.
1463        server_path.on_challenge_received(data);
1464        assert_eq!(server_path.received_challenges.len(), 1);
1465        server_path.on_challenge_received(data_2);
1466        assert_eq!(server_path.received_challenges.len(), 2);
1467        server_path.on_challenge_received(data_3);
1468        assert_eq!(server_path.received_challenges.len(), 3);
1469        server_path.on_challenge_received(data_4);
1470        assert_eq!(server_path.received_challenges.len(), 3);
1471
1472        // Response for first probe.
1473        client_path_mgr.on_response_received(data).unwrap();
1474        assert_eq!(
1475            client_path_mgr
1476                .get(client_pid)
1477                .unwrap()
1478                .in_flight_challenges
1479                .len(),
1480            3
1481        );
1482
1483        // Response for second probe.
1484        client_path_mgr.on_response_received(data_2).unwrap();
1485        assert_eq!(
1486            client_path_mgr
1487                .get(client_pid)
1488                .unwrap()
1489                .in_flight_challenges
1490                .len(),
1491            2
1492        );
1493
1494        // Response for third probe.
1495        client_path_mgr.on_response_received(data_3).unwrap();
1496        assert_eq!(
1497            client_path_mgr
1498                .get(client_pid)
1499                .unwrap()
1500                .in_flight_challenges
1501                .len(),
1502            1
1503        );
1504
1505        // There will never be a response for fourth probe...
1506    }
1507}