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