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