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            max_rtt: self.recovery.max_rtt(),
506            rttvar: self.recovery.rttvar(),
507            cwnd: self.recovery.cwnd(),
508            sent_bytes: self.sent_bytes,
509            recv_bytes: self.recv_bytes,
510            lost_bytes: self.recovery.bytes_lost(),
511            stream_retrans_bytes: self.stream_retrans_bytes,
512            pmtu: self.recovery.max_datagram_size(),
513            delivery_rate: self.recovery.delivery_rate().to_bytes_per_second(),
514        }
515    }
516}
517
518/// An iterator over SocketAddr.
519#[derive(Default)]
520pub struct SocketAddrIter {
521    pub(crate) sockaddrs: SmallVec<[SocketAddr; 8]>,
522    pub(crate) index: usize,
523}
524
525impl Iterator for SocketAddrIter {
526    type Item = SocketAddr;
527
528    #[inline]
529    fn next(&mut self) -> Option<Self::Item> {
530        let v = self.sockaddrs.get(self.index)?;
531        self.index += 1;
532        Some(*v)
533    }
534}
535
536impl ExactSizeIterator for SocketAddrIter {
537    #[inline]
538    fn len(&self) -> usize {
539        self.sockaddrs.len() - self.index
540    }
541}
542
543/// All path-related information.
544pub struct PathMap {
545    /// The paths of the connection. Each of them has an internal identifier
546    /// that is used by `addrs_to_paths` and `ConnectionEntry`.
547    paths: Slab<Path>,
548
549    /// The maximum number of concurrent paths allowed.
550    max_concurrent_paths: usize,
551
552    /// The mapping from the (local `SocketAddr`, peer `SocketAddr`) to the
553    /// `Path` structure identifier.
554    addrs_to_paths: BTreeMap<(SocketAddr, SocketAddr), usize>,
555
556    /// Path-specific events to be notified to the application.
557    events: VecDeque<PathEvent>,
558
559    /// Whether this manager serves a connection as a server.
560    is_server: bool,
561}
562
563impl PathMap {
564    /// Creates a new `PathMap` with the initial provided `path` and a
565    /// capacity limit.
566    pub fn new(
567        mut initial_path: Path, max_concurrent_paths: usize, is_server: bool,
568        enable_pmtud: bool, max_send_udp_payload_size: usize,
569    ) -> Self {
570        let mut paths = Slab::with_capacity(1); // most connections only have one path
571        let mut addrs_to_paths = BTreeMap::new();
572
573        let local_addr = initial_path.local_addr;
574        let peer_addr = initial_path.peer_addr;
575
576        // As it is the first path, it is active by default.
577        initial_path.active = true;
578
579        // Enable path MTU Discovery and start probing with the largest datagram
580        // size.
581        if enable_pmtud {
582            initial_path.pmtud.should_probe(enable_pmtud);
583            initial_path.pmtud.set_probe_size(max_send_udp_payload_size);
584            initial_path.pmtud.enable(enable_pmtud);
585        }
586
587        let active_path_id = paths.insert(initial_path);
588        addrs_to_paths.insert((local_addr, peer_addr), active_path_id);
589
590        Self {
591            paths,
592            max_concurrent_paths,
593            addrs_to_paths,
594            events: VecDeque::new(),
595            is_server,
596        }
597    }
598
599    /// Gets an immutable reference to the path identified by `path_id`. If the
600    /// provided `path_id` does not identify any current `Path`, returns an
601    /// [`InvalidState`].
602    ///
603    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
604    #[inline]
605    pub fn get(&self, path_id: usize) -> Result<&Path> {
606        self.paths.get(path_id).ok_or(Error::InvalidState)
607    }
608
609    /// Gets a mutable reference to the path identified by `path_id`. If the
610    /// provided `path_id` does not identify any current `Path`, returns an
611    /// [`InvalidState`].
612    ///
613    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
614    #[inline]
615    pub fn get_mut(&mut self, path_id: usize) -> Result<&mut Path> {
616        self.paths.get_mut(path_id).ok_or(Error::InvalidState)
617    }
618
619    #[inline]
620    /// Gets an immutable reference to the active path with the value of the
621    /// lowest identifier. If there is no active path, returns `None`.
622    pub fn get_active_with_pid(&self) -> Option<(usize, &Path)> {
623        self.paths.iter().find(|(_, p)| p.active())
624    }
625
626    /// Gets an immutable reference to the active path with the lowest
627    /// identifier. If there is no active path, returns an [`InvalidState`].
628    ///
629    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
630    #[inline]
631    pub fn get_active(&self) -> Result<&Path> {
632        self.get_active_with_pid()
633            .map(|(_, p)| p)
634            .ok_or(Error::InvalidState)
635    }
636
637    /// Gets the lowest active path identifier. If there is no active path,
638    /// returns an [`InvalidState`].
639    ///
640    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
641    #[inline]
642    pub fn get_active_path_id(&self) -> Result<usize> {
643        self.get_active_with_pid()
644            .map(|(pid, _)| pid)
645            .ok_or(Error::InvalidState)
646    }
647
648    /// Gets an mutable reference to the active path with the lowest identifier.
649    /// If there is no active path, returns an [`InvalidState`].
650    ///
651    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
652    #[inline]
653    pub fn get_active_mut(&mut self) -> Result<&mut Path> {
654        self.paths
655            .iter_mut()
656            .map(|(_, p)| p)
657            .find(|p| p.active())
658            .ok_or(Error::InvalidState)
659    }
660
661    /// Returns an iterator over all existing paths.
662    #[inline]
663    pub fn iter(&self) -> slab::Iter<Path> {
664        self.paths.iter()
665    }
666
667    /// Returns a mutable iterator over all existing paths.
668    #[inline]
669    pub fn iter_mut(&mut self) -> slab::IterMut<Path> {
670        self.paths.iter_mut()
671    }
672
673    /// Returns the number of existing paths.
674    #[inline]
675    pub fn len(&self) -> usize {
676        self.paths.len()
677    }
678
679    /// Returns the `Path` identifier related to the provided `addrs`.
680    #[inline]
681    pub fn path_id_from_addrs(
682        &self, addrs: &(SocketAddr, SocketAddr),
683    ) -> Option<usize> {
684        self.addrs_to_paths.get(addrs).copied()
685    }
686
687    /// Checks if creating a new path will not exceed the current `self.paths`
688    /// capacity. If yes, this method tries to remove one unused path. If it
689    /// fails to do so, returns [`Done`].
690    ///
691    /// [`Done`]: enum.Error.html#variant.Done
692    fn make_room_for_new_path(&mut self) -> Result<()> {
693        if self.paths.len() < self.max_concurrent_paths {
694            return Ok(());
695        }
696
697        let (pid_to_remove, _) = self
698            .paths
699            .iter()
700            .find(|(_, p)| p.unused())
701            .ok_or(Error::Done)?;
702
703        let path = self.paths.remove(pid_to_remove);
704        self.addrs_to_paths
705            .remove(&(path.local_addr, path.peer_addr));
706
707        self.notify_event(PathEvent::Closed(path.local_addr, path.peer_addr));
708
709        Ok(())
710    }
711
712    /// Records the provided `Path` and returns its assigned identifier.
713    ///
714    /// On success, this method takes care of creating a notification to the
715    /// serving application, if it serves a server-side connection.
716    ///
717    /// If there are already `max_concurrent_paths` currently recorded, this
718    /// method tries to remove an unused `Path` first. If it fails to do so,
719    /// it returns [`Done`].
720    ///
721    /// [`Done`]: enum.Error.html#variant.Done
722    pub fn insert_path(&mut self, path: Path, is_server: bool) -> Result<usize> {
723        self.make_room_for_new_path()?;
724
725        let local_addr = path.local_addr;
726        let peer_addr = path.peer_addr;
727
728        let pid = self.paths.insert(path);
729        self.addrs_to_paths.insert((local_addr, peer_addr), pid);
730
731        // Notifies the application if we are in server mode.
732        if is_server {
733            self.notify_event(PathEvent::New(local_addr, peer_addr));
734        }
735
736        Ok(pid)
737    }
738
739    /// Notifies a path event to the application served by the connection.
740    pub fn notify_event(&mut self, ev: PathEvent) {
741        self.events.push_back(ev);
742    }
743
744    /// Gets the first path event to be notified to the application.
745    pub fn pop_event(&mut self) -> Option<PathEvent> {
746        self.events.pop_front()
747    }
748
749    /// Notifies all failed validations to the application.
750    pub fn notify_failed_validations(&mut self) {
751        let validation_failed = self
752            .paths
753            .iter_mut()
754            .filter(|(_, p)| p.validation_failed() && !p.failure_notified);
755
756        for (_, p) in validation_failed {
757            self.events.push_back(PathEvent::FailedValidation(
758                p.local_addr,
759                p.peer_addr,
760            ));
761
762            p.failure_notified = true;
763        }
764    }
765
766    /// Finds a path candidate to be active and returns its identifier.
767    pub fn find_candidate_path(&self) -> Option<usize> {
768        // TODO: also consider unvalidated paths if there are no more validated.
769        self.paths
770            .iter()
771            .find(|(_, p)| p.usable())
772            .map(|(pid, _)| pid)
773    }
774
775    /// Handles incoming PATH_RESPONSE data.
776    pub fn on_response_received(&mut self, data: [u8; 8]) -> Result<()> {
777        let active_pid = self.get_active_path_id()?;
778
779        let challenge_pending =
780            self.iter_mut().find(|(_, p)| p.has_pending_challenge(data));
781
782        if let Some((pid, p)) = challenge_pending {
783            if p.on_response_received(data) {
784                let local_addr = p.local_addr;
785                let peer_addr = p.peer_addr;
786                let was_migrating = p.migrating;
787
788                p.migrating = false;
789
790                // Notifies the application.
791                self.notify_event(PathEvent::Validated(local_addr, peer_addr));
792
793                // If this path was the candidate for migration, notifies the
794                // application.
795                if pid == active_pid && was_migrating {
796                    self.notify_event(PathEvent::PeerMigrated(
797                        local_addr, peer_addr,
798                    ));
799                }
800            }
801        }
802        Ok(())
803    }
804
805    /// Sets the path with identifier 'path_id' to be active.
806    ///
807    /// There can be exactly one active path on which non-probing packets can be
808    /// sent. If another path is marked as active, it will be superseded by the
809    /// one having `path_id` as identifier.
810    ///
811    /// A server should always ensure that the active path is validated. If it
812    /// is already the case, it notifies the application that the connection
813    /// migrated. Otherwise, it triggers a path validation and defers the
814    /// notification once it is actually validated.
815    pub fn set_active_path(&mut self, path_id: usize) -> Result<()> {
816        let is_server = self.is_server;
817
818        if let Ok(old_active_path) = self.get_active_mut() {
819            old_active_path.active = false;
820        }
821
822        let new_active_path = self.get_mut(path_id)?;
823        new_active_path.active = true;
824
825        if is_server {
826            if new_active_path.validated() {
827                let local_addr = new_active_path.local_addr();
828                let peer_addr = new_active_path.peer_addr();
829
830                self.notify_event(PathEvent::PeerMigrated(local_addr, peer_addr));
831            } else {
832                new_active_path.migrating = true;
833
834                // Requests path validation if needed.
835                if !new_active_path.under_validation() {
836                    new_active_path.request_validation();
837                }
838            }
839        }
840
841        Ok(())
842    }
843}
844
845/// Statistics about the path of a connection.
846///
847/// A connection’s path statistics can be collected using the [`path_stats()`]
848/// method.
849///
850/// [`path_stats()`]: struct.Connection.html#method.path_stats
851#[derive(Clone)]
852pub struct PathStats {
853    /// The local address of the path.
854    pub local_addr: SocketAddr,
855
856    /// The peer address of the path.
857    pub peer_addr: SocketAddr,
858
859    /// The path validation state.
860    pub validation_state: PathState,
861
862    /// Whether the path is marked as active.
863    pub active: bool,
864
865    /// The number of QUIC packets received.
866    pub recv: usize,
867
868    /// The number of QUIC packets sent.
869    pub sent: usize,
870
871    /// The number of QUIC packets that were lost.
872    pub lost: usize,
873
874    /// The number of sent QUIC packets with retransmitted data.
875    pub retrans: usize,
876
877    /// The number of DATAGRAM frames received.
878    pub dgram_recv: usize,
879
880    /// The number of DATAGRAM frames sent.
881    pub dgram_sent: usize,
882
883    /// The estimated round-trip time of the connection.
884    pub rtt: time::Duration,
885
886    /// The minimum round-trip time observed.
887    pub min_rtt: Option<time::Duration>,
888
889    /// The maximum round-trip time observed.
890    pub max_rtt: Option<time::Duration>,
891
892    /// The estimated round-trip time variation in samples using a mean
893    /// variation.
894    pub rttvar: time::Duration,
895
896    /// The size of the connection's congestion window in bytes.
897    pub cwnd: usize,
898
899    /// The number of sent bytes.
900    pub sent_bytes: u64,
901
902    /// The number of received bytes.
903    pub recv_bytes: u64,
904
905    /// The number of bytes lost.
906    pub lost_bytes: u64,
907
908    /// The number of stream bytes retransmitted.
909    pub stream_retrans_bytes: u64,
910
911    /// The current PMTU for the connection.
912    pub pmtu: usize,
913
914    /// The most recent data delivery rate estimate in bytes/s.
915    ///
916    /// Note that this value could be inaccurate if the application does not
917    /// respect pacing hints (see [`SendInfo.at`] and [Pacing] for more
918    /// details).
919    ///
920    /// [`SendInfo.at`]: struct.SendInfo.html#structfield.at
921    /// [Pacing]: index.html#pacing
922    pub delivery_rate: u64,
923}
924
925impl std::fmt::Debug for PathStats {
926    #[inline]
927    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
928        write!(
929            f,
930            "local_addr={:?} peer_addr={:?} ",
931            self.local_addr, self.peer_addr,
932        )?;
933        write!(
934            f,
935            "validation_state={:?} active={} ",
936            self.validation_state, self.active,
937        )?;
938        write!(
939            f,
940            "recv={} sent={} lost={} retrans={} rtt={:?} min_rtt={:?} rttvar={:?} cwnd={}",
941            self.recv, self.sent, self.lost, self.retrans, self.rtt, self.min_rtt, self.rttvar, self.cwnd,
942        )?;
943
944        write!(
945            f,
946            " sent_bytes={} recv_bytes={} lost_bytes={}",
947            self.sent_bytes, self.recv_bytes, self.lost_bytes,
948        )?;
949
950        write!(
951            f,
952            " stream_retrans_bytes={} pmtu={} delivery_rate={}",
953            self.stream_retrans_bytes, self.pmtu, self.delivery_rate,
954        )
955    }
956}
957
958#[cfg(test)]
959mod tests {
960    use crate::rand;
961    use crate::MIN_CLIENT_INITIAL_LEN;
962
963    use crate::recovery::RecoveryConfig;
964    use crate::Config;
965
966    use super::*;
967
968    #[test]
969    fn path_validation_limited_mtu() {
970        let client_addr = "127.0.0.1:1234".parse().unwrap();
971        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
972        let server_addr = "127.0.0.1:4321".parse().unwrap();
973
974        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
975        let recovery_config = RecoveryConfig::from_config(&config);
976
977        let path = Path::new(
978            client_addr,
979            server_addr,
980            &recovery_config,
981            config.path_challenge_recv_max_queue_len,
982            1200,
983            true,
984        );
985        let mut path_mgr = PathMap::new(path, 2, false, true, 1200);
986
987        let probed_path = Path::new(
988            client_addr_2,
989            server_addr,
990            &recovery_config,
991            config.path_challenge_recv_max_queue_len,
992            1200,
993            false,
994        );
995        path_mgr.insert_path(probed_path, false).unwrap();
996
997        let pid = path_mgr
998            .path_id_from_addrs(&(client_addr_2, server_addr))
999            .unwrap();
1000        path_mgr.get_mut(pid).unwrap().request_validation();
1001        assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1002        assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1003
1004        // Fake sending of PathChallenge in a packet of MIN_CLIENT_INITIAL_LEN - 1
1005        // bytes.
1006        let data = rand::rand_u64().to_be_bytes();
1007        path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1008            data,
1009            MIN_CLIENT_INITIAL_LEN - 1,
1010            time::Instant::now(),
1011        );
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!(path_mgr.get_mut(pid).unwrap().state, PathState::Validating);
1018        assert_eq!(path_mgr.pop_event(), None);
1019
1020        // Receives the response. The path is reachable, but the MTU is not
1021        // validated yet.
1022        path_mgr.on_response_received(data).unwrap();
1023
1024        assert!(path_mgr.get_mut(pid).unwrap().validation_requested());
1025        assert!(path_mgr.get_mut(pid).unwrap().probing_required());
1026        assert!(path_mgr.get_mut(pid).unwrap().under_validation());
1027        assert!(!path_mgr.get_mut(pid).unwrap().validated());
1028        assert_eq!(
1029            path_mgr.get_mut(pid).unwrap().state,
1030            PathState::ValidatingMTU
1031        );
1032        assert_eq!(path_mgr.pop_event(), None);
1033
1034        // Fake sending of PathChallenge in a packet of MIN_CLIENT_INITIAL_LEN
1035        // bytes.
1036        let data = rand::rand_u64().to_be_bytes();
1037        path_mgr.get_mut(pid).unwrap().add_challenge_sent(
1038            data,
1039            MIN_CLIENT_INITIAL_LEN,
1040            time::Instant::now(),
1041        );
1042
1043        path_mgr.on_response_received(data).unwrap();
1044
1045        assert!(!path_mgr.get_mut(pid).unwrap().validation_requested());
1046        assert!(!path_mgr.get_mut(pid).unwrap().probing_required());
1047        assert!(!path_mgr.get_mut(pid).unwrap().under_validation());
1048        assert!(path_mgr.get_mut(pid).unwrap().validated());
1049        assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validated);
1050        assert_eq!(
1051            path_mgr.pop_event(),
1052            Some(PathEvent::Validated(client_addr_2, server_addr))
1053        );
1054    }
1055
1056    #[test]
1057    fn multiple_probes() {
1058        let client_addr = "127.0.0.1:1234".parse().unwrap();
1059        let server_addr = "127.0.0.1:4321".parse().unwrap();
1060
1061        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1062        let recovery_config = RecoveryConfig::from_config(&config);
1063
1064        let path = Path::new(
1065            client_addr,
1066            server_addr,
1067            &recovery_config,
1068            config.path_challenge_recv_max_queue_len,
1069            1200,
1070            true,
1071        );
1072        let mut client_path_mgr = PathMap::new(path, 2, false, false, 1200);
1073        let mut server_path = Path::new(
1074            server_addr,
1075            client_addr,
1076            &recovery_config,
1077            config.path_challenge_recv_max_queue_len,
1078            1200,
1079            false,
1080        );
1081
1082        let client_pid = client_path_mgr
1083            .path_id_from_addrs(&(client_addr, server_addr))
1084            .unwrap();
1085
1086        // First probe.
1087        let data = rand::rand_u64().to_be_bytes();
1088
1089        client_path_mgr
1090            .get_mut(client_pid)
1091            .unwrap()
1092            .add_challenge_sent(
1093                data,
1094                MIN_CLIENT_INITIAL_LEN,
1095                time::Instant::now(),
1096            );
1097
1098        // Second probe.
1099        let data_2 = rand::rand_u64().to_be_bytes();
1100
1101        client_path_mgr
1102            .get_mut(client_pid)
1103            .unwrap()
1104            .add_challenge_sent(
1105                data_2,
1106                MIN_CLIENT_INITIAL_LEN,
1107                time::Instant::now(),
1108            );
1109        assert_eq!(
1110            client_path_mgr
1111                .get(client_pid)
1112                .unwrap()
1113                .in_flight_challenges
1114                .len(),
1115            2
1116        );
1117
1118        // If we receive multiple challenges, we can store them.
1119        server_path.on_challenge_received(data);
1120        assert_eq!(server_path.received_challenges.len(), 1);
1121        server_path.on_challenge_received(data_2);
1122        assert_eq!(server_path.received_challenges.len(), 2);
1123
1124        // Response for first probe.
1125        client_path_mgr.on_response_received(data).unwrap();
1126        assert_eq!(
1127            client_path_mgr
1128                .get(client_pid)
1129                .unwrap()
1130                .in_flight_challenges
1131                .len(),
1132            1
1133        );
1134
1135        // Response for second probe.
1136        client_path_mgr.on_response_received(data_2).unwrap();
1137        assert_eq!(
1138            client_path_mgr
1139                .get(client_pid)
1140                .unwrap()
1141                .in_flight_challenges
1142                .len(),
1143            0
1144        );
1145    }
1146
1147    #[test]
1148    fn too_many_probes() {
1149        let client_addr = "127.0.0.1:1234".parse().unwrap();
1150        let server_addr = "127.0.0.1:4321".parse().unwrap();
1151
1152        // Default to DEFAULT_MAX_PATH_CHALLENGE_RX_QUEUE_LEN
1153        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
1154        let recovery_config = RecoveryConfig::from_config(&config);
1155
1156        let path = Path::new(
1157            client_addr,
1158            server_addr,
1159            &recovery_config,
1160            config.path_challenge_recv_max_queue_len,
1161            1200,
1162            true,
1163        );
1164        let mut client_path_mgr = PathMap::new(path, 2, false, false, 1200);
1165        let mut server_path = Path::new(
1166            server_addr,
1167            client_addr,
1168            &recovery_config,
1169            config.path_challenge_recv_max_queue_len,
1170            1200,
1171            false,
1172        );
1173
1174        let client_pid = client_path_mgr
1175            .path_id_from_addrs(&(client_addr, server_addr))
1176            .unwrap();
1177
1178        // First probe.
1179        let data = rand::rand_u64().to_be_bytes();
1180
1181        client_path_mgr
1182            .get_mut(client_pid)
1183            .unwrap()
1184            .add_challenge_sent(
1185                data,
1186                MIN_CLIENT_INITIAL_LEN,
1187                time::Instant::now(),
1188            );
1189
1190        // Second probe.
1191        let data_2 = rand::rand_u64().to_be_bytes();
1192
1193        client_path_mgr
1194            .get_mut(client_pid)
1195            .unwrap()
1196            .add_challenge_sent(
1197                data_2,
1198                MIN_CLIENT_INITIAL_LEN,
1199                time::Instant::now(),
1200            );
1201        assert_eq!(
1202            client_path_mgr
1203                .get(client_pid)
1204                .unwrap()
1205                .in_flight_challenges
1206                .len(),
1207            2
1208        );
1209
1210        // Third probe.
1211        let data_3 = rand::rand_u64().to_be_bytes();
1212
1213        client_path_mgr
1214            .get_mut(client_pid)
1215            .unwrap()
1216            .add_challenge_sent(
1217                data_3,
1218                MIN_CLIENT_INITIAL_LEN,
1219                time::Instant::now(),
1220            );
1221        assert_eq!(
1222            client_path_mgr
1223                .get(client_pid)
1224                .unwrap()
1225                .in_flight_challenges
1226                .len(),
1227            3
1228        );
1229
1230        // Fourth probe.
1231        let data_4 = rand::rand_u64().to_be_bytes();
1232
1233        client_path_mgr
1234            .get_mut(client_pid)
1235            .unwrap()
1236            .add_challenge_sent(
1237                data_4,
1238                MIN_CLIENT_INITIAL_LEN,
1239                time::Instant::now(),
1240            );
1241        assert_eq!(
1242            client_path_mgr
1243                .get(client_pid)
1244                .unwrap()
1245                .in_flight_challenges
1246                .len(),
1247            4
1248        );
1249
1250        // If we receive multiple challenges, we can store them up to our queue
1251        // size.
1252        server_path.on_challenge_received(data);
1253        assert_eq!(server_path.received_challenges.len(), 1);
1254        server_path.on_challenge_received(data_2);
1255        assert_eq!(server_path.received_challenges.len(), 2);
1256        server_path.on_challenge_received(data_3);
1257        assert_eq!(server_path.received_challenges.len(), 3);
1258        server_path.on_challenge_received(data_4);
1259        assert_eq!(server_path.received_challenges.len(), 3);
1260
1261        // Response for first probe.
1262        client_path_mgr.on_response_received(data).unwrap();
1263        assert_eq!(
1264            client_path_mgr
1265                .get(client_pid)
1266                .unwrap()
1267                .in_flight_challenges
1268                .len(),
1269            3
1270        );
1271
1272        // Response for second probe.
1273        client_path_mgr.on_response_received(data_2).unwrap();
1274        assert_eq!(
1275            client_path_mgr
1276                .get(client_pid)
1277                .unwrap()
1278                .in_flight_challenges
1279                .len(),
1280            2
1281        );
1282
1283        // Response for third probe.
1284        client_path_mgr.on_response_received(data_3).unwrap();
1285        assert_eq!(
1286            client_path_mgr
1287                .get(client_pid)
1288                .unwrap()
1289                .in_flight_challenges
1290                .len(),
1291            1
1292        );
1293
1294        // There will never be a response for fourth probe...
1295    }
1296}