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