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