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