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