noq_proto/connection/
mod.rs

1use std::{
2    cmp,
3    collections::{BTreeMap, VecDeque, btree_map},
4    convert::TryFrom,
5    fmt, io, mem,
6    net::SocketAddr,
7    num::{NonZeroU32, NonZeroUsize},
8    sync::Arc,
9};
10
11use bytes::{Bytes, BytesMut};
12use frame::StreamMetaVec;
13
14use rand::{RngExt, SeedableRng, rngs::StdRng};
15use rustc_hash::FxHashMap;
16use thiserror::Error;
17use tracing::{debug, error, trace, trace_span, warn};
18
19use crate::{
20    Dir, Duration, EndpointConfig, FourTuple, Frame, INITIAL_MTU, Instant, MAX_CID_SIZE,
21    MAX_STREAM_COUNT, MIN_INITIAL_SIZE, Side, StreamId, TIMER_GRANULARITY, TokenStore, Transmit,
22    TransportError, TransportErrorCode, VarInt,
23    cid_generator::ConnectionIdGenerator,
24    cid_queue::CidQueue,
25    config::{ServerConfig, TransportConfig},
26    congestion::Controller,
27    connection::{
28        paths::PathRetransmits,
29        qlog::{QlogRecvPacket, QlogSink},
30        spaces::LostPacket,
31        stats::PathStatsMap,
32        timer::{ConnTimer, PathTimer},
33    },
34    crypto::{self, Keys},
35    frame::{
36        self, Close, DataBlocked, Datagram, FrameStruct, NewToken, ObservedAddr, StreamDataBlocked,
37        StreamsBlocked,
38    },
39    n0_nat_traversal,
40    packet::{
41        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet,
42        PacketNumber, PartialDecode, SpaceId,
43    },
44    range_set::ArrayRangeSet,
45    shared::{
46        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
47        EndpointEvent, EndpointEventInner,
48    },
49    token::{ResetToken, Token, TokenPayload},
50    transport_parameters::TransportParameters,
51};
52
53mod ack_frequency;
54use ack_frequency::AckFrequencyState;
55
56mod assembler;
57pub use assembler::Chunk;
58
59mod cid_state;
60use cid_state::CidState;
61
62mod datagrams;
63use datagrams::DatagramState;
64pub use datagrams::{Datagrams, SendDatagramError};
65
66mod mtud;
67mod pacing;
68
69mod packet_builder;
70use packet_builder::{PacketBuilder, PadDatagram};
71
72mod packet_crypto;
73use packet_crypto::CryptoState;
74pub(crate) use packet_crypto::EncryptionLevel;
75
76mod paths;
77pub use paths::{
78    ClosedPath, PathAbandonReason, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError,
79};
80use paths::{PathData, PathState};
81
82pub(crate) mod qlog;
83pub(crate) mod send_buffer;
84
85pub(crate) mod spaces;
86#[cfg(fuzzing)]
87pub use spaces::Retransmits;
88#[cfg(not(fuzzing))]
89use spaces::Retransmits;
90pub(crate) use spaces::SpaceKind;
91use spaces::{OpenStatus, PacketSpace, SendableFrames, SentPacket, ThinRetransmits};
92
93mod stats;
94pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats};
95
96mod streams;
97#[cfg(fuzzing)]
98pub use streams::StreamsState;
99#[cfg(not(fuzzing))]
100use streams::StreamsState;
101pub use streams::{
102    Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream,
103    ShouldTransmit, StreamEvent, Streams, WriteError,
104};
105
106mod timer;
107use timer::{Timer, TimerTable};
108
109mod transmit_buf;
110use transmit_buf::TransmitBuf;
111
112mod state;
113
114#[cfg(not(fuzzing))]
115use state::State;
116#[cfg(fuzzing)]
117pub use state::State;
118use state::StateType;
119
120/// Protocol state and logic for a single QUIC connection
121///
122/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application
123/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and
124/// expects timeouts through various methods. A number of simple getter methods are exposed
125/// to allow callers to inspect some of the connection state.
126///
127/// `Connection` has roughly 4 types of methods:
128///
129/// - A. Simple getters, taking `&self`
130/// - B. Handlers for incoming events from the network or system, named `handle_*`.
131/// - C. State machine mutators, for incoming commands from the application. For convenience we
132///   refer to this as "performing I/O" below, however as per the design of this library none of the
133///   functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and
134///   [`write`](SendStream::write), but also things like [`reset`](SendStream::reset).
135/// - D. Polling functions for outgoing events or actions for the caller to take, named `poll_*`.
136///
137/// The simplest way to use this API correctly is to call (B) and (C) whenever
138/// appropriate, then after each of those calls, as soon as feasible call all
139/// polling methods (D) and deal with their outputs appropriately, e.g. by
140/// passing it to the application or by making a system-level I/O call. You
141/// should call the polling functions in this order:
142///
143/// 1. [`poll_transmit`](Self::poll_transmit)
144/// 2. [`poll_timeout`](Self::poll_timeout)
145/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events)
146/// 4. [`poll`](Self::poll)
147///
148/// Currently the only actual dependency is from (2) to (1), however additional
149/// dependencies may be added in future, so the above order is recommended.
150///
151/// (A) may be called whenever desired.
152///
153/// Care should be made to ensure that the input events represent monotonically
154/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout)
155/// with events of the same [`Instant`] may be interleaved in any order with a
156/// call to [`handle_event`](Self::handle_event) at that same instant; however
157/// events or timeouts with different instants must not be interleaved.
158pub struct Connection {
159    endpoint_config: Arc<EndpointConfig>,
160    config: Arc<TransportConfig>,
161    rng: StdRng,
162    /// Consolidated cryptographic state
163    crypto_state: CryptoState,
164    /// The CID we initially chose, for use during the handshake
165    handshake_cid: ConnectionId,
166    /// The CID the peer initially chose, for use during the handshake
167    remote_handshake_cid: ConnectionId,
168    /// The [`PathData`] for each path
169    ///
170    /// This needs to be ordered because [`Connection::poll_transmit`] needs to
171    /// deterministically select the next PathId to send on.
172    // TODO(flub): well does it really? But deterministic is nice for now.
173    paths: BTreeMap<PathId, PathState>,
174    /// Counter to uniquely identify every [`PathData`] created in this connection.
175    ///
176    /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all
177    /// [`PathData`]s created in the lifetime of this connection. This helps identify the
178    /// correct path when RFC9000-style migrations happen, even when they are
179    /// aborted.
180    ///
181    /// Multipath does not change this, each path can also undergo RFC9000-style
182    /// migrations. So a single multipath path ID could see several [`PathData`]s each with
183    /// their unique [`PathData::generation].
184    path_generation_counter: u64,
185    /// Whether MTU detection is supported in this environment
186    allow_mtud: bool,
187    state: State,
188    side: ConnectionSide,
189    /// Transport parameters set by the peer
190    peer_params: TransportParameters,
191    /// Source ConnectionId of the first packet received from the peer
192    original_remote_cid: ConnectionId,
193    /// Destination ConnectionId sent by the client on the first Initial
194    initial_dst_cid: ConnectionId,
195    /// The value that the server included in the Source Connection ID field of a Retry packet, if
196    /// one was received
197    retry_src_cid: Option<ConnectionId>,
198    /// Events returned by [`Connection::poll`]
199    events: VecDeque<Event>,
200    endpoint_events: VecDeque<EndpointEventInner>,
201    /// Whether the spin bit is in use for this connection
202    spin_enabled: bool,
203    /// Outgoing spin bit state
204    spin: bool,
205    /// Packet number spaces: initial, handshake, 1-RTT
206    spaces: [PacketSpace; 3],
207    /// Highest usable packet space.
208    highest_space: SpaceKind,
209    /// Negotiated idle timeout
210    idle_timeout: Option<Duration>,
211    timers: TimerTable,
212    /// Number of packets received which could not be authenticated
213    authentication_failures: u64,
214
215    //
216    // Queued non-retransmittable 1-RTT data
217    /// If the CONNECTION_CLOSE frame needs to be sent
218    connection_close_pending: bool,
219
220    //
221    // ACK frequency
222    ack_frequency: AckFrequencyState,
223
224    //
225    // Congestion Control
226    /// Whether the most recently received packet had an ECN codepoint set
227    receiving_ecn: bool,
228    /// Number of packets authenticated
229    total_authed_packets: u64,
230
231    //
232    // ObservedAddr
233    /// Sequence number for the next observed address frame sent to the peer.
234    next_observed_addr_seq_no: VarInt,
235
236    streams: StreamsState,
237    /// Active and surplus CIDs issued by the remote, for future use on new paths.
238    ///
239    /// These are given out before multiple paths exist, also for paths that will never
240    /// exist.  So if multipath is supported the number of paths here will be higher than
241    /// the actual number of paths in use.
242    remote_cids: FxHashMap<PathId, CidQueue>,
243    /// Attributes of CIDs generated by local endpoint
244    ///
245    /// Any path that is allowed to be opened is present in this map, as well as the already
246    /// opened paths. However since CIDs are issued async by the endpoint driver via
247    /// connection events it can not be used to know if CIDs have been issued for a path or
248    /// not. See [`Connection::max_path_id_with_cids`] for this.
249    local_cid_state: FxHashMap<PathId, CidState>,
250    /// State of the unreliable datagram extension
251    datagrams: DatagramState,
252    /// Path level statistics.
253    path_stats: PathStatsMap,
254    /// Accumulated stats of all discarded paths.
255    ///
256    /// The connection-level stats returned by [`Self::stats`] are the sum of the stats of
257    /// all the paths. However once a path is discarded it gets added to this field instead
258    /// so we do not have to keep an ever growing number of paths stats in memory.
259    partial_stats: ConnectionStats,
260    /// QUIC version used for the connection.
261    version: u32,
262
263    //
264    // Multipath
265    /// Maximum number of concurrent paths
266    ///
267    /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even
268    /// when multipath is disabled this will be set to 1, it is not used in that case
269    /// though.
270    max_concurrent_paths: NonZeroU32,
271    /// Local maximum [`PathId`] to be used
272    ///
273    /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath
274    /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of
275    /// the highest MAX_PATH_ID frame sent.
276    ///
277    /// Any path with an ID equal or below this [`PathId`] is either:
278    ///
279    /// - Abandoned, if it is also in [`Connection::abandoned_paths`].
280    /// - Open, in this case it is present in [`Connection::paths`]
281    /// - Not yet opened, if it is in neither of these two places.
282    ///
283    /// Note that for not-yet-open there may or may not be any CIDs issued. See
284    /// [`Connection::max_path_id_with_cids`].
285    local_max_path_id: PathId,
286    /// Remote's maximum [`PathId`] to be used
287    ///
288    /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when
289    /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit
290    /// by sending [`Frame::MaxPathId`] frames.
291    remote_max_path_id: PathId,
292    /// The greatest [`PathId`] we have issued CIDs for
293    ///
294    /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not
295    /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued
296    /// since they are issued asynchronously by the endpoint driver.
297    max_path_id_with_cids: PathId,
298    /// The paths already abandoned
299    ///
300    /// They may still have some state left in [`Connection::paths`] or
301    /// [`Connection::local_cid_state`] since some of this has to be kept around for some
302    /// time after a path is abandoned.
303    abandoned_paths: AbandonedPaths,
304
305    /// State for n0's (<https://n0.computer>) nat traversal protocol.
306    n0_nat_traversal: n0_nat_traversal::State,
307    qlog: QlogSink,
308}
309
310impl Connection {
311    pub(crate) fn new(
312        endpoint_config: Arc<EndpointConfig>,
313        config: Arc<TransportConfig>,
314        init_cid: ConnectionId,
315        local_cid: ConnectionId,
316        remote_cid: ConnectionId,
317        network_path: FourTuple,
318        crypto: Box<dyn crypto::Session>,
319        cid_gen: &dyn ConnectionIdGenerator,
320        now: Instant,
321        version: u32,
322        allow_mtud: bool,
323        rng_seed: [u8; 32],
324        side_args: SideArgs,
325        qlog: QlogSink,
326    ) -> Self {
327        let pref_addr_cid = side_args.pref_addr_cid();
328        let path_validated = side_args.path_validated();
329        let connection_side = ConnectionSide::from(side_args);
330        let side = connection_side.side();
331        let mut rng = StdRng::from_seed(rng_seed);
332        let mut initial_space = PacketSpace::new(now, SpaceId::Initial, &mut rng);
333        let mut handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng);
334        #[cfg(test)]
335        let mut data_space = match config.deterministic_packet_numbers {
336            true => PacketSpace::new_deterministic(now, SpaceId::Data),
337            false => PacketSpace::new(now, SpaceId::Data, &mut rng),
338        };
339        #[cfg(not(test))]
340        let mut data_space = PacketSpace::new(now, SpaceId::Data, &mut rng);
341
342        // The spaces for PathId::ZERO do not need the PathEvent::Established event.
343        initial_space.for_path(PathId::ZERO).open_status = OpenStatus::Informed;
344        handshake_space.for_path(PathId::ZERO).open_status = OpenStatus::Informed;
345        data_space.for_path(PathId::ZERO).open_status = OpenStatus::Informed;
346
347        let state = State::handshake(state::Handshake {
348            remote_cid_set: side.is_server(),
349            expected_token: Bytes::new(),
350            client_hello: None,
351            allow_server_migration: side.is_client() && config.server_handshake_migration,
352        });
353        let local_cid_state = FxHashMap::from_iter([(
354            PathId::ZERO,
355            CidState::new(
356                cid_gen.cid_len(),
357                cid_gen.cid_lifetime(),
358                now,
359                if pref_addr_cid.is_some() { 2 } else { 1 },
360            ),
361        )]);
362
363        let mut this = Self {
364            endpoint_config,
365            crypto_state: CryptoState::new(crypto, init_cid, side, &mut rng),
366            handshake_cid: local_cid,
367            remote_handshake_cid: remote_cid,
368            local_cid_state,
369            paths: BTreeMap::from_iter([(
370                PathId::ZERO,
371                PathState {
372                    data: PathData::new(network_path, allow_mtud, None, 0, now, &config),
373                    prev: None,
374                },
375            )]),
376            path_generation_counter: 0,
377            allow_mtud,
378            state,
379            side: connection_side,
380            peer_params: TransportParameters::default(),
381            original_remote_cid: remote_cid,
382            initial_dst_cid: init_cid,
383            retry_src_cid: None,
384            events: VecDeque::new(),
385            endpoint_events: VecDeque::new(),
386            spin_enabled: config.allow_spin && rng.random_ratio(7, 8),
387            spin: false,
388            spaces: [initial_space, handshake_space, data_space],
389            highest_space: SpaceKind::Initial,
390            idle_timeout: match config.max_idle_timeout {
391                None | Some(VarInt(0)) => None,
392                Some(dur) => Some(Duration::from_millis(dur.0)),
393            },
394            timers: TimerTable::default(),
395            authentication_failures: 0,
396            connection_close_pending: false,
397
398            ack_frequency: AckFrequencyState::new(get_max_ack_delay(
399                &TransportParameters::default(),
400            )),
401
402            receiving_ecn: false,
403            total_authed_packets: 0,
404
405            next_observed_addr_seq_no: 0u32.into(),
406
407            streams: StreamsState::new(
408                side,
409                config.max_concurrent_uni_streams,
410                config.max_concurrent_bidi_streams,
411                config.send_window,
412                config.receive_window,
413                config.stream_receive_window,
414            ),
415            datagrams: DatagramState::default(),
416            config,
417            remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]),
418            rng,
419            path_stats: Default::default(),
420            partial_stats: ConnectionStats::default(),
421            version,
422
423            // peer params are not yet known, so multipath is not enabled
424            max_concurrent_paths: NonZeroU32::MIN,
425            local_max_path_id: PathId::ZERO,
426            remote_max_path_id: PathId::ZERO,
427            max_path_id_with_cids: PathId::ZERO,
428            abandoned_paths: Default::default(),
429
430            n0_nat_traversal: Default::default(),
431            qlog,
432        };
433        if path_validated {
434            this.on_path_validated(PathId::ZERO);
435        }
436        if side.is_client() {
437            // Kick off the connection
438            this.write_crypto();
439            this.init_0rtt(now);
440        }
441        this.qlog
442            .emit_tuple_assigned(PathId::ZERO, network_path, now);
443        this
444    }
445
446    /// Returns the next time at which `handle_timeout` should be called
447    ///
448    /// The value returned may change after:
449    /// - the application performed some I/O on the connection
450    /// - a call was made to `handle_event`
451    /// - a call to `poll_transmit` returned `Some`
452    /// - a call was made to `handle_timeout`
453    #[must_use]
454    pub fn poll_timeout(&self) -> Option<Instant> {
455        self.timers.peek()
456    }
457
458    /// Returns application-facing events
459    ///
460    /// Connections should be polled for events after:
461    /// - a call was made to `handle_event`
462    /// - a call was made to `handle_timeout`
463    #[must_use]
464    pub fn poll(&mut self) -> Option<Event> {
465        if let Some(x) = self.events.pop_front() {
466            return Some(x);
467        }
468
469        if let Some(event) = self.streams.poll() {
470            return Some(Event::Stream(event));
471        }
472
473        if let Some(reason) = self.state.take_error() {
474            return Some(Event::ConnectionLost { reason });
475        }
476
477        None
478    }
479
480    /// Return endpoint-facing events
481    #[must_use]
482    pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> {
483        self.endpoint_events.pop_front().map(EndpointEvent)
484    }
485
486    /// Provide control over streams
487    #[must_use]
488    pub fn streams(&mut self) -> Streams<'_> {
489        Streams {
490            state: &mut self.streams,
491            conn_state: &self.state,
492        }
493    }
494
495    /// Provide control over streams
496    #[must_use]
497    pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> {
498        assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side());
499        RecvStream {
500            id,
501            state: &mut self.streams,
502            pending: &mut self.spaces[SpaceId::Data].pending,
503        }
504    }
505
506    /// Provide control over streams
507    #[must_use]
508    pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> {
509        assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side());
510        SendStream {
511            id,
512            state: &mut self.streams,
513            pending: &mut self.spaces[SpaceId::Data].pending,
514            conn_state: &self.state,
515        }
516    }
517
518    /// Opens a new path only if no path on the same network path currently exists.
519    ///
520    /// Returns `(path_id, true)` if the path already existed, or `(path_id, false)`
521    /// if was opened.
522    ///
523    /// If `network_path` has no local IP set, then this will open a new path
524    /// if no path exists for this remote address, independent of the existing
525    /// path's local IP. If a local IP is set, it will match against the full
526    /// four-tuple of existing paths. Not setting the local IP avoids having to
527    /// guess which local interface will be used to communicate with the remote,
528    /// should it not be known yet. We assume that if we already have a path to
529    /// the remote, the OS is likely to use the same interface to talk to said remote.
530    ///
531    /// See also [`open_path`].
532    ///
533    /// [`open_path`]: Connection::open_path
534    pub fn open_path_ensure(
535        &mut self,
536        network_path: FourTuple,
537        initial_status: PathStatus,
538        now: Instant,
539    ) -> Result<(PathId, bool), PathError> {
540        let existing_open_path = self.paths.iter().find(|(id, path)| {
541            network_path.is_probably_same_path(&path.data.network_path)
542                && !self.abandoned_paths.contains(id)
543        });
544        match existing_open_path {
545            Some((path_id, _state)) => Ok((*path_id, true)),
546            None => Ok((self.open_path(network_path, initial_status, now)?, false)),
547        }
548    }
549
550    /// Opens a new path.
551    ///
552    /// Further errors might occur and they will be emitted in [`PathEvent::Abandoned`]
553    /// events with this path id.  Once the path is opened and can carry application data it
554    /// will be reported using a [`PathEvent::Established`] event.
555    pub fn open_path(
556        &mut self,
557        network_path: FourTuple,
558        initial_status: PathStatus,
559        now: Instant,
560    ) -> Result<PathId, PathError> {
561        let Some(max_path_id) = self.max_path_id() else {
562            return Err(PathError::MultipathNotNegotiated);
563        };
564        if self.side().is_server() {
565            return Err(PathError::ServerSideNotAllowed);
566        }
567
568        let max_abandoned = self.abandoned_paths.max();
569        let max_used = self.paths.keys().last().copied();
570        let path_id = max_abandoned
571            .max(max_used)
572            .unwrap_or(PathId::ZERO)
573            .saturating_add(1u8);
574
575        if path_id > max_path_id {
576            self.spaces[SpaceId::Data].pending.paths_blocked = Some(self.remote_max_path_id);
577            return Err(PathError::MaxPathIdReached);
578        }
579        if !self.remote_cids.contains_key(&path_id) {
580            self.spaces[SpaceId::Data]
581                .pending
582                .path_cids_blocked
583                .insert(path_id, VarInt(0));
584            return Err(PathError::RemoteCidsExhausted);
585        }
586
587        let path = self.create_path(path_id, network_path, now, None);
588        path.status.local_update(initial_status);
589
590        Ok(path_id)
591    }
592
593    /// Closes a path and sends a PATH_ABANDON frame with the passed error code.
594    ///
595    /// Returns [`ClosePathError::LastOpenPath`] if this is the last open path.
596    /// It does allow closing paths which have not yet been opened, as e.g. is the case
597    /// when receiving a PATH_ABANDON from the peer for a path that was never opened locally.
598    pub fn close_path(
599        &mut self,
600        now: Instant,
601        path_id: PathId,
602        error_code: VarInt,
603    ) -> Result<(), ClosePathError> {
604        self.close_path_inner(
605            now,
606            path_id,
607            PathAbandonReason::ApplicationClosed { error_code },
608        )
609    }
610
611    /// Closes a path and sends a PATH_ABANDON frame.
612    ///
613    /// Other than [`Self::close_path`] this allows to specify the reason for the path being closed.
614    /// Internally, this should be used over [`Self::close_path`].
615    pub(crate) fn close_path_inner(
616        &mut self,
617        now: Instant,
618        path_id: PathId,
619        reason: PathAbandonReason,
620    ) -> Result<(), ClosePathError> {
621        if self.state.is_drained() {
622            return Ok(());
623        }
624
625        if !self.is_multipath_negotiated() {
626            return Err(ClosePathError::MultipathNotNegotiated);
627        }
628        if self.abandoned_paths.contains(&path_id)
629            || Some(path_id) > self.max_path_id()
630            || !self.paths.contains_key(&path_id)
631        {
632            return Err(ClosePathError::ClosedPath);
633        }
634
635        let is_last_path = !self
636            .paths
637            .keys()
638            .any(|id| *id != path_id && !self.abandoned_paths.contains(id));
639
640        if is_last_path && !reason.is_remote() {
641            return Err(ClosePathError::LastOpenPath);
642        }
643
644        self.abandon_path(now, path_id, reason);
645
646        // When the remote abandons our last path, start a grace timer to allow
647        // the application to open a replacement path.
648        // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8
649        if is_last_path {
650            // The spec suggests 1 PTO, but we use 3 * PTO to account for
651            // packet loss when opening a replacement path. Uses initial RTT
652            // since the abandoned path's RTT estimate is no longer valid.
653            let rtt = RttEstimator::new(self.config.initial_rtt);
654            let pto = rtt.pto_base() + self.ack_frequency.max_ack_delay_for_pto();
655            let grace = pto * 3;
656            self.timers.set(
657                Timer::Conn(ConnTimer::NoAvailablePath),
658                now + grace,
659                self.qlog.with_time(now),
660            );
661        }
662
663        Ok(())
664    }
665
666    /// Unconditionally abandon a path.
667    ///
668    /// Only to be called once sure this path should be abandoned, all checks
669    /// should have happened before calling this.
670    fn abandon_path(&mut self, now: Instant, path_id: PathId, reason: PathAbandonReason) {
671        trace!(%path_id, ?reason, "abandoning path");
672
673        let pending_space = &mut self.spaces[SpaceId::Data].pending;
674        // Send PATH_ABANDON
675        pending_space
676            .path_abandon
677            .insert(path_id, reason.error_code());
678
679        // Remove pending NEW CIDs for this path
680        pending_space.new_cids.retain(|cid| cid.path_id != path_id);
681        pending_space.path_status.retain(|&id| id != path_id);
682
683        // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other
684        // paths)
685        for space in self.spaces[SpaceId::Data].iter_paths_mut() {
686            for sent_packet in space.sent_packets.values_mut() {
687                if let Some(retransmits) = sent_packet.retransmits.get_mut() {
688                    retransmits.new_cids.retain(|cid| cid.path_id != path_id);
689                    retransmits.path_status.retain(|&id| id != path_id);
690                }
691            }
692        }
693
694        // We can't send anything on abandoned paths, so we set
695        // tail-loss probes to zero.
696        // This likely doesn't do much, as the path won't even be tried for sending
697        // in poll_transmit after the path is abandoned.
698        self.spaces[SpaceId::Data].for_path(path_id).loss_probes = 0;
699
700        // Note: remote CIDs are NOT removed here. They are removed when the PATH_ABANDON
701        // frame is actually written to a packet (in populate_packet). This allows sending
702        // PATH_ABANDON on the abandoned path itself when no other path exists (#509).
703
704        self.abandoned_paths.insert(path_id);
705
706        for timer in PathTimer::VALUES {
707            // match for completeness
708            let keep_timer = match timer {
709                // These timers deal with sending and receiving PATH_CHALLENGE and
710                // PATH_RESPONSE, but now that the path is abandoned, we no longer care about
711                // these frames or their timing
712                PathTimer::PathValidationFailed | PathTimer::PathChallengeLost => false,
713                // These timers deal with the lifetime of the path. Now that the path is abandoned,
714                // these are not relevant.
715                PathTimer::PathKeepAlive | PathTimer::PathIdle => false,
716                // The path has already been informed that outstanding acks should be sent
717                // immediately
718                PathTimer::MaxAckDelay => false,
719                // This timer should not be set, for completeness it's not kept as it's set when
720                // the PATH_ABANDON frame is sent.
721                PathTimer::PathDrained => false,
722                // Sent packets still need to be identified as lost to trigger timely
723                // retransmission.
724                PathTimer::LossDetection => true,
725                // This path should not be used for sending after the PATH_ABANDON frame is sent.
726                // However, any outstanding data that should be sent before PATH_ABANDON, should
727                // still respect pacing.
728                PathTimer::Pacing => true,
729            };
730
731            if !keep_timer {
732                let qlog = self.qlog.with_time(now);
733                self.timers.stop(Timer::PerPath(path_id, timer), qlog);
734            }
735        }
736
737        // Set the loss detection timer again, as now it should only be set
738        // for time-based loss detection, not tail-loss probes, but currently it
739        // could still be set to a tail-loss probe.
740        // This will reset it to the next time-based loss time, if applicable.
741        self.set_loss_detection_timer(now, path_id);
742
743        // Emit event to the application.
744        self.events.push_back(Event::Path(PathEvent::Abandoned {
745            id: path_id,
746            reason,
747        }));
748    }
749
750    /// Gets the [`PathData`] for a known [`PathId`].
751    ///
752    /// Will panic if the path_id does not reference any known path.
753    #[track_caller]
754    fn path_data(&self, path_id: PathId) -> &PathData {
755        if let Some(data) = self.paths.get(&path_id) {
756            &data.data
757        } else {
758            panic!(
759                "unknown path: {path_id}, currently known paths: {:?}",
760                self.paths.keys().collect::<Vec<_>>()
761            );
762        }
763    }
764
765    /// Gets the [`PathData`] for a known [`PathId`].
766    ///
767    /// Will panic if the path_id does not reference any known path.
768    #[track_caller]
769    fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData {
770        &mut self.paths.get_mut(&path_id).expect("known path").data
771    }
772
773    /// Gets a reference to the [`PathData`] for a [`PathId`]
774    fn path(&self, path_id: PathId) -> Option<&PathData> {
775        self.paths.get(&path_id).map(|path_state| &path_state.data)
776    }
777
778    /// Gets a mutable reference to the [`PathData`] for a [`PathId`]
779    fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> {
780        self.paths
781            .get_mut(&path_id)
782            .map(|path_state| &mut path_state.data)
783    }
784
785    /// Returns all known paths.
786    ///
787    /// There is no guarantee any of these paths are open or usable.
788    pub fn paths(&self) -> Vec<PathId> {
789        self.paths.keys().copied().collect()
790    }
791
792    /// Gets the local [`PathStatus`] for a known [`PathId`]
793    pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> {
794        self.path(path_id)
795            .map(PathData::local_status)
796            .ok_or(ClosedPath { _private: () })
797    }
798
799    /// Returns the path's network path represented as a 4-tuple.
800    pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> {
801        self.path(path_id)
802            .map(|path| path.network_path)
803            .ok_or(ClosedPath { _private: () })
804    }
805
806    /// Sets the [`PathStatus`] for a known [`PathId`]
807    ///
808    /// Returns the previous path status on success.
809    pub fn set_path_status(
810        &mut self,
811        path_id: PathId,
812        status: PathStatus,
813    ) -> Result<PathStatus, SetPathStatusError> {
814        if !self.is_multipath_negotiated() {
815            return Err(SetPathStatusError::MultipathNotNegotiated);
816        }
817        let path = self
818            .path_mut(path_id)
819            .ok_or(SetPathStatusError::ClosedPath)?;
820        let prev = match path.status.local_update(status) {
821            Some(prev) => {
822                self.spaces[SpaceId::Data]
823                    .pending
824                    .path_status
825                    .insert(path_id);
826                prev
827            }
828            None => path.local_status(),
829        };
830        Ok(prev)
831    }
832
833    /// Returns the remote path status
834    // TODO(flub): Probably should also be some kind of path event?  Not even sure if I like
835    //    this as an API, but for now it allows me to write a test easily.
836    // TODO(flub): Technically this should be a Result<Option<PathSTatus>>?
837    pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> {
838        self.path(path_id).and_then(|path| path.remote_status())
839    }
840
841    /// Sets the max_idle_timeout for a specific path.
842    ///
843    /// If `Some`, the path idle timer is immediately re-armed. Setting `None` disables the timeout
844    /// and stops the timer.
845    ///
846    /// See [`TransportConfig::default_path_max_idle_timeout`] for details.
847    ///
848    /// Returns the previous value of the setting.
849    pub fn set_path_max_idle_timeout(
850        &mut self,
851        now: Instant,
852        path_id: PathId,
853        timeout: Option<Duration>,
854    ) -> Result<Option<Duration>, ClosedPath> {
855        let path = self
856            .paths
857            .get_mut(&path_id)
858            .ok_or(ClosedPath { _private: () })?;
859        let prev_timeout = mem::replace(&mut path.data.idle_timeout, timeout);
860
861        // The expiration instant of the timer should generally be computed from the last time the
862        // path was active. This reference instance is, however, not possible to be recovered.
863        // Previous attempts used the expiration instant and previous setting to get a "last
864        // activity" instant. Since the timer is extended to 3*PTO to prevent very small timeouts,
865        // it's likely that the computed value was in the future, and instead of accounting for
866        // elapsed idle time, it further extended the timer. Then, for consistency, we simply choose
867        // to compute the timer expiration in the same way as if the path had been immediately used.
868        self.rearm_path_max_idle_timer(now, self.highest_space, path_id);
869
870        Ok(prev_timeout)
871    }
872
873    /// Rearms or stops the state of the [`PathTimer::PathIdle`] based on the configured value in
874    /// [`PathData::idle_timeout`].
875    ///
876    /// The timer is extended to 3*PTO if such value is greater than the configured timeout,
877    /// applying the guidance of RFC9000 §10.1 to multipaths.
878    ///
879    /// The timer only applies for non-closed, multiplath-negotiated connections.
880    fn rearm_path_max_idle_timer(&mut self, now: Instant, space: SpaceKind, path_id: PathId) {
881        let timer = Timer::PerPath(path_id, PathTimer::PathIdle);
882
883        if self.state.is_closed() || !self.is_multipath_negotiated() {
884            return self.timers.stop(timer, self.qlog.with_time(now));
885        }
886
887        if let Some(timeout) = self.path_data(path_id).idle_timeout {
888            let dt = cmp::max(timeout, 3 * self.pto(space, path_id));
889            self.timers.set(timer, now + dt, self.qlog.with_time(now));
890        } else {
891            self.timers.stop(timer, self.qlog.with_time(now));
892        }
893    }
894
895    /// Sets the keep_alive_interval for a specific path
896    ///
897    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
898    ///
899    /// Returns the previous value of the setting.
900    pub fn set_path_keep_alive_interval(
901        &mut self,
902        path_id: PathId,
903        interval: Option<Duration>,
904    ) -> Result<Option<Duration>, ClosedPath> {
905        let path = self
906            .paths
907            .get_mut(&path_id)
908            .ok_or(ClosedPath { _private: () })?;
909        Ok(mem::replace(&mut path.data.keep_alive, interval))
910    }
911
912    /// Find an open, validated path that's on the same network path as the given network path.
913    ///
914    /// Returns the first path matching, even if there's multiple.
915    fn find_validated_path_on_network_path(
916        &self,
917        network_path: FourTuple,
918    ) -> Option<(&PathId, &PathState)> {
919        self.paths.iter().find(|(path_id, path_state)| {
920            path_state.data.validated
921                // Would this use the same network path, if network_path were used to send right now?
922                && network_path.is_probably_same_path(&path_state.data.network_path)
923                && !self.abandoned_paths.contains(path_id)
924        })
925        // TODO(@divma): we might want to ensure the path has been recently active to consider the
926        // address validated
927        // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given
928        // keep-alives?
929    }
930
931    /// Creates the [`PathData`] for a new [`PathId`].
932    ///
933    /// Called for incoming packets as well as when opening a new path locally.
934    fn create_path(
935        &mut self,
936        path_id: PathId,
937        network_path: FourTuple,
938        now: Instant,
939        pn: Option<u64>,
940    ) -> &mut PathData {
941        let valid_path = self.find_validated_path_on_network_path(network_path);
942        let validated = valid_path.is_some();
943        let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative());
944        let vacant_entry = match self.paths.entry(path_id) {
945            btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
946            btree_map::Entry::Occupied(occupied_entry) => {
947                return &mut occupied_entry.into_mut().data;
948            }
949        };
950
951        debug!(%validated, %path_id, %network_path, "path added");
952
953        // A new path was added. Cancel any pending NoAvailablePath grace timer.
954        self.timers.stop(
955            Timer::Conn(ConnTimer::NoAvailablePath),
956            self.qlog.with_time(now),
957        );
958        let peer_max_udp_payload_size =
959            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
960        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
961        let mut data = PathData::new(
962            network_path,
963            self.allow_mtud,
964            Some(peer_max_udp_payload_size),
965            self.path_generation_counter,
966            now,
967            &self.config,
968        );
969
970        data.validated = validated;
971        if let Some(initial_rtt) = initial_rtt {
972            data.rtt.reset_initial_rtt(initial_rtt);
973        }
974
975        // To open a path locally we need to send a packet on the path. Sending a challenge
976        // guarantees this.
977        data.pending_challenge = true;
978        data.pending.observed_address = self
979            .config
980            .address_discovery_role
981            .should_report(&self.peer_params.address_discovery_role);
982
983        let path = vacant_entry.insert(PathState { data, prev: None });
984
985        let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng);
986        if let Some(pn) = pn {
987            pn_space.dedup.insert(pn);
988        }
989        self.spaces[SpaceId::Data]
990            .number_spaces
991            .insert(path_id, pn_space);
992        self.qlog.emit_tuple_assigned(path_id, network_path, now);
993
994        // If the remote opened this path we may not have CIDs for it. For locally opened
995        // paths the caller should have already made sure we have CIDs and refused to open
996        // it if there were none.
997        if !self.remote_cids.contains_key(&path_id) {
998            debug!(%path_id, "Remote opened path without issuing CIDs");
999            self.spaces[SpaceId::Data]
1000                .pending
1001                .path_cids_blocked
1002                .insert(path_id, VarInt(0));
1003            // Do not abandon this path right away. CIDs might be in-flight still and arrive
1004            // soon. It is up to the remote to handle this situation.
1005        }
1006
1007        &mut path.data
1008    }
1009
1010    /// Returns packets to transmit
1011    ///
1012    /// Connections should be polled for transmit after:
1013    /// - the application performed some I/O on the connection
1014    /// - a call was made to `handle_event`
1015    /// - a call was made to `handle_timeout`
1016    ///
1017    /// `max_datagrams` specifies how many datagrams can be returned inside a
1018    /// single Transmit using GSO. This must be at least 1.
1019    #[must_use]
1020    pub fn poll_transmit(
1021        &mut self,
1022        now: Instant,
1023        max_datagrams: NonZeroUsize,
1024        buf: &mut Vec<u8>,
1025    ) -> Option<Transmit> {
1026        let max_datagrams = match self.config.enable_segmentation_offload {
1027            false => NonZeroUsize::MIN,
1028            true => max_datagrams,
1029        };
1030
1031        // Each call to poll_transmit can only send datagrams to one destination, because
1032        // all datagrams in a GSO batch are for the same destination.  Therefore only
1033        // datagrams for one destination address are produced for each poll_transmit call.
1034
1035        // Check whether we need to send a close message
1036        let connection_close_pending = match self.state.as_type() {
1037            StateType::Drained => {
1038                for path in self.paths.values_mut() {
1039                    path.data.app_limited = true;
1040                }
1041                return None;
1042            }
1043            StateType::Draining | StateType::Closed => {
1044                // self.connection_close_pending is only reset once the associated packet
1045                // had been encoded successfully
1046                if !self.connection_close_pending {
1047                    for path in self.paths.values_mut() {
1048                        path.data.app_limited = true;
1049                    }
1050                    return None;
1051                }
1052                true
1053            }
1054            _ => false,
1055        };
1056
1057        // Schedule an ACK_FREQUENCY frame if a new one needs to be sent.
1058        if let Some(config) = &self.config.ack_frequency_config {
1059            let rtt = self
1060                .paths
1061                .values()
1062                .map(|p| p.data.rtt.get())
1063                .min()
1064                .expect("one path exists");
1065            self.spaces[SpaceId::Data].pending.ack_frequency = self
1066                .ack_frequency
1067                .should_send_ack_frequency(rtt, config, &self.peer_params)
1068                && self.highest_space == SpaceKind::Data
1069                && self.peer_supports_ack_frequency();
1070        }
1071
1072        let mut next_path_id = self.paths.first_entry().map(|e| *e.key());
1073        while let Some(path_id) = next_path_id {
1074            if !connection_close_pending
1075                && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id)
1076            {
1077                #[cfg(test)]
1078                {
1079                    self.partial_stats.transmits_tx += 1;
1080                }
1081                return Some(transmit);
1082            }
1083
1084            let info = self.scheduling_info(path_id);
1085            if let Some(transmit) = self.poll_transmit_on_path(
1086                now,
1087                buf,
1088                path_id,
1089                max_datagrams,
1090                &info,
1091                connection_close_pending,
1092            ) {
1093                #[cfg(test)]
1094                {
1095                    self.partial_stats.transmits_tx += 1;
1096                }
1097                return Some(transmit);
1098            }
1099
1100            // Continue checking other paths, tail-loss probes may need to be sent
1101            // in all spaces.
1102            debug_assert!(
1103                buf.is_empty(),
1104                "nothing to send on path but buffer not empty"
1105            );
1106
1107            next_path_id = self.paths.keys().find(|i| **i > path_id).copied();
1108        }
1109
1110        // We didn't produce any application data packet
1111        debug_assert!(
1112            buf.is_empty(),
1113            "there was data in the buffer, but it was not sent"
1114        );
1115
1116        if self.state.is_established() {
1117            // Try MTU probing now
1118            let mut next_path_id = self.paths.first_entry().map(|e| *e.key());
1119            while let Some(path_id) = next_path_id {
1120                if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) {
1121                    #[cfg(test)]
1122                    {
1123                        self.partial_stats.transmits_tx += 1;
1124                    }
1125                    return Some(transmit);
1126                }
1127                next_path_id = self.paths.keys().find(|i| **i > path_id).copied();
1128            }
1129        }
1130
1131        None
1132    }
1133
1134    /// Computes the packet scheduling information for this path.
1135    ///
1136    /// While this information is only returned for a single path, it is important to know
1137    /// that this information remains static for the entire span of a single
1138    /// [`Connection::poll_transmit`] call. In other words, the return value is purely
1139    /// functional and only depends on the [`PathId`] **during a single** `poll_transmit`
1140    /// call. It can be computed up-front for all paths but we don't do that because it
1141    /// involves an allocation.
1142    ///
1143    /// See the inline comments for how the  packet scheduling works.
1144    ///
1145    /// # Panics
1146    ///
1147    /// This will panic if called for a path for which we do not have any [`PathData`], like
1148    /// so many other functions we have. But this is the only one to document this in its
1149    /// doc comment. Maybe that should change. Eventually we'll refactor things for this
1150    /// panic to go away.
1151    fn scheduling_info(&self, path_id: PathId) -> PathSchedulingInfo {
1152        // Such a space is preferred for SpaceKind::Data frames.
1153        let have_validated_status_available_space = self.paths.iter().any(|(path_id, path)| {
1154            self.remote_cids.contains_key(path_id)
1155                && !self.abandoned_paths.contains(path_id)
1156                && path.data.validated
1157                && path.data.local_status() == PathStatus::Available
1158        });
1159
1160        // Such a space is able to send SpaceKind::Data frames.
1161        let have_validated_space = self.paths.iter().any(|(path_id, path)| {
1162            self.remote_cids.contains_key(path_id)
1163                && !self.abandoned_paths.contains(path_id)
1164                && path.data.validated
1165        });
1166
1167        let is_handshaking = self.is_handshaking();
1168        let has_cids = self.remote_cids.contains_key(&path_id);
1169        let is_abandoned = self.abandoned_paths.contains(&path_id);
1170        let path_data = self.path_data(path_id);
1171        let validated = path_data.validated;
1172        let status = path_data.local_status();
1173
1174        // This is the core packet scheduling, whether this space ID may send
1175        // SpaceKind::Data frames.
1176        let may_send_data = has_cids
1177            && !is_abandoned
1178            && if is_handshaking {
1179                // There is only one path during the handshake. We want to
1180                // already send 0-RTT and 0.5-RTT (permitting anti-amplification
1181                // limit) data.
1182                true
1183            } else if !validated {
1184                // TODO(flub): When we have a network change we might end up
1185                //    having to abandon all paths and re-open new ones to the
1186                //    same remotes. This leaves us without any validated
1187                //    path. Perhaps we should have a way to figure out if the
1188                //    path is to a previously-validated remote address and allow
1189                //    sending data to such remotes immediately.
1190                false
1191            } else {
1192                match status {
1193                    PathStatus::Available => {
1194                        // Best possible space to send data on.
1195                        true
1196                    }
1197                    PathStatus::Backup => {
1198                        // If there is a status-available path we prefer that.
1199                        !have_validated_status_available_space
1200                    }
1201                }
1202            };
1203
1204        // CONNECTION_CLOSE is allowed to be sent on a non-validated
1205        // path. Particularly during the handshake we want to send it before the
1206        // path is validated. Later if there is no validated path available we
1207        // will also accept sending it on an un-validated path.
1208        let may_send_close = has_cids
1209            && !is_abandoned
1210            && if !validated && have_validated_status_available_space {
1211                // We have a better space to send on.
1212                false
1213            } else {
1214                // No other validated space, this is as good as it gets.
1215                true
1216            };
1217
1218        // PATH_ABANDON is normally sent together with other SpaceKind::Data frames. But if
1219        // there is no other validated space to send it on, it can be sent on the path to be
1220        // abandoned itself if that was validated.
1221        let may_self_abandon = has_cids && validated && !have_validated_space;
1222
1223        PathSchedulingInfo {
1224            is_abandoned,
1225            may_send_data,
1226            may_send_close,
1227            may_self_abandon,
1228        }
1229    }
1230
1231    fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit {
1232        debug_assert!(
1233            !transmit.is_empty(),
1234            "must not be called with an empty transmit buffer"
1235        );
1236
1237        let network_path = self.path_data(path_id).network_path;
1238        trace!(
1239            segment_size = transmit.segment_size(),
1240            last_datagram_len = transmit.len() % transmit.segment_size(),
1241            %network_path,
1242            "sending {} bytes in {} datagrams",
1243            transmit.len(),
1244            transmit.num_datagrams()
1245        );
1246        self.path_data_mut(path_id)
1247            .inc_total_sent(transmit.len() as u64);
1248
1249        self.path_stats
1250            .get_mut(path_id)
1251            .udp_tx
1252            .on_sent(transmit.num_datagrams() as u64, transmit.len());
1253
1254        Transmit {
1255            destination: network_path.remote,
1256            size: transmit.len(),
1257            ecn: if self.path_data(path_id).sending_ecn {
1258                Some(EcnCodepoint::Ect0)
1259            } else {
1260                None
1261            },
1262            segment_size: match transmit.num_datagrams() {
1263                1 => None,
1264                _ => Some(transmit.segment_size()),
1265            },
1266            src_ip: network_path.local_ip,
1267        }
1268    }
1269
1270    /// poll_transmit logic for off-path data.
1271    fn poll_transmit_off_path(
1272        &mut self,
1273        now: Instant,
1274        buf: &mut Vec<u8>,
1275        path_id: PathId,
1276    ) -> Option<Transmit> {
1277        if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) {
1278            return Some(challenge);
1279        }
1280        if let Some(response) = self.send_off_path_path_response(now, buf, path_id) {
1281            return Some(response);
1282        }
1283        if let Some(challenge) = self.send_nat_traversal_path_challenge(now, buf, path_id) {
1284            return Some(challenge);
1285        }
1286        None
1287    }
1288
1289    /// poll_transmit logic for on-path data.
1290    ///
1291    /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`]
1292    /// has 3 packet spaces, which this handles.
1293    ///
1294    /// See [`Self::poll_transmit_off_path`] for off-path data.
1295    #[must_use]
1296    fn poll_transmit_on_path(
1297        &mut self,
1298        now: Instant,
1299        buf: &mut Vec<u8>,
1300        path_id: PathId,
1301        max_datagrams: NonZeroUsize,
1302        scheduling_info: &PathSchedulingInfo,
1303        connection_close_pending: bool,
1304    ) -> Option<Transmit> {
1305        // Check if there is at least one active CID to use for sending
1306        let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else {
1307            if !self.abandoned_paths.contains(&path_id) {
1308                debug!(%path_id, "no remote CIDs for path");
1309            }
1310            return None;
1311        };
1312
1313        // Whether the last packet in the datagram must be padded so the datagram takes up
1314        // an exact size. An earlier space can decide to not fill an entire datagram and
1315        // require the next space to fill it further. But may need a specific size of the
1316        // datagram containing the packet. The final packet built in the datagram must pad
1317        // to this size.
1318        let mut pad_datagram = PadDatagram::No;
1319
1320        // The packet number of the last built packet. This is kept kept across spaces.
1321        // QUIC is supposed to have a single congestion controller for the Initial,
1322        // Handshake and Data(PathId::ZERO) spaces.
1323        let mut last_packet_number = None;
1324
1325        // If we end up not sending anything, we need to know if that was because there was
1326        // nothing to send or because we were congestion blocked.
1327        let mut congestion_blocked = false;
1328
1329        // Set the segment size to this path's MTU for on-path data.
1330        let pmtu = self.path_data(path_id).current_mtu().into();
1331        let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu);
1332
1333        // Iterate over the available spaces.
1334        for space_id in SpaceId::iter() {
1335            // Only PathId::ZERO uses non Data space ids.
1336            if path_id != PathId::ZERO && space_id != SpaceId::Data {
1337                continue;
1338            }
1339            match self.poll_transmit_path_space(
1340                now,
1341                &mut transmit,
1342                path_id,
1343                space_id,
1344                remote_cid,
1345                scheduling_info,
1346                connection_close_pending,
1347                pad_datagram,
1348            ) {
1349                PollPathSpaceStatus::NothingToSend {
1350                    congestion_blocked: cb,
1351                } => {
1352                    congestion_blocked |= cb;
1353                    // Continue checking other spaces, tail-loss probes may need to be sent
1354                    // in all spaces.
1355                }
1356                PollPathSpaceStatus::WrotePacket {
1357                    last_packet_number: pn,
1358                    pad_datagram: pad,
1359                } => {
1360                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
1361                    last_packet_number = Some(pn);
1362                    pad_datagram = pad;
1363                    // Always check higher spaces. If the transmit is full or they have
1364                    // nothing to send they will not write packets. But if they can, they
1365                    // must always be allowed to add to this transmit because coalescing may
1366                    // be required.
1367                    continue;
1368                }
1369                PollPathSpaceStatus::Send {
1370                    last_packet_number: pn,
1371                } => {
1372                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
1373                    last_packet_number = Some(pn);
1374                    break;
1375                }
1376            }
1377        }
1378
1379        if last_packet_number.is_some() || congestion_blocked {
1380            self.qlog.emit_recovery_metrics(
1381                path_id,
1382                &mut self
1383                    .paths
1384                    .get_mut(&path_id)
1385                    .expect("path_id was iterated from self.paths above")
1386                    .data,
1387                now,
1388            );
1389        }
1390
1391        self.path_data_mut(path_id).app_limited =
1392            last_packet_number.is_none() && !congestion_blocked;
1393
1394        match last_packet_number {
1395            Some(last_packet_number) => {
1396                // Note that when sending in multiple spaces the last packet number will be
1397                // the one from the highest space.
1398                self.path_data_mut(path_id).congestion.on_sent(
1399                    now,
1400                    transmit.len() as u64,
1401                    last_packet_number,
1402                );
1403                Some(self.build_transmit(path_id, transmit))
1404            }
1405            None => None,
1406        }
1407    }
1408
1409    /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId).
1410    #[must_use]
1411    fn poll_transmit_path_space(
1412        &mut self,
1413        now: Instant,
1414        transmit: &mut TransmitBuf<'_>,
1415        path_id: PathId,
1416        space_id: SpaceId,
1417        remote_cid: ConnectionId,
1418        scheduling_info: &PathSchedulingInfo,
1419        // If we need to send a CONNECTION_CLOSE frame.
1420        connection_close_pending: bool,
1421        // Whether the current datagram needs to be padded to a certain size.
1422        mut pad_datagram: PadDatagram,
1423    ) -> PollPathSpaceStatus {
1424        // Keep track of the last packet number we wrote. If None we did not write any
1425        // packets.
1426        let mut last_packet_number = None;
1427
1428        // Each loop of this may build one packet. It works logically as follows:
1429        //
1430        // - Check if something *needs* to be sent in this space and *can* be sent.
1431        //   - If not, return to the caller who will call us again for the next space.
1432        // - Start a new datagram.
1433        //   - Unless coalescing the packet into an existing datagram.
1434        // - Write the packet header and payload.
1435        // - Check if coalescing a next packet into the datagram is possible.
1436        // - If coalescing, finish packet without padding to leave space in the datagram.
1437        // - If not coalescing, complete the datagram:
1438        //   - Finish packet with padding.
1439        //   - Set the transmit segment size if this is the first datagram.
1440        // - Loop: next iteration will exit the loop if nothing more to send in this space. The
1441        //   TransmitBuf will contain a started datagram with space if coalescing, or completely
1442        //   filled datagram if not coalescing.
1443        loop {
1444            // Determine if anything can be sent in this packet number space.
1445            let max_packet_size = if transmit.datagram_remaining_mut() > 0 {
1446                // A datagram is started already, we are coalescing another packet into it.
1447                transmit.datagram_remaining_mut()
1448            } else {
1449                // A new datagram needs to be started.
1450                transmit.segment_size()
1451            };
1452            let can_send =
1453                self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending);
1454            let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
1455            let space_will_send = {
1456                if scheduling_info.is_abandoned {
1457                    // If this path is abandoned then we might still have to send
1458                    // PATH_ABANDON itself on it if there was no better space
1459                    // available. Otherwise we want to send the PATH_ABANDON as permitted by
1460                    // may_send_data however.
1461                    scheduling_info.may_self_abandon
1462                        && self.spaces[space_id]
1463                            .pending
1464                            .path_abandon
1465                            .contains_key(&path_id)
1466                } else if can_send.close && scheduling_info.may_send_close {
1467                    // This is the best path to send a CONNECTION_CLOSE on.
1468                    true
1469                } else if needs_loss_probe || can_send.space_specific {
1470                    // We always send a loss probe or space-specific frames if the path is
1471                    // not abandoned.
1472                    true
1473                } else {
1474                    // Anything else we only send if we're the best path for SpaceKind::Data
1475                    // frames.
1476                    !can_send.is_empty() && scheduling_info.may_send_data
1477                }
1478            };
1479
1480            if !space_will_send {
1481                // Nothing more to send. Previous iterations of this loop may have built
1482                // packets already.
1483                return match last_packet_number {
1484                    Some(pn) => PollPathSpaceStatus::WrotePacket {
1485                        last_packet_number: pn,
1486                        pad_datagram,
1487                    },
1488                    None => {
1489                        // Only log for spaces which have crypto.
1490                        if self.crypto_state.has_keys(space_id.encryption_level())
1491                            || (space_id == SpaceId::Data
1492                                && self.crypto_state.has_keys(EncryptionLevel::ZeroRtt))
1493                        {
1494                            trace!(?space_id, %path_id, "nothing to send in space");
1495                        }
1496                        PollPathSpaceStatus::NothingToSend {
1497                            congestion_blocked: false,
1498                        }
1499                    }
1500                };
1501            }
1502
1503            // We want to send on this space, check congestion control if we can. But only
1504            // if we will need to start a new datagram. If we are coalescing into an already
1505            // started datagram we do not need to check congestion control again.
1506            if transmit.datagram_remaining_mut() == 0 {
1507                let congestion_blocked =
1508                    self.path_congestion_check(space_id, path_id, transmit, &can_send, now);
1509                if congestion_blocked != PathBlocked::No {
1510                    // Previous iterations of this loop may have built packets already.
1511                    return match last_packet_number {
1512                        Some(pn) => PollPathSpaceStatus::WrotePacket {
1513                            last_packet_number: pn,
1514                            pad_datagram,
1515                        },
1516                        None => {
1517                            return PollPathSpaceStatus::NothingToSend {
1518                                congestion_blocked: true,
1519                            };
1520                        }
1521                    };
1522                }
1523
1524                // If the datagram is full (or there never was one started), we need to start a
1525                // new one.
1526                if transmit.num_datagrams() >= transmit.max_datagrams().get() {
1527                    // No more datagrams allowed.
1528                    // Previous iterations of this loop may have built packets already.
1529                    return match last_packet_number {
1530                        Some(pn) => PollPathSpaceStatus::WrotePacket {
1531                            last_packet_number: pn,
1532                            pad_datagram,
1533                        },
1534                        None => {
1535                            return PollPathSpaceStatus::NothingToSend {
1536                                congestion_blocked: false,
1537                            };
1538                        }
1539                    };
1540                }
1541
1542                if needs_loss_probe {
1543                    // Ensure we have something to send for a tail-loss probe.
1544                    let request_immediate_ack =
1545                        space_id == SpaceId::Data && self.peer_supports_ack_frequency();
1546                    self.spaces[space_id].queue_tail_loss_probe(
1547                        path_id,
1548                        request_immediate_ack,
1549                        &self.streams,
1550                    );
1551
1552                    self.spaces[space_id].for_path(path_id).loss_probes -= 1; // needs_loss_probe ensures loss_probes > 0
1553
1554                    // Clamp the datagram to at most the minimum MTU to ensure that loss
1555                    // probes can get through and enable recovery even if the path MTU
1556                    // has shrank unexpectedly.
1557                    transmit.start_new_datagram_with_size(cmp::min(
1558                        usize::from(INITIAL_MTU),
1559                        transmit.segment_size(),
1560                    ));
1561                } else {
1562                    transmit.start_new_datagram();
1563                }
1564                trace!(count = transmit.num_datagrams(), "new datagram started");
1565
1566                // We started a new datagram, we decide later if it needs padding.
1567                pad_datagram = PadDatagram::No;
1568            }
1569
1570            // If coalescing another packet into the existing datagram, there should
1571            // still be enough space for a whole packet.
1572            if transmit.datagram_start_offset() < transmit.len() {
1573                debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE);
1574            }
1575
1576            //
1577            // From here on, we've determined that a packet will definitely be sent.
1578            //
1579
1580            if self.crypto_state.has_keys(EncryptionLevel::Initial)
1581                && space_id == SpaceId::Handshake
1582                && self.side.is_client()
1583            {
1584                // A client stops both sending and processing Initial packets when it
1585                // sends its first Handshake packet.
1586                self.discard_space(now, SpaceKind::Initial);
1587            }
1588            if let Some(ref mut prev) = self.crypto_state.prev_crypto {
1589                prev.update_unacked = false;
1590            }
1591
1592            let Some(mut builder) =
1593                PacketBuilder::new(now, space_id, path_id, remote_cid, transmit, self)
1594            else {
1595                // Confidentiality limit is exceeded and the connection has been killed. We
1596                // should not send any other packets. This works in a roundabout way: We
1597                // have started a datagram but not written anything into it. So even if we
1598                // get called again for another space we will see an already started
1599                // datagram and try and start another packet here. Then be stopped by the
1600                // same confidentiality limit.
1601                return PollPathSpaceStatus::NothingToSend {
1602                    congestion_blocked: false,
1603                };
1604            };
1605            last_packet_number = Some(builder.packet_number);
1606
1607            if space_id == SpaceId::Initial
1608                && (self.side.is_client() || can_send.is_ack_eliciting() || needs_loss_probe)
1609            {
1610                // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1
1611                pad_datagram |= PadDatagram::ToMinMtu;
1612            }
1613            if space_id == SpaceId::Data && self.config.pad_to_mtu {
1614                pad_datagram |= PadDatagram::ToSegmentSize;
1615            }
1616
1617            if scheduling_info.may_send_close && can_send.close {
1618                trace!("sending CONNECTION_CLOSE");
1619                // Encode ACKs before the ConnectionClose message, to give the receiver
1620                // a better approximate on what data has been processed. This is
1621                // especially important with ack delay, since the peer might not
1622                // have gotten any other ACK for the data earlier on.
1623                let is_multipath_negotiated = self.is_multipath_negotiated();
1624                for path_id in self.spaces[space_id]
1625                    .number_spaces
1626                    .iter()
1627                    .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty())
1628                    .map(|(&path_id, _)| path_id)
1629                    .collect::<Vec<_>>()
1630                {
1631                    Self::populate_acks(
1632                        now,
1633                        self.receiving_ecn,
1634                        path_id,
1635                        space_id,
1636                        &mut self.spaces[space_id],
1637                        is_multipath_negotiated,
1638                        &mut builder,
1639                        &mut self.path_stats.get_mut(path_id).frame_tx,
1640                        self.crypto_state.has_keys(space_id.encryption_level()),
1641                    );
1642                }
1643
1644                // Since there only 64 ACK frames there will always be enough space
1645                // to encode the ConnectionClose frame too. However we still have the
1646                // check here to prevent crashes if something changes.
1647
1648                // TODO(flub): This needs fixing for multipath, to ensure we can always
1649                //    write the CONNECTION_CLOSE even if we have many PATH_ACKs to send:
1650                //    https://github.com/n0-computer/noq/issues/367.
1651                debug_assert!(
1652                    builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND,
1653                    "ACKs should leave space for ConnectionClose"
1654                );
1655                let stats = &mut self.path_stats.get_mut(path_id).frame_tx;
1656                if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() {
1657                    let max_frame_size = builder.frame_space_remaining();
1658                    let close: Close = match self.state.as_type() {
1659                        StateType::Closed => {
1660                            let reason: Close =
1661                                self.state.as_closed().expect("checked").clone().into();
1662                            if space_id == SpaceId::Data || reason.is_transport_layer() {
1663                                reason
1664                            } else {
1665                                TransportError::APPLICATION_ERROR("").into()
1666                            }
1667                        }
1668                        StateType::Draining => TransportError::NO_ERROR("").into(),
1669                        _ => unreachable!(
1670                            "tried to make a close packet when the connection wasn't closed"
1671                        ),
1672                    };
1673                    builder.write_frame(close.encoder(max_frame_size), stats);
1674                }
1675                let last_pn = builder.packet_number;
1676                builder.finish_and_track(now, self, path_id, pad_datagram);
1677                if space_id.kind() == self.highest_space {
1678                    // Don't send another close packet. Even with multipath we only send
1679                    // CONNECTION_CLOSE on a single path since we expect our paths to work.
1680                    self.connection_close_pending = false;
1681                }
1682                // Send a close frame in every possible space for robustness, per
1683                // RFC9000 "Immediate Close during the Handshake". Don't bother trying
1684                // to send anything else.
1685                // TODO(flub): This breaks during the handshake if we can not coalesce
1686                //    packets due to space reasons: the next space would either fail a
1687                //    debug_assert checking for enough packet space or produce an invalid
1688                //    packet. We need to keep track of per-space pending CONNECTION_CLOSE to
1689                //    be able to send these across multiple calls to poll_transmit. Then
1690                //    check for coalescing space here because initial packets need to be in
1691                //    padded datagrams. And also add space checks for CONNECTION_CLOSE in
1692                //    space_can_send so it would stop a GSO batch if the datagram is too
1693                //    small for another CONNECTION_CLOSE packet.
1694                return PollPathSpaceStatus::WrotePacket {
1695                    last_packet_number: last_pn,
1696                    pad_datagram,
1697                };
1698            }
1699
1700            self.populate_packet(now, space_id, path_id, scheduling_info, &mut builder);
1701
1702            // ACK-only packets should only be sent when explicitly allowed. If we write them due to
1703            // any other reason, there is a bug which leads to one component announcing write
1704            // readiness while not writing any data. This degrades performance. The condition is
1705            // only checked if the full MTU is available and when potentially large fixed-size
1706            // frames aren't queued, so that lack of space in the datagram isn't the reason for just
1707            // writing ACKs.
1708            debug_assert!(
1709                !(builder.sent_frames().is_ack_only(&self.streams)
1710                    && !can_send.acks
1711                    && (can_send.other || can_send.space_specific)
1712                    && builder.buf.segment_size()
1713                        == self.path_data(path_id).current_mtu() as usize
1714                    && self.datagrams.outgoing.is_empty()),
1715                "SendableFrames was {can_send:?}, but only ACKs have been written"
1716            );
1717            if builder.sent_frames().requires_padding {
1718                pad_datagram |= PadDatagram::ToMinMtu;
1719            }
1720
1721            for path_id in builder.sent_frames().largest_acked.keys() {
1722                self.spaces[space_id]
1723                    .for_path(*path_id)
1724                    .pending_acks
1725                    .acks_sent();
1726                self.timers.stop(
1727                    Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
1728                    self.qlog.with_time(now),
1729                );
1730            }
1731
1732            // Now we need to finish the packet.  Before we do so we need to know if we will
1733            // be coalescing the next packet into this one, or will be ending the datagram
1734            // as well.  Because if this is the last packet in the datagram more padding
1735            // might be needed because of the packet type, or to fill the GSO segment size.
1736
1737            let max_packet_size = builder
1738                .buf
1739                .datagram_remaining_mut()
1740                .saturating_sub(builder.predict_packet_end());
1741            // Are we allowed to coalesce AND is there enough space for another *packet* in
1742            // this datagram AND will we definitely send another packet?
1743            if builder.can_coalesce
1744                && path_id == PathId::ZERO
1745                && let Some(next_space_id) = space_id.next()
1746                && max_packet_size > MIN_PACKET_SPACE
1747                && self
1748                    .space_can_send(space_id, path_id, max_packet_size, connection_close_pending)
1749                    .is_empty()
1750                && self.has_pending_packet(next_space_id, max_packet_size, connection_close_pending)
1751            {
1752                // We can append/coalesce the next packet into the current
1753                // datagram. Finish the current packet without adding extra padding.
1754                trace!("will coalesce with next packet");
1755                let last_pn = builder.packet_number;
1756                builder.finish_and_track(now, self, path_id, PadDatagram::No);
1757                // We need to return - this loop would re-try the same SpaceId, which we don't want.
1758                // We want to coalesce with the next space_id.
1759                return PollPathSpaceStatus::WrotePacket {
1760                    last_packet_number: last_pn,
1761                    pad_datagram,
1762                };
1763            } else {
1764                // We need a new datagram for the next packet.  Finish the current
1765                // packet with padding.
1766                // TODO(flub): if there isn't any more data to be sent, this will still pad
1767                //    to the segment size and only discover there is nothing to send before
1768                //    starting the next packet. That is wasting up to 32 bytes.
1769                if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) {
1770                    // If too many padding bytes would be required to continue the
1771                    // GSO batch after this packet, end the GSO batch here. Ensures
1772                    // that fixed-size frames with heterogeneous sizes
1773                    // (e.g. application datagrams) won't inadvertently waste large
1774                    // amounts of bandwidth. The exact threshold is a bit arbitrary
1775                    // and might benefit from further tuning, though there's no
1776                    // universally optimal value.
1777                    const MAX_PADDING: usize = 32;
1778                    if builder.buf.datagram_remaining_mut()
1779                        > builder.predict_packet_end() + MAX_PADDING
1780                    {
1781                        trace!(
1782                            "GSO truncated by demand for {} padding bytes",
1783                            builder.buf.datagram_remaining_mut() - builder.predict_packet_end()
1784                        );
1785                        let last_pn = builder.packet_number;
1786                        builder.finish_and_track(now, self, path_id, PadDatagram::No);
1787                        return PollPathSpaceStatus::Send {
1788                            last_packet_number: last_pn,
1789                        };
1790                    }
1791
1792                    // Pad the current datagram to GSO segment size so it can be
1793                    // included in the GSO batch.
1794                    builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize);
1795                } else {
1796                    builder.finish_and_track(now, self, path_id, pad_datagram);
1797                }
1798
1799                // If this is the first datagram we set the segment size to the size of the
1800                // first datagram.
1801                if transmit.num_datagrams() == 1 {
1802                    transmit.clip_segment_size();
1803                }
1804            }
1805        }
1806    }
1807
1808    fn poll_transmit_mtu_probe(
1809        &mut self,
1810        now: Instant,
1811        buf: &mut Vec<u8>,
1812        path_id: PathId,
1813    ) -> Option<Transmit> {
1814        let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?;
1815
1816        // We are definitely sending a DPLPMTUD probe.
1817        let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize);
1818        transmit.start_new_datagram_with_size(probe_size as usize);
1819
1820        let mut builder =
1821            PacketBuilder::new(now, SpaceId::Data, path_id, active_cid, &mut transmit, self)?;
1822
1823        // We implement MTU probes as ping packets padded up to the probe size
1824        trace!(?probe_size, "writing MTUD probe");
1825        builder.write_frame(frame::Ping, &mut self.path_stats.get_mut(path_id).frame_tx);
1826
1827        // If supported by the peer, we want no delays to the probe's ACK
1828        if self.peer_supports_ack_frequency() {
1829            builder.write_frame(
1830                frame::ImmediateAck,
1831                &mut self.path_stats.get_mut(path_id).frame_tx,
1832            );
1833        }
1834
1835        builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size));
1836
1837        self.path_stats.get_mut(path_id).sent_plpmtud_probes += 1;
1838
1839        Some(self.build_transmit(path_id, transmit))
1840    }
1841
1842    /// Returns the CID and probe size if a DPLPMTUD probe is needed.
1843    ///
1844    /// We MTU probe all paths for which all of the following is true:
1845    /// - We have an active destination CID for the path.
1846    /// - The remote address *and* path are validated.
1847    /// - The path is not abandoned.
1848    /// - The MTU Discovery subsystem wants to probe the path.
1849    fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> {
1850        let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?;
1851        let is_eligible = self.path_data(path_id).validated
1852            && !self.path_data(path_id).is_validating_path()
1853            && !self.abandoned_paths.contains(&path_id);
1854
1855        if !is_eligible {
1856            return None;
1857        }
1858        let next_pn = self.spaces[SpaceId::Data]
1859            .for_path(path_id)
1860            .peek_tx_number();
1861        let probe_size = self
1862            .path_data_mut(path_id)
1863            .mtud
1864            .poll_transmit(now, next_pn)?;
1865
1866        Some((active_cid, probe_size))
1867    }
1868
1869    /// Returns true if there is a further packet to send on [`PathId::ZERO`].
1870    ///
1871    /// In other words this is predicting whether the next call to
1872    /// [`Connection::space_can_send`] issued will return some frames to be sent. Including
1873    /// having to predict which packet number space it will be invoked with. This depends on
1874    /// how both [`Connection::poll_transmit_on_path`] and
1875    /// [`Connection::poll_transmit_path_space`] behave.
1876    ///
1877    /// This is needed to determine if packet coalescing can happen. Because the last packet
1878    /// in a datagram may need to be padded and thus we must know if another packet will
1879    /// follow or not.
1880    ///
1881    /// The next packet can be either in the same space, or in one of the following spaces
1882    /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and
1883    /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because
1884    /// packets are built from Initial to Handshake to Data spaces.
1885    fn has_pending_packet(
1886        &mut self,
1887        current_space_id: SpaceId,
1888        max_packet_size: usize,
1889        connection_close_pending: bool,
1890    ) -> bool {
1891        let mut space_id = current_space_id;
1892        loop {
1893            let can_send = self.space_can_send(
1894                space_id,
1895                PathId::ZERO,
1896                max_packet_size,
1897                connection_close_pending,
1898            );
1899            if !can_send.is_empty() {
1900                return true;
1901            }
1902            match space_id.next() {
1903                Some(next_space_id) => space_id = next_space_id,
1904                None => break,
1905            }
1906        }
1907        false
1908    }
1909
1910    /// Checks if creating a new datagram would be blocked by congestion control
1911    fn path_congestion_check(
1912        &mut self,
1913        space_id: SpaceId,
1914        path_id: PathId,
1915        transmit: &TransmitBuf<'_>,
1916        can_send: &SendableFrames,
1917        now: Instant,
1918    ) -> PathBlocked {
1919        // Anti-amplification is only based on `total_sent`, which gets updated after
1920        // the transmit is sent. Therefore we pass the amount of bytes for datagrams
1921        // that are already created, as well as 1 byte for starting another datagram. If
1922        // there is any anti-amplification budget left, we always allow a full MTU to be
1923        // sent (see https://github.com/quinn-rs/quinn/issues/1082).
1924        if self.side().is_server()
1925            && self
1926                .path_data(path_id)
1927                .anti_amplification_blocked(transmit.len() as u64 + 1)
1928        {
1929            trace!(?space_id, %path_id, "blocked by anti-amplification");
1930            return PathBlocked::AntiAmplification;
1931        }
1932
1933        // Congestion control check.
1934        // Tail loss probes must not be blocked by congestion, or a deadlock could arise.
1935        let bytes_to_send = transmit.segment_size() as u64;
1936        let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
1937
1938        if can_send.other && !need_loss_probe && !can_send.close {
1939            let path = self.path_data(path_id);
1940            if path.in_flight.bytes + bytes_to_send >= path.congestion.window() {
1941                trace!(
1942                    ?space_id,
1943                    %path_id,
1944                    in_flight=%path.in_flight.bytes,
1945                    congestion_window=%path.congestion.window(),
1946                    "blocked by congestion control",
1947                );
1948                return PathBlocked::Congestion;
1949            }
1950        }
1951
1952        // Pacing check.
1953        if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) {
1954            let resume_time = now + delay;
1955            self.timers.set(
1956                Timer::PerPath(path_id, PathTimer::Pacing),
1957                resume_time,
1958                self.qlog.with_time(now),
1959            );
1960            // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though
1961            // they are not congestion controlled.
1962            trace!(?space_id, %path_id, ?delay, "blocked by pacing");
1963            return PathBlocked::Pacing;
1964        }
1965
1966        PathBlocked::No
1967    }
1968
1969    /// Send PATH_CHALLENGE for a previous path if necessary
1970    ///
1971    /// QUIC-TRANSPORT section 9.3.3
1972    /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding>
1973    fn send_prev_path_challenge(
1974        &mut self,
1975        now: Instant,
1976        buf: &mut Vec<u8>,
1977        path_id: PathId,
1978    ) -> Option<Transmit> {
1979        let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?;
1980        if !prev_path.pending_challenge {
1981            return None;
1982        };
1983        prev_path.pending_challenge = false;
1984        let token = self.rng.random();
1985        let network_path = prev_path.network_path;
1986        prev_path.record_path_challenge_sent(now, token, network_path);
1987
1988        debug_assert_eq!(
1989            self.highest_space,
1990            SpaceKind::Data,
1991            "PATH_CHALLENGE queued without 1-RTT keys"
1992        );
1993        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
1994        buf.start_new_datagram();
1995
1996        // Use the previous CID to avoid linking the new path with the previous path. We
1997        // don't bother accounting for possible retirement of that prev_cid because this is
1998        // sent once, immediately after migration, when the CID is known to be valid. Even
1999        // if a post-migration packet caused the CID to be retired, it's fair to pretend
2000        // this is sent first.
2001        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, self)?;
2002        let challenge = frame::PathChallenge(token);
2003        let stats = &mut self.path_stats.get_mut(path_id).frame_tx;
2004        builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path"));
2005
2006        // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
2007        // to at least the smallest allowed maximum datagram size of 1200 bytes,
2008        // unless the anti-amplification limit for the path does not permit
2009        // sending a datagram of this size
2010        builder.pad_to(MIN_INITIAL_SIZE);
2011
2012        builder.finish(self, now);
2013        self.path_stats
2014            .get_mut(path_id)
2015            .udp_tx
2016            .on_sent(1, buf.len());
2017
2018        trace!(
2019            dst = ?network_path.remote,
2020            src = ?network_path.local_ip,
2021            len = buf.len(),
2022            "sending prev_path off-path challenge",
2023        );
2024        Some(Transmit {
2025            destination: network_path.remote,
2026            size: buf.len(),
2027            ecn: None,
2028            segment_size: None,
2029            src_ip: network_path.local_ip,
2030        })
2031    }
2032
2033    fn send_off_path_path_response(
2034        &mut self,
2035        now: Instant,
2036        buf: &mut Vec<u8>,
2037        path_id: PathId,
2038    ) -> Option<Transmit> {
2039        let network_path = self
2040            .paths
2041            .get_mut(&path_id)
2042            .map(|state| state.data.network_path)?;
2043        let cid_queue = self.remote_cids.get_mut(&path_id)?;
2044        let pns = self.spaces[SpaceKind::Data].for_path(path_id);
2045        let (token, network_path) = pns.pending_path_responses.pop_off_path(network_path)?;
2046
2047        // TODO: make off-path probes unlinkable.
2048        let cid = cid_queue.active();
2049
2050        // PATH_RESPONSE (off-path)
2051        let frame = frame::PathResponse(token);
2052
2053        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
2054        buf.start_new_datagram();
2055
2056        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, self)?;
2057        let stats = &mut self.path_stats.get_mut(path_id).frame_tx;
2058        builder.write_frame_with_log_msg(frame, stats, Some("(off-path)"));
2059
2060        // PATH_CHALLENGE (off-path)
2061        //
2062        // If we are a client doing NAT traversal, always include a PATH_CHALLENGE with any
2063        // off-path PATH_RESPONSE. No need to schedule any retries for this, if NAT
2064        // traversal is taking place then this remote already is being probed with
2065        // retries, this only speeds up a successful traversal.
2066        if self
2067            .find_validated_path_on_network_path(network_path)
2068            .is_none()
2069            && self.n0_nat_traversal.client_side().is_ok()
2070        {
2071            let token = self.rng.random();
2072            let stats = &mut self.path_stats.get_mut(path_id).frame_tx;
2073            builder.write_frame(frame::PathChallenge(token), stats);
2074            let ip_port = (network_path.remote.ip(), network_path.remote.port());
2075            self.n0_nat_traversal.mark_probe_sent(ip_port, token);
2076        }
2077
2078        // Off-path: not tracked in congestion control. The packet is sent to a
2079        // different destination than path_id's network path.
2080        builder.pad_to(MIN_INITIAL_SIZE);
2081        builder.finish(self, now);
2082
2083        let size = buf.len();
2084        self.path_stats.get_mut(path_id).udp_tx.on_sent(1, size);
2085
2086        trace!(
2087            dst = ?network_path.remote,
2088            src = ?network_path.local_ip,
2089            len = buf.len(),
2090            "sending off-path PATH_RESPONSE",
2091        );
2092        Some(Transmit {
2093            destination: network_path.remote,
2094            size,
2095            ecn: None,
2096            segment_size: None,
2097            src_ip: network_path.local_ip,
2098        })
2099    }
2100
2101    /// Send a nat traversal challenge (off-path) on this path if possible.
2102    fn send_nat_traversal_path_challenge(
2103        &mut self,
2104        now: Instant,
2105        buf: &mut Vec<u8>,
2106        path_id: PathId,
2107    ) -> Option<Transmit> {
2108        let remote = self.n0_nat_traversal.next_probe_addr()?;
2109
2110        if !self.paths.get(&path_id)?.data.validated {
2111            // Path is not usable for probing
2112            return None;
2113        }
2114
2115        // TODO: Using the active CID here makes the paths linkable. This is a violation of
2116        //    RFC9000 but something we want to accept in the short term. Eventually we aim
2117        //    to fix up the supply of CIDs sufficiently so that we can keep paths unlinkable
2118        //    again.
2119        let Some(cid) = self
2120            .remote_cids
2121            .get(&path_id)
2122            .map(|cid_queue| cid_queue.active())
2123        else {
2124            trace!(%path_id, "Not sending NAT traversal probe for path with no CIDs");
2125            return None;
2126        };
2127        let token = self.rng.random();
2128
2129        // PATH_CHALLENGE (NAT probe)
2130        let frame = frame::PathChallenge(token);
2131
2132        let mut buf = TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
2133        buf.start_new_datagram();
2134
2135        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, &mut buf, self)?;
2136        let stats = &mut self.path_stats.get_mut(path_id).frame_tx;
2137        builder.write_frame_with_log_msg(frame, stats, Some("(nat-traversal)"));
2138        // Off-path: not tracked in congestion control. The packet is sent to a
2139        // different destination than path_id's network path.
2140        builder.finish(self, now);
2141
2142        // Mark as sent after packet build succeeds.
2143        self.n0_nat_traversal.mark_probe_sent(remote, token);
2144
2145        let size = buf.len();
2146        self.path_stats.get_mut(path_id).udp_tx.on_sent(1, size);
2147
2148        trace!(dst = ?remote, len = buf.len(), "sending off-path NAT probe");
2149        Some(Transmit {
2150            destination: remote.into(),
2151            size,
2152            ecn: None,
2153            segment_size: None,
2154            src_ip: None,
2155        })
2156    }
2157
2158    /// Indicate what types of frames are ready to send for the given packet number space.
2159    ///
2160    /// Only for on-path data.
2161    ///
2162    /// *packet_size* is the number of bytes available to build the next packet.
2163    /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be
2164    /// sent.
2165    fn space_can_send(
2166        &mut self,
2167        space_id: SpaceId,
2168        path_id: PathId,
2169        packet_size: usize,
2170        connection_close_pending: bool,
2171    ) -> SendableFrames {
2172        let space = &mut self.spaces[space_id];
2173        let space_has_crypto = self.crypto_state.has_keys(space_id.encryption_level());
2174
2175        if !space_has_crypto
2176            && (space_id != SpaceId::Data
2177                || !self.crypto_state.has_keys(EncryptionLevel::ZeroRtt)
2178                || self.side.is_server())
2179        {
2180            // Nothing to send in this space
2181            return SendableFrames::empty();
2182        }
2183
2184        let mut can_send = space.can_send(path_id, &self.streams);
2185
2186        // Check for 1RTT space.
2187        if space_id == SpaceId::Data {
2188            let pn = space.for_path(path_id).peek_tx_number();
2189            // Number of bytes available for frames if this is a 1-RTT packet. We're
2190            // guaranteed to be able to send an individual frame at least this large in the
2191            // next 1-RTT packet. This could be generalized to support every space, but it's
2192            // only needed to handle large fixed-size frames, which only exist in 1-RTT
2193            // (application datagrams).
2194            let frame_space_1rtt =
2195                packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
2196            can_send |= self.can_send_1rtt(path_id, frame_space_1rtt);
2197        }
2198
2199        can_send.close = connection_close_pending && space_has_crypto;
2200
2201        can_send
2202    }
2203
2204    /// Process `ConnectionEvent`s generated by the associated `Endpoint`
2205    ///
2206    /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals
2207    /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be
2208    /// extracted through the relevant methods.
2209    pub fn handle_event(&mut self, event: ConnectionEvent) {
2210        use ConnectionEventInner::*;
2211        match event.0 {
2212            Datagram(DatagramConnectionEvent {
2213                now,
2214                network_path,
2215                path_id,
2216                ecn,
2217                first_decode,
2218                remaining,
2219            }) => {
2220                let span = trace_span!("pkt", %path_id);
2221                let _guard = span.enter();
2222
2223                if self.early_discard_packet(network_path, path_id) {
2224                    // A return value of true indicates we should discard this packet.
2225                    return;
2226                }
2227
2228                let was_anti_amplification_blocked = self
2229                    .path(path_id)
2230                    .map(|path| path.anti_amplification_blocked(1))
2231                    // We never tried to send on an non-existing (new) path so have not been
2232                    // anti-amplification blocked for it previously.
2233                    .unwrap_or(false);
2234
2235                let rx = &mut self.path_stats.get_mut(path_id).udp_rx;
2236                rx.datagrams += 1;
2237                rx.bytes += first_decode.len() as u64;
2238                let data_len = first_decode.len();
2239
2240                self.handle_decode(now, network_path, path_id, ecn, first_decode);
2241                // The current `path` might have changed inside `handle_decode` since the packet
2242                // could have triggered a migration. The packet might also belong to an unknown
2243                // path and have been rejected. Make sure the data received is accounted for the
2244                // most recent path by accessing `path` after `handle_decode`.
2245                if let Some(path) = self.path_mut(path_id) {
2246                    path.inc_total_recvd(data_len as u64);
2247                }
2248
2249                if let Some(data) = remaining {
2250                    self.path_stats.get_mut(path_id).udp_rx.bytes += data.len() as u64;
2251                    self.handle_coalesced(now, network_path, path_id, ecn, data);
2252                }
2253
2254                if let Some(path) = self.paths.get_mut(&path_id) {
2255                    self.qlog
2256                        .emit_recovery_metrics(path_id, &mut path.data, now);
2257                }
2258
2259                if was_anti_amplification_blocked {
2260                    // A prior attempt to set the loss detection timer may have failed due to
2261                    // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if
2262                    // the server's first flight is lost.
2263                    self.set_loss_detection_timer(now, path_id);
2264                }
2265            }
2266            NewIdentifiers(ids, now, cid_len, cid_lifetime) => {
2267                let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default();
2268                debug_assert!(ids.iter().all(|issued| issued.path_id == path_id));
2269
2270                // Path may have been abandoned while this reply was in flight,
2271                // retire the CIDs instead of queuing them.
2272                if self.abandoned_paths.contains(&path_id) {
2273                    if !self.state.is_drained() {
2274                        for issued in &ids {
2275                            self.endpoint_events
2276                                .push_back(EndpointEventInner::RetireConnectionId(
2277                                    now,
2278                                    path_id,
2279                                    issued.sequence,
2280                                    false,
2281                                ));
2282                        }
2283                    }
2284                    return;
2285                }
2286
2287                let cid_state = self
2288                    .local_cid_state
2289                    .entry(path_id)
2290                    .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0));
2291                cid_state.new_cids(&ids, now);
2292
2293                ids.into_iter().rev().for_each(|frame| {
2294                    self.spaces[SpaceId::Data].pending.new_cids.push(frame);
2295                });
2296                // Always update Timer::PushNewCid
2297                self.reset_cid_retirement(now);
2298            }
2299        }
2300    }
2301
2302    /// Returns whether a packet can be discarded early.
2303    ///
2304    /// Packets sent on the wrong network path can be entirely ignored, saving further
2305    /// processing.
2306    ///
2307    /// Returns true if a packet coming in for this `path_id` over given `network_path`
2308    /// should be discarded.
2309    fn early_discard_packet(&mut self, network_path: FourTuple, path_id: PathId) -> bool {
2310        if self.is_handshaking() && path_id != PathId::ZERO {
2311            debug!(%network_path, %path_id, "discarding multipath packet during handshake");
2312            return true;
2313        }
2314
2315        if !self.paths.contains_key(&path_id) && self.abandoned_paths.contains(&path_id) {
2316            trace!(%path_id, "discarding packet for discarded path");
2317            return true;
2318        }
2319
2320        let peer_may_probe = self.peer_may_probe();
2321        let local_ip_may_migrate = self.local_ip_may_migrate();
2322
2323        // If this packet could initiate a migration and we're a client or a server that
2324        // forbids migration, drop the datagram. This could be relaxed to heuristically
2325        // permit NAT-rebinding-like migration.
2326        if let Some(known_path) = self.path_mut(path_id) {
2327            if network_path.remote != known_path.network_path.remote && !peer_may_probe {
2328                trace!(
2329                    %path_id,
2330                    %network_path,
2331                    %known_path.network_path,
2332                    "discarding packet from unrecognized peer"
2333                );
2334                return true;
2335            }
2336
2337            if known_path.network_path.local_ip.is_some()
2338                && network_path.local_ip.is_some()
2339                && known_path.network_path.local_ip != network_path.local_ip
2340                && !local_ip_may_migrate
2341            {
2342                trace!(
2343                    %path_id,
2344                    %network_path,
2345                    %known_path.network_path,
2346                    "discarding packet sent to incorrect interface"
2347                );
2348                return true;
2349            }
2350        }
2351        false
2352    }
2353
2354    /// Whether the peer may probe new paths.
2355    ///
2356    /// RFC9000 §9 and QNT both have probing packets which may arrive from new paths. This
2357    /// indicates whether these are allowed or not. This is a strict superset from
2358    /// [`Self::peer_may_migrate`]: every network path that may be migrated to, may also
2359    /// be probed. But e.g. servers may not migrate, but can be allowed to probe.
2360    // TODO(flub): In RFC9000 the server is allowed to send off-path probing packets
2361    //    once the client has been probing such a 4-tuple. These probes are currently
2362    //    not yet recognised and will end up being discarded because of this.
2363    //    See https://github.com/n0-computer/noq/issues/607.
2364    fn peer_may_probe(&self) -> bool {
2365        match &self.side {
2366            ConnectionSide::Client { .. } => {
2367                if let Some(hs) = self.state.as_handshake() {
2368                    hs.allow_server_migration
2369                } else {
2370                    self.n0_nat_traversal.is_negotiated() && self.is_handshake_confirmed()
2371                }
2372            }
2373            ConnectionSide::Server { server_config } => {
2374                self.is_handshake_confirmed()
2375                    && (server_config.migration || self.n0_nat_traversal.is_negotiated())
2376            }
2377        }
2378    }
2379
2380    /// Whether the peer's remote address may migrate.
2381    ///
2382    /// In RFC9000 only the client may migrate.
2383    ///
2384    /// QUIC relies on stable endpoints during the handshake. So other than the server's
2385    /// preferred_address transport parameter no side may migrate before the handshake is
2386    /// completed.
2387    ///
2388    /// It is noteworthy that for iroh we allow server migrations during the handshake when
2389    /// [`state::Handshake::allow_server_migration`] is enabled, but that is handled earlier
2390    /// in [`Self::handle_packet`] and without probing the current and previous paths.
2391    fn peer_may_migrate(&self) -> bool {
2392        match &self.side {
2393            ConnectionSide::Server { server_config } => {
2394                server_config.migration && self.is_handshake_confirmed()
2395            }
2396            ConnectionSide::Client { .. } => false,
2397        }
2398    }
2399
2400    /// Whether our local IP address is allowed to change with new incoming packets.
2401    ///
2402    /// Incoming packets show us the local IP address we received a packet on, which could
2403    /// be different from what we thought due to e.g. NAT rebinding or moving from mobile
2404    /// data to WiFi without being notified of the network change.
2405    ///
2406    /// This is only allowed to happen after the handshake is confirmed and when we are the
2407    /// client. Unless QNT is negotiated in which case the server is also allowed to
2408    /// migrate.
2409    ///
2410    /// Be aware that probing packets, which do not exist in Multipath without QNT, are
2411    /// exempt from this.
2412    fn local_ip_may_migrate(&self) -> bool {
2413        (self.side.is_client() || self.n0_nat_traversal.is_negotiated())
2414            && self.is_handshake_confirmed()
2415    }
2416    /// Process timer expirations
2417    ///
2418    /// Executes protocol logic, potentially preparing signals (including application `Event`s,
2419    /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant
2420    /// methods.
2421    ///
2422    /// It is most efficient to call this immediately after the system clock reaches the latest
2423    /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply
2424    /// no-op and therefore are safe.
2425    pub fn handle_timeout(&mut self, now: Instant) {
2426        while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) {
2427            let span = match timer {
2428                Timer::Conn(timer) => trace_span!("timeout", scope = "conn", ?timer),
2429                Timer::PerPath(path_id, timer) => {
2430                    trace_span!("timer_fired", scope="path", %path_id, ?timer)
2431                }
2432            };
2433            let _guard = span.enter();
2434            trace!("timeout");
2435            match timer {
2436                Timer::Conn(timer) => match timer {
2437                    ConnTimer::Close => {
2438                        self.state.move_to_drained(None, &mut self.endpoint_events);
2439                    }
2440                    ConnTimer::Idle => {
2441                        self.kill(ConnectionError::TimedOut);
2442                    }
2443                    ConnTimer::KeepAlive => {
2444                        self.ping();
2445                    }
2446                    ConnTimer::KeyDiscard => {
2447                        self.crypto_state.discard_temporary_keys();
2448                    }
2449                    ConnTimer::PushNewCid => {
2450                        while let Some((path_id, when)) = self.next_cid_retirement() {
2451                            if when > now {
2452                                break;
2453                            }
2454                            match self.local_cid_state.get_mut(&path_id) {
2455                                None => error!(%path_id, "No local CID state for path"),
2456                                Some(cid_state) => {
2457                                    // Update `retire_prior_to` field in NEW_CONNECTION_ID frame
2458                                    let num_new_cid = cid_state.on_cid_timeout().into();
2459                                    if !self.state.is_closed() {
2460                                        trace!(
2461                                            "push a new CID to peer RETIRE_PRIOR_TO field {}",
2462                                            cid_state.retire_prior_to()
2463                                        );
2464                                        self.endpoint_events.push_back(
2465                                            EndpointEventInner::NeedIdentifiers(
2466                                                path_id,
2467                                                now,
2468                                                num_new_cid,
2469                                            ),
2470                                        );
2471                                    }
2472                                }
2473                            }
2474                        }
2475                    }
2476                    ConnTimer::NoAvailablePath => {
2477                        // Grace period expired: all paths were abandoned and no new path
2478                        // was opened. Close the connection. There are no paths left to
2479                        // send CONNECTION_CLOSE on, so this is a silent close.
2480                        // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8
2481                        if self.state.is_closed() || self.state.is_drained() {
2482                            // Connection already closing/drained (e.g. application called
2483                            // close() before the grace timer fired). Nothing to do.
2484                            error!("no viable path timer fired, but connection already closing");
2485                        } else {
2486                            trace!("no viable path grace period expired, closing connection");
2487                            let err = TransportError::NO_VIABLE_PATH(
2488                                "last path abandoned, no new path opened",
2489                            );
2490                            self.close_common();
2491                            self.set_close_timer(now);
2492                            self.connection_close_pending = true;
2493                            self.state.move_to_closed(err);
2494                        }
2495                    }
2496                    ConnTimer::NatTraversalProbeRetry => {
2497                        self.n0_nat_traversal.queue_retries(self.is_ipv6());
2498                        if let Some(delay) =
2499                            self.n0_nat_traversal.retry_delay(self.config.initial_rtt)
2500                        {
2501                            self.timers.set(
2502                                Timer::Conn(ConnTimer::NatTraversalProbeRetry),
2503                                now + delay,
2504                                self.qlog.with_time(now),
2505                            );
2506                            trace!("re-queued NAT probes");
2507                        } else {
2508                            trace!("no more NAT probes remaining");
2509                        }
2510                    }
2511                },
2512                Timer::PerPath(path_id, timer) => {
2513                    match timer {
2514                        PathTimer::PathIdle => {
2515                            if let Err(err) =
2516                                self.close_path_inner(now, path_id, PathAbandonReason::TimedOut)
2517                            {
2518                                warn!(?err, "failed closing path");
2519                            }
2520                        }
2521
2522                        PathTimer::PathKeepAlive => {
2523                            self.ping_path(path_id).ok();
2524                        }
2525                        PathTimer::LossDetection => {
2526                            self.on_loss_detection_timeout(now, path_id);
2527                            if let Some(path) = self.paths.get_mut(&path_id) {
2528                                self.qlog
2529                                    .emit_recovery_metrics(path_id, &mut path.data, now);
2530                            } else {
2531                                error!("LossDetection fired for unknown path");
2532                            }
2533                        }
2534                        PathTimer::PathValidationFailed => {
2535                            let Some(path) = self.paths.get_mut(&path_id) else {
2536                                continue;
2537                            };
2538                            self.timers.stop(
2539                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
2540                                self.qlog.with_time(now),
2541                            );
2542                            debug!("path migration validation failed");
2543                            path.data.reset_on_path_challenges();
2544                            if let Some((_, prev)) = path.prev.take() {
2545                                path.data = prev;
2546                                self.set_loss_detection_timer(now, path_id);
2547                            }
2548                        }
2549                        PathTimer::PathChallengeLost => {
2550                            let Some(path) = self.paths.get_mut(&path_id) else {
2551                                continue;
2552                            };
2553                            trace!(?path.data.lost_challenge_count, "path challenge deemed lost");
2554                            path.data.pending_challenge = true;
2555                            path.data.lost_challenge_count += 1;
2556                            self.timers.set(
2557                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
2558                                now + path.data.on_path_challenge_pto(),
2559                                self.qlog.with_time(now),
2560                            );
2561                        }
2562                        PathTimer::Pacing => {}
2563                        PathTimer::MaxAckDelay => {
2564                            // This timer is only armed in the Data space
2565                            self.spaces[SpaceId::Data]
2566                                .for_path(path_id)
2567                                .pending_acks
2568                                .on_max_ack_delay_timeout()
2569                        }
2570                        PathTimer::PathDrained => {
2571                            self.discard_path(path_id, now);
2572                        }
2573                    }
2574                }
2575            }
2576        }
2577    }
2578
2579    /// Close a connection immediately
2580    ///
2581    /// This does not ensure delivery of outstanding data. It is the application's responsibility to
2582    /// call this only when all important communications have been completed, e.g. by calling
2583    /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding
2584    /// [`StreamEvent::Finished`] event.
2585    ///
2586    /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been
2587    /// delivered. There may still be data from the peer that has not been received.
2588    ///
2589    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
2590    pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) {
2591        self.close_inner(
2592            now,
2593            Close::Application(frame::ApplicationClose { error_code, reason }),
2594        )
2595    }
2596
2597    /// Close the connection immediately, initiated by an API call.
2598    ///
2599    /// This will not produce a [`ConnectionLost`] event propagated by the
2600    /// [`Connection::poll`] call, because the API call already propagated the error to the
2601    /// user.
2602    ///
2603    /// Not to be used when entering immediate close due to an internal state change based
2604    /// on an event. See [`State::move_to_closed_local`] for details.
2605    ///
2606    /// This initiates immediate close from
2607    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed
2608    /// state.
2609    ///
2610    /// [`ConnectionLost`]: crate::Event::ConnectionLost
2611    /// [`Connection::poll`]: super::Connection::poll
2612    fn close_inner(&mut self, now: Instant, reason: Close) {
2613        let was_closed = self.state.is_closed();
2614        if !was_closed {
2615            self.close_common();
2616            self.set_close_timer(now);
2617            self.connection_close_pending = true;
2618            self.state.move_to_closed_local(reason);
2619        }
2620    }
2621
2622    /// Control datagrams
2623    pub fn datagrams(&mut self) -> Datagrams<'_> {
2624        Datagrams { conn: self }
2625    }
2626
2627    /// Returns connection statistics
2628    pub fn stats(&mut self) -> ConnectionStats {
2629        let mut stats = self.partial_stats.clone();
2630
2631        for path_stats in self.path_stats.iter_stats() {
2632            // Self::path_stats() computes the path rtt, cwnd and current_mtu on access
2633            // because they are not simple counters. When computing the connection stats we
2634            // can skip that effort since those fields are not used in the `impl
2635            // Add<PathStats> for ConnectionStats`.
2636            stats += *path_stats;
2637        }
2638
2639        stats
2640    }
2641
2642    /// Returns path statistics
2643    pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
2644        let path = self.paths.get(&path_id)?;
2645        let mut stats = self.path_stats.get(path_id).unwrap_or_default();
2646        stats.rtt = path.data.rtt.get();
2647        stats.cwnd = path.data.congestion.window();
2648        stats.current_mtu = path.data.mtud.current_mtu();
2649        Some(stats)
2650    }
2651
2652    /// Ping the remote endpoint
2653    ///
2654    /// Causes an ACK-eliciting packet to be transmitted on the connection.
2655    pub fn ping(&mut self) {
2656        // TODO(flub): This is very brute-force: it pings *all* the paths.  Instead it would
2657        //    be nice if we could only send a single packet for this.
2658        for path_data in self.spaces[self.highest_space].number_spaces.values_mut() {
2659            path_data.pending_ping = true;
2660        }
2661    }
2662
2663    /// Ping the remote endpoint over a specific path
2664    ///
2665    /// Causes an ACK-eliciting packet to be transmitted on the path.
2666    pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> {
2667        let path_data = self.spaces[self.highest_space]
2668            .number_spaces
2669            .get_mut(&path)
2670            .ok_or(ClosedPath { _private: () })?;
2671        path_data.pending_ping = true;
2672        Ok(())
2673    }
2674
2675    /// Update traffic keys spontaneously
2676    ///
2677    /// This can be useful for testing key updates, as they otherwise only happen infrequently.
2678    pub fn force_key_update(&mut self) {
2679        if !self.state.is_established() {
2680            debug!("ignoring forced key update in illegal state");
2681            return;
2682        }
2683        if self.crypto_state.prev_crypto.is_some() {
2684            // We already just updated, or are currently updating, the keys. Concurrent key updates
2685            // are illegal.
2686            debug!("ignoring redundant forced key update");
2687            return;
2688        }
2689        self.crypto_state.update_keys(None, false);
2690    }
2691
2692    /// Get a session reference
2693    pub fn crypto_session(&self) -> &dyn crypto::Session {
2694        self.crypto_state.session.as_ref()
2695    }
2696
2697    /// Whether the connection is in the process of being established
2698    ///
2699    /// If this returns `false`, the connection may be either established or closed, signaled by the
2700    /// emission of a [`Connected`](Event::Connected) or [`ConnectionLost`](Event::ConnectionLost)
2701    /// event respectively. Note that locally-initiated closes via [`close()`](Self::close) do not
2702    /// emit a `ConnectionLost` event.
2703    ///
2704    /// For an established connection this essentially means the handshake is **completed**,
2705    /// but not necessarily yet confirmed.
2706    pub fn is_handshaking(&self) -> bool {
2707        self.state.is_handshake()
2708    }
2709
2710    /// Whether the connection is closed
2711    ///
2712    /// Closed connections cannot transport any further data. A connection becomes closed when
2713    /// either peer application intentionally closes it, or when either transport layer detects an
2714    /// error such as a time-out or certificate validation failure.
2715    ///
2716    /// A [`ConnectionLost`](Event::ConnectionLost) event is emitted with details when the
2717    /// connection is closed by the peer or due to an error. When the local application closes
2718    /// the connection via [`close()`](Self::close), no `ConnectionLost` event is emitted;
2719    /// instead, pending operations fail with [`ConnectionError::LocallyClosed`].
2720    pub fn is_closed(&self) -> bool {
2721        self.state.is_closed()
2722    }
2723
2724    /// Whether there is no longer any need to keep the connection around
2725    ///
2726    /// Closed connections become drained after a brief timeout to absorb any remaining in-flight
2727    /// packets from the peer. All drained connections have been closed.
2728    pub fn is_drained(&self) -> bool {
2729        self.state.is_drained()
2730    }
2731
2732    /// For clients, if the peer accepted the 0-RTT data packets
2733    ///
2734    /// The value is meaningless until after the handshake completes.
2735    pub fn accepted_0rtt(&self) -> bool {
2736        self.crypto_state.accepted_0rtt
2737    }
2738
2739    /// Whether 0-RTT is/was possible during the handshake
2740    pub fn has_0rtt(&self) -> bool {
2741        self.crypto_state.zero_rtt_enabled
2742    }
2743
2744    /// Whether there are any pending retransmits
2745    pub fn has_pending_retransmits(&self) -> bool {
2746        !self.spaces[SpaceId::Data].pending.is_empty(&self.streams)
2747    }
2748
2749    /// Look up whether we're the client or server of this Connection
2750    pub fn side(&self) -> Side {
2751        self.side.side()
2752    }
2753
2754    /// Get the address observed by the remote over the given path
2755    pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> {
2756        self.path(path_id)
2757            .map(|path_data| {
2758                path_data
2759                    .last_observed_addr_report
2760                    .as_ref()
2761                    .map(|observed| observed.socket_addr())
2762            })
2763            .ok_or(ClosedPath { _private: () })
2764    }
2765
2766    /// Current best estimate of this connection's latency (round-trip-time)
2767    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
2768        self.path(path_id).map(|d| d.rtt.get())
2769    }
2770
2771    /// Current state of this connection's congestion controller, for debugging purposes
2772    pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> {
2773        self.path(path_id).map(|d| d.congestion.as_ref())
2774    }
2775
2776    /// Modify the number of remotely initiated streams that may be concurrently open
2777    ///
2778    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
2779    /// `count`s increase both minimum and worst-case memory consumption.
2780    pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) {
2781        self.streams.set_max_concurrent(dir, count);
2782        // If the limit was reduced, then a flow control update previously deemed insignificant may
2783        // now be significant.
2784        let pending = &mut self.spaces[SpaceId::Data].pending;
2785        self.streams.queue_max_stream_id(pending);
2786    }
2787
2788    /// Modify the number of open paths allowed when multipath is enabled
2789    ///
2790    /// When reducing the number of concurrent paths this will only affect delaying sending
2791    /// new MAX_PATH_ID frames until fewer than this number of paths are possible.  To
2792    /// actively reduce paths they must be closed using [`Connection::close_path`], which
2793    /// can also be used to close not-yet-opened paths.
2794    ///
2795    /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable
2796    /// multipath and will fail.
2797    pub fn set_max_concurrent_paths(
2798        &mut self,
2799        now: Instant,
2800        count: NonZeroU32,
2801    ) -> Result<(), MultipathNotNegotiated> {
2802        if !self.is_multipath_negotiated() {
2803            return Err(MultipathNotNegotiated { _private: () });
2804        }
2805        self.max_concurrent_paths = count;
2806
2807        let in_use_count = self
2808            .local_max_path_id
2809            .next()
2810            .saturating_sub(self.abandoned_paths.len())
2811            .as_u32();
2812        let extra_needed = count.get().saturating_sub(in_use_count);
2813        let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed);
2814
2815        self.set_max_path_id(now, new_max_path_id);
2816
2817        Ok(())
2818    }
2819
2820    /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths
2821    fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) {
2822        if max_path_id <= self.local_max_path_id {
2823            return;
2824        }
2825
2826        self.local_max_path_id = max_path_id;
2827        self.spaces[SpaceId::Data].pending.max_path_id = true;
2828
2829        self.issue_first_path_cids(now);
2830    }
2831
2832    /// Current number of remotely initiated streams that may be concurrently open
2833    ///
2834    /// If the target for this limit is reduced using
2835    /// [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), it will not change
2836    /// immediately, even if fewer streams are open. Instead, it will decrement by one for each
2837    /// time a remotely initiated stream of matching directionality is closed.
2838    pub fn max_concurrent_streams(&self, dir: Dir) -> u64 {
2839        self.streams.max_concurrent(dir)
2840    }
2841
2842    /// See [`TransportConfig::send_window()`]
2843    pub fn set_send_window(&mut self, send_window: u64) {
2844        self.streams.set_send_window(send_window);
2845    }
2846
2847    /// See [`TransportConfig::receive_window()`]
2848    pub fn set_receive_window(&mut self, receive_window: VarInt) {
2849        if self.streams.set_receive_window(receive_window) {
2850            self.spaces[SpaceId::Data].pending.max_data = true;
2851        }
2852    }
2853
2854    /// Whether the Multipath for QUIC extension is enabled.
2855    ///
2856    /// Multipath is only enabled after the handshake is completed and if it was enabled by both
2857    /// peers.
2858    pub fn is_multipath_negotiated(&self) -> bool {
2859        !self.is_handshaking()
2860            && self.config.max_concurrent_multipath_paths.is_some()
2861            && self.peer_params.initial_max_path_id.is_some()
2862    }
2863
2864    fn on_ack_received(
2865        &mut self,
2866        now: Instant,
2867        space: SpaceId,
2868        ack: frame::Ack,
2869    ) -> Result<(), TransportError> {
2870        // All ACKs are referencing path 0
2871        let path = PathId::ZERO;
2872        self.inner_on_ack_received(now, space, path, ack)
2873    }
2874
2875    fn on_path_ack_received(
2876        &mut self,
2877        now: Instant,
2878        space: SpaceId,
2879        path_ack: frame::PathAck,
2880    ) -> Result<(), TransportError> {
2881        let (ack, path) = path_ack.into_ack();
2882        self.inner_on_ack_received(now, space, path, ack)
2883    }
2884
2885    /// Handles an ACK frame acknowledging packets sent on *path*.
2886    fn inner_on_ack_received(
2887        &mut self,
2888        now: Instant,
2889        space: SpaceId,
2890        path: PathId,
2891        ack: frame::Ack,
2892    ) -> Result<(), TransportError> {
2893        if !self.spaces[space].number_spaces.contains_key(&path) {
2894            if self.abandoned_paths.contains(&path) {
2895                // See also
2896                // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4.3-3
2897                // > When an endpoint finally deletes all state associated with the path [...]
2898                // > PATH_ACK frames received with an abandoned path ID are silently ignored,
2899                // > as specified in Section 4.
2900                trace!("silently ignoring PATH_ACK on discarded path");
2901                return Ok(());
2902            } else {
2903                return Err(TransportError::PROTOCOL_VIOLATION(
2904                    "received PATH_ACK with path ID never used",
2905                ));
2906            }
2907        }
2908        if ack.largest >= self.spaces[space].for_path(path).next_packet_number {
2909            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
2910        }
2911        // `Some(pn)` if this ACK raised `largest_acked_packet_pn`.
2912        let new_largest_pn = {
2913            let space = &mut self.spaces[space].for_path(path);
2914            if space
2915                .largest_acked_packet_pn
2916                .is_none_or(|pn| ack.largest > pn)
2917            {
2918                space.largest_acked_packet_pn = Some(ack.largest);
2919                if let Some(info) = space.sent_packets.get(ack.largest) {
2920                    // This should always succeed, but a misbehaving peer might ACK a packet we
2921                    // haven't sent. At worst, that will result in us spuriously reducing the
2922                    // congestion window.
2923                    space.largest_acked_packet_send_time = info.time_sent;
2924                }
2925                Some(ack.largest)
2926            } else {
2927                None
2928            }
2929        };
2930
2931        if self.detect_spurious_loss(&ack, space, path) {
2932            self.path_stats.get_mut(path).spurious_congestion_events += 1;
2933            self.path_data_mut(path)
2934                .congestion
2935                .on_spurious_congestion_event();
2936        }
2937
2938        // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks.
2939        let mut newly_acked: ArrayRangeSet = ArrayRangeSet::new();
2940        for range in ack.iter() {
2941            self.spaces[space].for_path(path).check_ack(range.clone())?;
2942            for (pn, _) in self.spaces[space]
2943                .for_path(path)
2944                .sent_packets
2945                .iter_range(range)
2946            {
2947                newly_acked.insert_one(pn);
2948            }
2949        }
2950
2951        if newly_acked.is_empty() {
2952            return Ok(());
2953        }
2954
2955        let mut ack_eliciting_acked = false;
2956        for packet in newly_acked.elts() {
2957            if let Some(info) = self.spaces[space].for_path(path).take(packet) {
2958                for (acked_path_id, acked_pn) in info.largest_acked.iter() {
2959                    // Assume ACKs for all packets below the largest acknowledged in
2960                    // `packet` have been received. This can cause the peer to spuriously
2961                    // retransmit if some of our earlier ACKs were lost, but allows for
2962                    // simpler state tracking. See discussion at
2963                    // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking
2964                    if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) {
2965                        pns.pending_acks.subtract_below(*acked_pn);
2966                    }
2967                }
2968                ack_eliciting_acked |= info.ack_eliciting;
2969
2970                // Notify MTU discovery that a packet was acked, because it might be an MTU probe
2971                let path_data = self.path_data_mut(path);
2972                let mtu_updated = path_data.mtud.on_acked(space.kind(), packet, info.size);
2973                if mtu_updated {
2974                    path_data
2975                        .congestion
2976                        .on_mtu_update(path_data.mtud.current_mtu());
2977                }
2978
2979                // Notify ack frequency that a packet was acked, because it might contain an
2980                // ACK_FREQUENCY frame
2981                self.ack_frequency.on_acked(path, packet);
2982
2983                self.on_packet_acked(now, path, packet, info);
2984            }
2985        }
2986
2987        let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet_pn;
2988        let path_data = self.path_data_mut(path);
2989        let app_limited = path_data.app_limited;
2990        let in_flight = path_data.in_flight.bytes;
2991
2992        path_data
2993            .congestion
2994            .on_end_acks(now, in_flight, app_limited, largest_ackd);
2995
2996        if new_largest_pn.is_some() && ack_eliciting_acked {
2997            let ack_delay = if space != SpaceId::Data {
2998                Duration::from_micros(0)
2999            } else {
3000                cmp::min(
3001                    self.ack_frequency.peer_max_ack_delay,
3002                    Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0),
3003                )
3004            };
3005            let rtt = now.saturating_duration_since(
3006                self.spaces[space]
3007                    .for_path(path)
3008                    .largest_acked_packet_send_time,
3009            );
3010
3011            let next_pn = self.spaces[space].for_path(path).next_packet_number;
3012            let path_data = self.path_data_mut(path);
3013            // TODO(@divma): should be a method of path, should be contained in a single place
3014            path_data.rtt.update(ack_delay, rtt);
3015            if path_data.first_packet_after_rtt_sample.is_none() {
3016                path_data.first_packet_after_rtt_sample = Some((space.kind(), next_pn));
3017            }
3018        }
3019
3020        // Must be called before crypto/pto_count are clobbered
3021        self.detect_lost_packets(now, space, path, true);
3022
3023        // If the peer did not complete the handshake address validation the ACK could be
3024        // spoofed, e.g. in the Initial space. Setting the pto_count back to 0 removes the
3025        // exponential backoff from the PTO timer and would result in too many tail-loss
3026        // probes being sent.
3027        if self.peer_completed_handshake_address_validation() {
3028            self.path_data_mut(path).pto_count = 0;
3029        }
3030
3031        // Explicit congestion notification
3032        // TODO(@divma): this code is a good example of logic that should be contained in a single
3033        // place but it's split between the path data and the packet number space data, we should
3034        // find a way to make this work without two lookups
3035        if self.path_data(path).sending_ecn {
3036            if let Some(ecn) = ack.ecn {
3037                // We only examine ECN counters from ACKs that we are certain we received in
3038                // transmit order, allowing us to compute an increase in ECN counts
3039                // to compare against the number of newly acked packets that remains
3040                // well-defined in the presence of arbitrary packet reordering.
3041                if let Some(largest_sent_pn) = new_largest_pn {
3042                    let sent = self.spaces[space]
3043                        .for_path(path)
3044                        .largest_acked_packet_send_time;
3045                    self.process_ecn(
3046                        now,
3047                        space,
3048                        path,
3049                        newly_acked.range_count() as u64,
3050                        ecn,
3051                        sent,
3052                        largest_sent_pn,
3053                    );
3054                }
3055            } else {
3056                // We always start out sending ECN, so any ack that doesn't acknowledge it disables
3057                // it.
3058                debug!("ECN not acknowledged by peer");
3059                self.path_data_mut(path).sending_ecn = false;
3060            }
3061        }
3062
3063        self.set_loss_detection_timer(now, path);
3064        Ok(())
3065    }
3066
3067    fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool {
3068        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
3069
3070        if lost_packets.is_empty() {
3071            return false;
3072        }
3073
3074        for range in ack.iter() {
3075            let spurious_losses: Vec<u64> = lost_packets
3076                .iter_range(range.clone())
3077                .map(|(pn, _info)| pn)
3078                .collect();
3079
3080            for pn in spurious_losses {
3081                lost_packets.remove(pn);
3082            }
3083        }
3084
3085        // If this ACK frame acknowledged all deemed lost packets,
3086        // then we have raised a spurious congestion event in the past.
3087        // We cannot conclude when there are remaining packets,
3088        // but future ACK frames might indicate a spurious loss detection.
3089        lost_packets.is_empty()
3090    }
3091
3092    /// Drain lost packets that we reasonably think will never arrive
3093    ///
3094    /// The current criterion is copied from `msquic`:
3095    /// discard packets that were sent earlier than 2 probe timeouts ago.
3096    fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) {
3097        let two_pto = 2 * self.path_data(path).rtt.pto_base();
3098
3099        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
3100        lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto);
3101    }
3102
3103    /// Process a new ECN block from an in-order ACK
3104    fn process_ecn(
3105        &mut self,
3106        now: Instant,
3107        space: SpaceId,
3108        path: PathId,
3109        newly_acked_pn: u64,
3110        ecn: frame::EcnCounts,
3111        largest_sent_time: Instant,
3112        largest_sent_pn: u64,
3113    ) {
3114        match self.spaces[space]
3115            .for_path(path)
3116            .detect_ecn(newly_acked_pn, ecn)
3117        {
3118            Err(e) => {
3119                debug!("halting ECN due to verification failure: {}", e);
3120
3121                self.path_data_mut(path).sending_ecn = false;
3122                // Wipe out the existing value because it might be garbage and could interfere with
3123                // future attempts to use ECN on new paths.
3124                self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO;
3125            }
3126            Ok(false) => {}
3127            Ok(true) => {
3128                self.path_stats.get_mut(path).congestion_events += 1;
3129                self.path_data_mut(path).congestion.on_congestion_event(
3130                    now,
3131                    largest_sent_time,
3132                    false,
3133                    true,
3134                    0,
3135                    largest_sent_pn,
3136                );
3137            }
3138        }
3139    }
3140
3141    // Not timing-aware, so it's safe to call this for inferred acks, such as arise from
3142    // high-latency handshakes
3143    fn on_packet_acked(&mut self, now: Instant, path_id: PathId, pn: u64, info: SentPacket) {
3144        let path = self.path_data_mut(path_id);
3145        let app_limited = path.app_limited;
3146        path.remove_in_flight(&info);
3147        if info.ack_eliciting && info.path_generation == path.generation() {
3148            // Only pass ACKs to the congestion controller if it belongs to this exact
3149            // generation of the path. Otherwise we might be feeding ACKs from the previous
3150            // 4-tuple into our congestion controller.
3151            let rtt = path.rtt;
3152            path.congestion
3153                .on_ack(now, info.time_sent, info.size.into(), pn, app_limited, &rtt);
3154        }
3155
3156        // Update state for confirmed delivery of frames
3157        if let Some(retransmits) = info.retransmits.get() {
3158            for (id, _) in retransmits.reset_stream.iter() {
3159                self.streams.reset_acked(*id);
3160            }
3161        }
3162
3163        for frame in info.stream_frames {
3164            self.streams.received_ack_of(frame);
3165        }
3166    }
3167
3168    fn set_key_discard_timer(&mut self, now: Instant, space: SpaceKind) {
3169        let start = if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) {
3170            now
3171        } else {
3172            self.crypto_state
3173                .prev_crypto
3174                .as_ref()
3175                .expect("no previous keys")
3176                .end_packet
3177                .as_ref()
3178                .expect("update not acknowledged yet")
3179                .1
3180        };
3181
3182        // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths.
3183        self.timers.set(
3184            Timer::Conn(ConnTimer::KeyDiscard),
3185            start + self.max_pto_for_space(space) * 3,
3186            self.qlog.with_time(now),
3187        );
3188    }
3189
3190    /// Handle a [`PathTimer::LossDetection`] timeout.
3191    ///
3192    /// This timer expires for two reasons:
3193    /// - An ACK-eliciting packet we sent should be considered lost.
3194    /// - The PTO may have expired and a tail-loss probe needs to be scheduled.
3195    ///
3196    /// The former needs us to schedule re-transmission of the lost data.
3197    ///
3198    /// The latter means we have not received an ACK for an ack-eliciting packet we sent
3199    /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting
3200    /// packet, to try and elicit new acknowledgements. These new acknowledgements will
3201    /// indicate whether the previously sent packets were lost or not.
3202    fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) {
3203        if let Some((_, pn_space)) = self.loss_time_and_space(path_id) {
3204            // Time threshold loss Detection
3205            self.detect_lost_packets(now, pn_space, path_id, false);
3206            self.set_loss_detection_timer(now, path_id);
3207            return;
3208        }
3209
3210        let Some((_, space)) = self.pto_time_and_space(now, path_id) else {
3211            debug!(%path_id, "PTO expired while unset");
3212            return;
3213        };
3214        trace!(
3215            in_flight = self.path_data(path_id).in_flight.bytes,
3216            count = self.path_data(path_id).pto_count,
3217            ?space,
3218            %path_id,
3219            "PTO fired"
3220        );
3221
3222        let count = match self.path_data(path_id).in_flight.ack_eliciting {
3223            // A PTO when we're not expecting any ACKs must be due to handshake
3224            // anti-amplification deadlock prevention.
3225            0 => {
3226                debug_assert!(!self.peer_completed_handshake_address_validation());
3227                1
3228            }
3229            // Conventional loss probe
3230            _ => 2,
3231        };
3232        let pns = self.spaces[space].for_path(path_id);
3233        pns.loss_probes = pns.loss_probes.saturating_add(count);
3234        let path_data = self.path_data_mut(path_id);
3235        path_data.pto_count = path_data.pto_count.saturating_add(1);
3236        self.set_loss_detection_timer(now, path_id);
3237    }
3238
3239    /// Detect any lost packets
3240    ///
3241    /// There are two cases in which we detects lost packets:
3242    ///
3243    /// - We received an ACK packet.
3244    /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet that
3245    ///   was followed by an acknowledged packet. The loss timer for this un-acknowledged packet
3246    ///   expired and we need to detect that packet as lost.
3247    ///
3248    /// Packets are lost if they are both (See RFC9002 §6.1):
3249    ///
3250    /// - Unacknowledged, in flight and sent prior to an acknowledged packet.
3251    /// - Old enough by either:
3252    ///   - Having a packet number [`TransportConfig::packet_threshold`] lower then the last
3253    ///     acknowledged packet.
3254    ///   - Being sent [`TransportConfig::time_threshold`] * RTT in the past.
3255    fn detect_lost_packets(
3256        &mut self,
3257        now: Instant,
3258        pn_space: SpaceId,
3259        path_id: PathId,
3260        due_to_ack: bool,
3261    ) {
3262        let mut lost_packets = Vec::<u64>::new();
3263        let mut lost_mtu_probe = None;
3264        let mut in_persistent_congestion = false;
3265        let mut size_of_lost_packets = 0u64;
3266        self.spaces[pn_space].for_path(path_id).loss_time = None;
3267
3268        // Find all the lost packets, populating all variables initialised above.
3269
3270        let path = self.path_data(path_id);
3271        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
3272        let loss_delay = path
3273            .rtt
3274            .conservative()
3275            .mul_f32(self.config.time_threshold)
3276            .max(TIMER_GRANULARITY);
3277        let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample;
3278
3279        let largest_acked_packet_pn = self.spaces[pn_space]
3280            .for_path(path_id)
3281            .largest_acked_packet_pn
3282            .expect("detect_lost_packets only to be called if path received at least one ACK");
3283        let packet_threshold = self.config.packet_threshold as u64;
3284
3285        // InPersistentCongestion: Determine if all packets in the time period before the newest
3286        // lost packet, including the edges, are marked lost. PTO computation must always
3287        // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1).
3288        let congestion_period = self
3289            .pto(SpaceKind::Data, path_id)
3290            .saturating_mul(self.config.persistent_congestion_threshold);
3291        let mut persistent_congestion_start: Option<Instant> = None;
3292        let mut prev_packet = None;
3293        let space = self.spaces[pn_space].for_path(path_id);
3294
3295        for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet_pn) {
3296            if prev_packet != Some(packet.wrapping_sub(1)) {
3297                // An intervening packet was acknowledged
3298                persistent_congestion_start = None;
3299            }
3300
3301            // Packets sent before now - loss_delay are deemed lost.
3302            // However, we avoid subtraction as it can panic and there's no
3303            // saturating equivalent of this subtraction operation with a Duration.
3304            let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay;
3305            if packet_too_old || largest_acked_packet_pn >= packet + packet_threshold {
3306                // The packet should be declared lost.
3307                if Some(packet) == in_flight_mtu_probe {
3308                    // Lost MTU probes are not included in `lost_packets`, because they
3309                    // should not trigger a congestion control response
3310                    lost_mtu_probe = in_flight_mtu_probe;
3311                } else {
3312                    lost_packets.push(packet);
3313                    size_of_lost_packets += info.size as u64;
3314                    if info.ack_eliciting && due_to_ack {
3315                        match persistent_congestion_start {
3316                            // Two ACK-eliciting packets lost more than
3317                            // congestion_period apart, with no ACKed packets in between
3318                            Some(start) if info.time_sent - start > congestion_period => {
3319                                in_persistent_congestion = true;
3320                            }
3321                            // Persistent congestion must start after the first RTT sample
3322                            None if first_packet_after_rtt_sample
3323                                .is_some_and(|x| x < (pn_space.kind(), packet)) =>
3324                            {
3325                                persistent_congestion_start = Some(info.time_sent);
3326                            }
3327                            _ => {}
3328                        }
3329                    }
3330                }
3331            } else {
3332                // The packet should not yet be declared lost.
3333                if space.loss_time.is_none() {
3334                    // Since we iterate in order the lowest packet number's loss time will
3335                    // always be the earliest.
3336                    space.loss_time = Some(info.time_sent + loss_delay);
3337                }
3338                persistent_congestion_start = None;
3339            }
3340
3341            prev_packet = Some(packet);
3342        }
3343
3344        self.handle_lost_packets(
3345            pn_space,
3346            path_id,
3347            now,
3348            lost_packets,
3349            lost_mtu_probe,
3350            loss_delay,
3351            in_persistent_congestion,
3352            size_of_lost_packets,
3353        );
3354    }
3355
3356    /// Drops the path state, declaring any remaining in-flight packets as lost
3357    fn discard_path(&mut self, path_id: PathId, now: Instant) {
3358        trace!(%path_id, "dropping path state");
3359
3360        self.timers.stop_per_path(path_id, self.qlog.with_time(now));
3361
3362        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events. All timers should be cleared in drained connections.
3363
3364        if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) {
3365            let (min_seq, max_seq) = local_cid_state.active_seq();
3366            for seq in min_seq..=max_seq {
3367                self.endpoint_events
3368                    .push_back(EndpointEventInner::RetireConnectionId(
3369                        now, path_id, seq, false,
3370                    ));
3371            }
3372        }
3373
3374        self.endpoint_events
3375            .push_back(EndpointEventInner::RetireResetToken(path_id));
3376
3377        let path = self.path_data(path_id);
3378        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
3379
3380        let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes;
3381        let lost_pns: Vec<_> = self.spaces[SpaceId::Data]
3382            .for_path(path_id)
3383            .sent_packets
3384            .iter()
3385            .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe)
3386            .map(|(pn, info)| {
3387                size_of_lost_packets += info.size as u64;
3388                pn
3389            })
3390            .collect();
3391
3392        if !lost_pns.is_empty() {
3393            trace!(
3394                %path_id,
3395                count = lost_pns.len(),
3396                lost_bytes = size_of_lost_packets,
3397                "packets lost on path abandon"
3398            );
3399            self.handle_lost_packets(
3400                SpaceId::Data,
3401                path_id,
3402                now,
3403                lost_pns,
3404                in_flight_mtu_probe,
3405                Duration::ZERO,
3406                false,
3407                size_of_lost_packets,
3408            );
3409        }
3410        // Before removing the path, we fetch the final path stats via `Self::path_stats`.
3411        // This ensures snapshot values (like rtt) are properly updated.
3412        let path_stats = self.path_stats(path_id).unwrap_or_default();
3413        self.path_stats.discard(&path_id);
3414        self.partial_stats += path_stats;
3415        self.paths.remove(&path_id);
3416        self.spaces[SpaceId::Data].number_spaces.remove(&path_id);
3417
3418        self.events.push_back(
3419            PathEvent::Discarded {
3420                id: path_id,
3421                path_stats: Box::new(path_stats),
3422            }
3423            .into(),
3424        );
3425    }
3426
3427    fn handle_lost_packets(
3428        &mut self,
3429        pn_space: SpaceId,
3430        path_id: PathId,
3431        now: Instant,
3432        lost_packets: Vec<u64>,
3433        lost_mtu_probe: Option<u64>,
3434        loss_delay: Duration,
3435        in_persistent_congestion: bool,
3436        size_of_lost_packets: u64,
3437    ) {
3438        debug_assert!(lost_packets.is_sorted(), "lost_packets must be sorted");
3439
3440        self.drain_lost_packets(now, pn_space, path_id);
3441
3442        // OnPacketsLost
3443        if let Some(largest_lost) = lost_packets.last().cloned() {
3444            let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes;
3445            let largest_lost_sent = self.spaces[pn_space]
3446                .for_path(path_id)
3447                .sent_packets
3448                .get(largest_lost)
3449                .unwrap()
3450                .time_sent;
3451            let path_stats = self.path_stats.get_mut(path_id);
3452            path_stats.lost_packets += lost_packets.len() as u64;
3453            path_stats.lost_bytes += size_of_lost_packets;
3454            trace!(
3455                %path_id,
3456                count = lost_packets.len(),
3457                lost_bytes = size_of_lost_packets,
3458                "packets lost",
3459            );
3460
3461            for &packet in &lost_packets {
3462                let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else {
3463                    continue;
3464                };
3465                self.qlog
3466                    .emit_packet_lost(packet, &info, loss_delay, pn_space.kind(), now);
3467                self.paths
3468                    .get_mut(&path_id)
3469                    .unwrap()
3470                    .remove_in_flight(&info);
3471
3472                for frame in info.stream_frames {
3473                    self.streams.retransmit(frame);
3474                }
3475                self.spaces[pn_space].pending |= info.retransmits;
3476                let path = self.path_data_mut(path_id);
3477                path.pending |= info.path_retransmits;
3478                path.mtud.on_non_probe_lost(packet, info.size);
3479                path.congestion.on_packet_lost(info.size, packet, now);
3480
3481                self.spaces[pn_space].for_path(path_id).lost_packets.insert(
3482                    packet,
3483                    LostPacket {
3484                        time_sent: info.time_sent,
3485                    },
3486                );
3487            }
3488
3489            let path = self.path_data_mut(path_id);
3490            if path.mtud.black_hole_detected(now) {
3491                path.congestion.on_mtu_update(path.mtud.current_mtu());
3492                if let Some(max_datagram_size) = self.datagrams().max_size()
3493                    && self.datagrams.drop_oversized(max_datagram_size)
3494                    && self.datagrams.send_blocked
3495                {
3496                    self.datagrams.send_blocked = false;
3497                    self.events.push_back(Event::DatagramsUnblocked);
3498                }
3499                self.path_stats.get_mut(path_id).black_holes_detected += 1;
3500            }
3501
3502            // Don't apply congestion penalty for lost ack-only packets
3503            let lost_ack_eliciting =
3504                old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes;
3505
3506            if lost_ack_eliciting {
3507                self.path_stats.get_mut(path_id).congestion_events += 1;
3508                self.path_data_mut(path_id).congestion.on_congestion_event(
3509                    now,
3510                    largest_lost_sent,
3511                    in_persistent_congestion,
3512                    false,
3513                    size_of_lost_packets,
3514                    largest_lost,
3515                );
3516            }
3517        }
3518
3519        // Handle a lost MTU probe
3520        if let Some(packet) = lost_mtu_probe {
3521            let info = self.spaces[SpaceId::Data]
3522                .for_path(path_id)
3523                .take(packet)
3524                .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and
3525            // therefore must not have been removed yet
3526            self.paths
3527                .get_mut(&path_id)
3528                .unwrap()
3529                .remove_in_flight(&info);
3530            self.path_data_mut(path_id).mtud.on_probe_lost();
3531            self.path_stats.get_mut(path_id).lost_plpmtud_probes += 1;
3532        }
3533    }
3534
3535    /// Returns the earliest time packets should be declared lost for all spaces on a path.
3536    ///
3537    /// If a path has an acknowledged packet with any prior un-acknowledged packets, the
3538    /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed.
3539    /// The time returned is when this packet should be declared lost.
3540    fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> {
3541        SpaceId::iter()
3542            .filter_map(|id| {
3543                self.spaces[id]
3544                    .number_spaces
3545                    .get(&path_id)
3546                    .and_then(|pns| pns.loss_time)
3547                    .map(|time| (time, id))
3548            })
3549            .min_by_key(|&(time, _)| time)
3550    }
3551
3552    /// Returns the earliest next PTO should fire for all spaces on a path.
3553    ///
3554    /// This needs to be fully deterministic because it is also used to determine the PTO
3555    /// that fired, not just to set the next timer. So if it fired in the past it needs to
3556    /// return the time from the past at which it fired.
3557    ///
3558    /// This is the next time a tail-loss probe should be sent.
3559    fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> {
3560        let path = self.path(path_id)?;
3561        let pto_count = path.pto_count;
3562
3563        // Cap the maximum interval between two tail-loss probes.
3564        let max_interval = if path.rtt.get() > SLOW_RTT_THRESHOLD {
3565            // For slow links we want to increase the interval beyond 2s.
3566            (path.rtt.get() * 3) / 2
3567        } else if let Some(idle) = path.idle_timeout.or(self.idle_timeout)
3568            && idle <= MIN_IDLE_FOR_FAST_PTO
3569        {
3570            // If the idle timeout is relatively low, cap at 1s so we get plenty of retries
3571            // before the idle timeout fires.
3572            MAX_PTO_FAST_INTERVAL
3573        } else {
3574            // Otherwise cap to 2s.
3575            MAX_PTO_INTERVAL
3576        };
3577
3578        if path_id == PathId::ZERO
3579            && path.in_flight.ack_eliciting == 0
3580            && !self.peer_completed_handshake_address_validation()
3581        {
3582            // Address Validation during Connection Establishment:
3583            // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a
3584            // deadlock if an Initial or Handshake packet from the server is lost and the
3585            // server can not send more due to its anti-amplification limit the client must
3586            // send another packet on PTO.
3587            let space = match self.highest_space {
3588                SpaceKind::Handshake => SpaceId::Handshake,
3589                _ => SpaceId::Initial,
3590            };
3591
3592            let backoff = 2u32.pow(path.pto_count.min(MAX_BACKOFF_EXPONENT));
3593            let duration = path.rtt.pto_base() * backoff;
3594            let duration = duration.min(max_interval);
3595            return Some((now + duration, space));
3596        }
3597
3598        let mut result = None;
3599        for space in SpaceId::iter() {
3600            let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else {
3601                continue;
3602            };
3603
3604            if space == SpaceId::Data && !self.is_handshake_confirmed() {
3605                // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-7:
3606                // An endpoint MUST NOT set its PTO timer for the Application Data packet
3607                // number space until the handshake is confirmed.
3608                continue;
3609            }
3610
3611            if !pns.has_in_flight() {
3612                continue;
3613            }
3614
3615            // Compute the PTO duration for this space, we want to cap the maximum interval
3616            // between two tail-loss probes so to not do a simple exponential backoff but
3617            // rather iterate through the probes to compute the capped increment for an
3618            // exponential backoff at each step.
3619            let duration = {
3620                let max_ack_delay = if space == SpaceId::Data {
3621                    self.ack_frequency.max_ack_delay_for_pto()
3622                } else {
3623                    Duration::ZERO
3624                };
3625                let pto_base = path.rtt.pto_base() + max_ack_delay;
3626                let mut duration = pto_base;
3627                for i in 1..=pto_count {
3628                    let exponential_duration = pto_base * 2u32.pow(i.min(MAX_BACKOFF_EXPONENT));
3629                    let max_duration = duration + max_interval;
3630                    duration = exponential_duration.min(max_duration);
3631                }
3632                duration
3633            };
3634
3635            let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else {
3636                continue;
3637            };
3638            // Base the deadline on when the last probe was sent, so the PTO
3639            // doesn't fire before the response has had time to arrive.
3640            let pto = last_ack_eliciting + duration;
3641            if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) {
3642                if path.anti_amplification_blocked(1) {
3643                    // Nothing would be able to be sent.
3644                    continue;
3645                }
3646                if path.in_flight.ack_eliciting == 0 {
3647                    // Nothing ack-eliciting, no PTO to arm/fire.
3648                    continue;
3649                }
3650                result = Some((pto, space));
3651            }
3652        }
3653        result
3654    }
3655
3656    /// Whether the peer validated our address in the connection handshake.
3657    fn peer_completed_handshake_address_validation(&self) -> bool {
3658        if self.side.is_server() || self.state.is_closed() {
3659            return true;
3660        }
3661        // The server is guaranteed to have validated our address if any of our handshake or
3662        // 1-RTT packets are acknowledged or we've seen HANDSHAKE_DONE and discarded
3663        // handshake keys.
3664        self.spaces[SpaceId::Handshake]
3665            .path_space(PathId::ZERO)
3666            .and_then(|pns| pns.largest_acked_packet_pn)
3667            .is_some()
3668            || self.spaces[SpaceId::Data]
3669                .path_space(PathId::ZERO)
3670                .and_then(|pns| pns.largest_acked_packet_pn)
3671                .is_some()
3672            || (self.crypto_state.has_keys(EncryptionLevel::OneRtt)
3673                && !self.crypto_state.has_keys(EncryptionLevel::Handshake))
3674    }
3675
3676    /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed
3677    ///
3678    /// The timer must fire if either:
3679    /// - An ack-eliciting packet we sent needs to be declared lost.
3680    /// - A tail-loss probe needs to be sent.
3681    ///
3682    /// See [`Connection::on_loss_detection_timeout`] for details.
3683    fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) {
3684        if self.state.is_closed() {
3685            // No loss detection takes place on closed connections, and `close_common` already
3686            // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a
3687            // reordered packet being handled by state-insensitive code.
3688            return;
3689        }
3690
3691        if let Some((loss_time, _)) = self.loss_time_and_space(path_id) {
3692            // Time threshold loss detection.
3693            self.timers.set(
3694                Timer::PerPath(path_id, PathTimer::LossDetection),
3695                loss_time,
3696                self.qlog.with_time(now),
3697            );
3698            return;
3699        }
3700
3701        // Determine which PN space to arm PTO for.
3702        // We can only send tail-loss probes on paths that aren't abandoned yet.
3703        if !self.abandoned_paths.contains(&path_id)
3704            && let Some((timeout, _)) = self.pto_time_and_space(now, path_id)
3705        {
3706            self.timers.set(
3707                Timer::PerPath(path_id, PathTimer::LossDetection),
3708                timeout,
3709                self.qlog.with_time(now),
3710            );
3711        } else {
3712            self.timers.stop(
3713                Timer::PerPath(path_id, PathTimer::LossDetection),
3714                self.qlog.with_time(now),
3715            );
3716        }
3717    }
3718
3719    /// The maximum probe timeout across all paths
3720    ///
3721    /// See [`Connection::pto`]
3722    fn max_pto_for_space(&self, space: SpaceKind) -> Duration {
3723        self.paths
3724            .keys()
3725            .map(|path_id| self.pto(space, *path_id))
3726            .max()
3727            .unwrap_or_else(|| {
3728                // No paths remain (e.g. last path was abandoned and the NoAvailablePath grace timer
3729                // fired before any new path was opened). Fall back to a PTO derived from the
3730                // configured initial RTT, matching RFC 9002 §6.2.2 initial values.
3731                let rtt = self.config.initial_rtt;
3732                let max_ack_delay = match space {
3733                    SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO,
3734                    SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(),
3735                };
3736                rtt + cmp::max(4 * (rtt / 2), TIMER_GRANULARITY) + max_ack_delay
3737            })
3738    }
3739
3740    /// Probe Timeout
3741    ///
3742    /// The PTO is logically the time in which you'd expect to receive an acknowledgement
3743    /// for a packet. So approximately RTT + max_ack_delay.
3744    fn pto(&self, space: SpaceKind, path_id: PathId) -> Duration {
3745        let max_ack_delay = match space {
3746            SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO,
3747            SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(),
3748        };
3749        self.path_data(path_id).rtt.pto_base() + max_ack_delay
3750    }
3751
3752    fn on_packet_authenticated(
3753        &mut self,
3754        now: Instant,
3755        space_id: SpaceKind,
3756        path_id: PathId,
3757        ecn: Option<EcnCodepoint>,
3758        packet_number: Option<u64>,
3759        spin: bool,
3760        is_1rtt: bool,
3761        remote: &FourTuple,
3762    ) {
3763        // During the handshake we already have discarded packets that do not match the path
3764        // remote. So any off-path packet here is either a probing packet or a
3765        // migration. Handling probing packets here means that the path's idle timeout will
3766        // be reset and will delay detecting the path as idle. However tail-loss probes
3767        // would still not get acknowledged if the path was broken so eventually the path
3768        // would still become idle.
3769        let is_on_path = self
3770            .path_data(path_id)
3771            .network_path
3772            .is_probably_same_path(remote);
3773
3774        self.total_authed_packets += 1;
3775        self.reset_keep_alive(path_id, now);
3776        self.reset_idle_timeout(now, space_id, path_id);
3777        self.path_data_mut(path_id).permit_idle_reset = true;
3778
3779        // Do not process ECN for off-path packets. If this is a migration we'll get ECN
3780        // back once we've migrated.
3781        if is_on_path {
3782            self.receiving_ecn |= ecn.is_some();
3783            if let Some(x) = ecn {
3784                let space = &mut self.spaces[space_id];
3785                space.for_path(path_id).ecn_counters += x;
3786
3787                if x.is_ce() {
3788                    space
3789                        .for_path(path_id)
3790                        .pending_acks
3791                        .set_immediate_ack_required();
3792                }
3793            }
3794        }
3795
3796        let Some(packet_number) = packet_number else {
3797            return;
3798        };
3799        match &self.side {
3800            ConnectionSide::Client { .. } => {
3801                // If we received a handshake packet that authenticated, then we're talking to
3802                // the real server.  From now on we should no longer allow the server to migrate
3803                // its address.
3804                if space_id == SpaceKind::Handshake
3805                    && let Some(hs) = self.state.as_handshake_mut()
3806                {
3807                    hs.allow_server_migration = false;
3808                }
3809            }
3810            ConnectionSide::Server { .. } => {
3811                if self.crypto_state.has_keys(EncryptionLevel::Initial)
3812                    && space_id == SpaceKind::Handshake
3813                {
3814                    // A server stops sending and processing Initial packets when it receives its
3815                    // first Handshake packet.
3816                    self.discard_space(now, SpaceKind::Initial);
3817                }
3818                if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) && is_1rtt {
3819                    // Discard 0-RTT keys soon after receiving a 1-RTT packet
3820                    self.set_key_discard_timer(now, space_id)
3821                }
3822            }
3823        }
3824        let space = self.spaces[space_id].for_path(path_id);
3825
3826        space.pending_acks.insert_one(packet_number, now);
3827        if packet_number >= space.largest_received_packet_number.unwrap_or_default() {
3828            space.largest_received_packet_number = Some(packet_number);
3829
3830            // Update outgoing spin bit for on-path packets, inverting iff we're the client
3831            if is_on_path {
3832                self.spin = self.side.is_client() ^ spin;
3833            }
3834        }
3835    }
3836
3837    /// Resets the idle timeout timers.
3838    ///
3839    /// Without multipath there is only the connection-wide idle timeout. When multipath is
3840    /// enabled there is an additional per-path idle timeout.
3841    fn reset_idle_timeout(&mut self, now: Instant, space: SpaceKind, path_id: PathId) {
3842        // First reset the global idle timeout.
3843        if let Some(timeout) = self.idle_timeout {
3844            if self.state.is_closed() {
3845                self.timers
3846                    .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now));
3847            } else {
3848                let dt = cmp::max(timeout, 3 * self.max_pto_for_space(space));
3849                self.timers.set(
3850                    Timer::Conn(ConnTimer::Idle),
3851                    now + dt,
3852                    self.qlog.with_time(now),
3853                );
3854            }
3855        }
3856
3857        // Now handle the per-path state.
3858        self.rearm_path_max_idle_timer(now, space, path_id);
3859    }
3860
3861    /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers
3862    fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) {
3863        if !self.state.is_established() {
3864            return;
3865        }
3866
3867        if let Some(interval) = self.config.keep_alive_interval {
3868            self.timers.set(
3869                Timer::Conn(ConnTimer::KeepAlive),
3870                now + interval,
3871                self.qlog.with_time(now),
3872            );
3873        }
3874
3875        if let Some(interval) = self.path_data(path_id).keep_alive {
3876            self.timers.set(
3877                Timer::PerPath(path_id, PathTimer::PathKeepAlive),
3878                now + interval,
3879                self.qlog.with_time(now),
3880            );
3881        }
3882    }
3883
3884    /// Sets the timer for when a previously issued CID should be retired next
3885    fn reset_cid_retirement(&mut self, now: Instant) {
3886        if let Some((_path, t)) = self.next_cid_retirement() {
3887            self.timers.set(
3888                Timer::Conn(ConnTimer::PushNewCid),
3889                t,
3890                self.qlog.with_time(now),
3891            );
3892        }
3893    }
3894
3895    /// The next time when a previously issued CID should be retired
3896    fn next_cid_retirement(&self) -> Option<(PathId, Instant)> {
3897        self.local_cid_state
3898            .iter()
3899            .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t)))
3900            .min_by_key(|(_path_id, timeout)| *timeout)
3901    }
3902
3903    /// Handle the already-decrypted first packet from the client
3904    ///
3905    /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more
3906    /// efficient.
3907    pub(crate) fn handle_first_packet(
3908        &mut self,
3909        now: Instant,
3910        network_path: FourTuple,
3911        ecn: Option<EcnCodepoint>,
3912        packet_number: u64,
3913        packet: InitialPacket,
3914        remaining: Option<BytesMut>,
3915    ) -> Result<(), ConnectionError> {
3916        let span = trace_span!("first recv");
3917        let _guard = span.enter();
3918        debug_assert!(self.side.is_server());
3919        let len = packet.header_data.len() + packet.payload.len();
3920        let path_id = PathId::ZERO;
3921        self.path_data_mut(path_id).total_recvd = len as u64;
3922
3923        if let Some(hs) = self.state.as_handshake_mut() {
3924            hs.expected_token = packet.header.token.clone();
3925        } else {
3926            unreachable!("first packet must be delivered in Handshake state");
3927        }
3928
3929        // The first packet is always on PathId::ZERO
3930        self.on_packet_authenticated(
3931            now,
3932            SpaceKind::Initial,
3933            path_id,
3934            ecn,
3935            Some(packet_number),
3936            false,
3937            false,
3938            &network_path,
3939        );
3940
3941        let packet: Packet = packet.into();
3942
3943        let mut qlog = QlogRecvPacket::new(len);
3944        qlog.header(&packet.header, Some(packet_number), path_id);
3945
3946        self.process_decrypted_packet(
3947            now,
3948            network_path,
3949            path_id,
3950            Some(packet_number),
3951            packet,
3952            &mut qlog,
3953        )?;
3954        self.qlog.emit_packet_received(qlog, now);
3955        if let Some(data) = remaining {
3956            self.handle_coalesced(now, network_path, path_id, ecn, data);
3957        }
3958
3959        self.qlog.emit_recovery_metrics(
3960            path_id,
3961            &mut self
3962                .paths
3963                .get_mut(&path_id)
3964                .expect("path_id was supplied by the caller for an active path")
3965                .data,
3966            now,
3967        );
3968
3969        Ok(())
3970    }
3971
3972    fn init_0rtt(&mut self, now: Instant) {
3973        let Some((header, packet)) = self.crypto_state.session.early_crypto() else {
3974            return;
3975        };
3976        if self.side.is_client() {
3977            match self.crypto_state.session.transport_parameters() {
3978                Ok(params) => {
3979                    let params = params
3980                        .expect("crypto layer didn't supply transport parameters with ticket");
3981                    // Certain values must not be cached
3982                    let params = TransportParameters {
3983                        initial_src_cid: None,
3984                        original_dst_cid: None,
3985                        preferred_address: None,
3986                        retry_src_cid: None,
3987                        stateless_reset_token: None,
3988                        min_ack_delay: None,
3989                        ack_delay_exponent: TransportParameters::default().ack_delay_exponent,
3990                        max_ack_delay: TransportParameters::default().max_ack_delay,
3991                        initial_max_path_id: None,
3992                        ..params
3993                    };
3994                    self.set_peer_params(params);
3995                    self.qlog.emit_peer_transport_params_restored(self, now);
3996                }
3997                Err(e) => {
3998                    error!("session ticket has malformed transport parameters: {}", e);
3999                    return;
4000                }
4001            }
4002        }
4003        trace!("0-RTT enabled");
4004        self.crypto_state.enable_zero_rtt(header, packet);
4005    }
4006
4007    fn read_crypto(
4008        &mut self,
4009        space: SpaceId,
4010        crypto: &frame::Crypto,
4011        payload_len: usize,
4012    ) -> Result<(), TransportError> {
4013        let expected = if !self.state.is_handshake() {
4014            SpaceId::Data
4015        } else if self.highest_space == SpaceKind::Initial {
4016            SpaceId::Initial
4017        } else {
4018            // On the server, self.highest_space can be Data after receiving the client's first
4019            // flight, but we expect Handshake CRYPTO until the handshake is complete.
4020            SpaceId::Handshake
4021        };
4022        // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT
4023        // packets are illegal, and we don't process 1-RTT packets until the handshake is
4024        // complete. Therefore, we will never see CRYPTO data from a later-than-expected space.
4025        debug_assert!(space <= expected, "received out-of-order CRYPTO data");
4026
4027        let end = crypto.offset + crypto.data.len() as u64;
4028        if space < expected
4029            && end
4030                > self.crypto_state.spaces[space.kind()]
4031                    .crypto_stream
4032                    .bytes_read()
4033        {
4034            warn!(
4035                "received new {:?} CRYPTO data when expecting {:?}",
4036                space, expected
4037            );
4038            return Err(TransportError::PROTOCOL_VIOLATION(
4039                "new data at unexpected encryption level",
4040            ));
4041        }
4042
4043        let crypto_space = &mut self.crypto_state.spaces[space.kind()];
4044        let max = end.saturating_sub(crypto_space.crypto_stream.bytes_read());
4045        if max > self.config.crypto_buffer_size as u64 {
4046            return Err(TransportError::CRYPTO_BUFFER_EXCEEDED(""));
4047        }
4048
4049        crypto_space
4050            .crypto_stream
4051            .insert(crypto.offset, crypto.data.clone(), payload_len);
4052        while let Some(chunk) = crypto_space.crypto_stream.read(usize::MAX, true) {
4053            trace!("consumed {} CRYPTO bytes", chunk.bytes.len());
4054            if self.crypto_state.session.read_handshake(&chunk.bytes)? {
4055                self.events.push_back(Event::HandshakeDataReady);
4056            }
4057        }
4058
4059        Ok(())
4060    }
4061
4062    fn write_crypto(&mut self) {
4063        loop {
4064            let space = self.highest_space;
4065            let mut outgoing = Vec::new();
4066            if let Some(crypto) = self.crypto_state.session.write_handshake(&mut outgoing) {
4067                match space {
4068                    SpaceKind::Initial => {
4069                        self.upgrade_crypto(SpaceKind::Handshake, crypto);
4070                    }
4071                    SpaceKind::Handshake => {
4072                        self.upgrade_crypto(SpaceKind::Data, crypto);
4073                    }
4074                    SpaceKind::Data => unreachable!("got updated secrets during 1-RTT"),
4075                }
4076            }
4077            if outgoing.is_empty() {
4078                if space == self.highest_space {
4079                    break;
4080                } else {
4081                    // Keys updated, check for more data to send
4082                    continue;
4083                }
4084            }
4085            let offset = self.crypto_state.spaces[space].crypto_offset;
4086            let outgoing = Bytes::from(outgoing);
4087            if let Some(hs) = self.state.as_handshake_mut()
4088                && space == SpaceKind::Initial
4089                && offset == 0
4090                && self.side.is_client()
4091            {
4092                hs.client_hello = Some(outgoing.clone());
4093            }
4094            self.crypto_state.spaces[space].crypto_offset += outgoing.len() as u64;
4095            trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space);
4096            self.spaces[space].pending.crypto.push_back(frame::Crypto {
4097                offset,
4098                data: outgoing,
4099            });
4100        }
4101    }
4102
4103    /// Switch to stronger cryptography during handshake
4104    fn upgrade_crypto(&mut self, space: SpaceKind, crypto: Keys) {
4105        debug_assert!(
4106            !self.crypto_state.has_keys(space.encryption_level()),
4107            "already reached packet space {space:?}"
4108        );
4109        trace!("{:?} keys ready", space);
4110        if space == SpaceKind::Data {
4111            // Precompute the first key update
4112            self.crypto_state.next_crypto = Some(
4113                self.crypto_state
4114                    .session
4115                    .next_1rtt_keys()
4116                    .expect("handshake should be complete"),
4117            );
4118        }
4119
4120        self.crypto_state.spaces[space].keys = Some(crypto);
4121        debug_assert!(space > self.highest_space);
4122        self.highest_space = space;
4123        if space == SpaceKind::Data && self.side.is_client() {
4124            // Discard 0-RTT keys because 1-RTT keys are available.
4125            self.crypto_state.discard_zero_rtt();
4126        }
4127    }
4128
4129    fn discard_space(&mut self, now: Instant, space: SpaceKind) {
4130        debug_assert!(space != SpaceKind::Data);
4131        trace!("discarding {:?} keys", space);
4132        if space == SpaceKind::Initial {
4133            // No longer needed
4134            if let ConnectionSide::Client { token, .. } = &mut self.side {
4135                *token = Bytes::new();
4136            }
4137        }
4138        self.crypto_state.spaces[space].keys = None;
4139        let space = &mut self.spaces[space];
4140        let pns = space.for_path(PathId::ZERO);
4141        pns.time_of_last_ack_eliciting_packet = None;
4142        pns.loss_time = None;
4143        pns.loss_probes = 0;
4144        let sent_packets = mem::take(&mut pns.sent_packets);
4145        let path = self
4146            .paths
4147            .get_mut(&PathId::ZERO)
4148            .expect("PathId::ZERO is alive while Initial/Handshake spaces exist");
4149        for (_, packet) in sent_packets.into_iter() {
4150            path.data.remove_in_flight(&packet);
4151        }
4152
4153        self.set_loss_detection_timer(now, PathId::ZERO)
4154    }
4155
4156    fn handle_coalesced(
4157        &mut self,
4158        now: Instant,
4159        network_path: FourTuple,
4160        path_id: PathId,
4161        ecn: Option<EcnCodepoint>,
4162        data: BytesMut,
4163    ) {
4164        self.path_data_mut(path_id)
4165            .inc_total_recvd(data.len() as u64);
4166        let mut remaining = Some(data);
4167        let cid_len = self
4168            .local_cid_state
4169            .values()
4170            .map(|cid_state| cid_state.cid_len())
4171            .next()
4172            .expect("one cid_state must exist");
4173        while let Some(data) = remaining {
4174            match PartialDecode::new(
4175                data,
4176                &FixedLengthConnectionIdParser::new(cid_len),
4177                &[self.version],
4178                self.endpoint_config.grease_quic_bit,
4179            ) {
4180                Ok((partial_decode, rest)) => {
4181                    remaining = rest;
4182                    self.handle_decode(now, network_path, path_id, ecn, partial_decode);
4183                }
4184                Err(e) => {
4185                    trace!("malformed header: {}", e);
4186                    return;
4187                }
4188            }
4189        }
4190    }
4191
4192    /// Decrypts the packet and processes the payload.
4193    ///
4194    /// Processes the entire packet, starting with removing header protection, then handling
4195    /// a stateless reset if needed, and decrypting and processing the frames in the payload
4196    /// if not a stateless reset.
4197    fn handle_decode(
4198        &mut self,
4199        now: Instant,
4200        network_path: FourTuple,
4201        path_id: PathId,
4202        ecn: Option<EcnCodepoint>,
4203        partial_decode: PartialDecode,
4204    ) {
4205        let qlog = QlogRecvPacket::new(partial_decode.len());
4206        if let Some(decoded) = self
4207            .crypto_state
4208            .unprotect_header(partial_decode, self.peer_params.stateless_reset_token)
4209        {
4210            self.handle_packet(
4211                now,
4212                network_path,
4213                path_id,
4214                ecn,
4215                decoded.packet,
4216                decoded.stateless_reset,
4217                qlog,
4218            );
4219        }
4220    }
4221
4222    /// Handles a packet with header protection removed.
4223    ///
4224    /// The packet body is still encrypted at this point.
4225    ///
4226    /// If the datagram was a stateless reset we may have failed to remove header protection
4227    /// and thus `packet` may be `None`.
4228    fn handle_packet(
4229        &mut self,
4230        now: Instant,
4231        network_path: FourTuple,
4232        path_id: PathId,
4233        ecn: Option<EcnCodepoint>,
4234        packet: Option<Packet>,
4235        stateless_reset: bool,
4236        mut qlog: QlogRecvPacket,
4237    ) {
4238        if let Some(ref packet) = packet {
4239            trace!(
4240                "got {:?} packet ({} bytes) from {} using id {}",
4241                packet.header.space(),
4242                packet.payload.len() + packet.header_data.len(),
4243                network_path,
4244                packet.header.dst_cid(),
4245            );
4246        }
4247
4248        let was_closed = self.state.is_closed();
4249        let was_drained = self.state.is_drained();
4250
4251        // Now decrypt the packet payload in-place.
4252        let decrypted = match packet {
4253            None => Err(None),
4254            Some(mut packet) => self
4255                .decrypt_packet(now, path_id, &mut packet)
4256                .map(move |number| (packet, number)),
4257        };
4258        let result = match decrypted {
4259            _ if stateless_reset => {
4260                debug!("got stateless reset");
4261                Err(ConnectionError::Reset)
4262            }
4263            Err(Some(e)) => {
4264                warn!("illegal packet: {}", e);
4265                Err(e.into())
4266            }
4267            Err(None) => {
4268                debug!("failed to authenticate packet");
4269                self.authentication_failures += 1;
4270                let integrity_limit = self
4271                    .crypto_state
4272                    .integrity_limit(self.highest_space)
4273                    .unwrap();
4274                if self.authentication_failures > integrity_limit {
4275                    Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into())
4276                } else {
4277                    return;
4278                }
4279            }
4280            Ok((packet, pn)) => {
4281                // We received an authenticated packet and decrypted it.
4282                qlog.header(&packet.header, pn, path_id);
4283                let span = match pn {
4284                    Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
4285                    None => trace_span!("recv", space = ?packet.header.space()),
4286                };
4287                let _guard = span.enter();
4288
4289                // Now the packet is authenticated we do the migration during the handshake,
4290                // see Handshake::allow_server_migration for details.  Be careful here to
4291                // not yet rely on the path existing however, new paths are accepted and
4292                // created later.
4293                // Note that we can't do any other migrations yet, for those we need to know
4294                // whether this was a probing packet or not. See the end of
4295                // Self::process_packet for that.
4296                if self.is_handshaking()
4297                    && self
4298                        .path(path_id)
4299                        .map(|path_data| {
4300                            !path_data.network_path.is_probably_same_path(&network_path)
4301                        })
4302                        .unwrap_or(false)
4303                {
4304                    if let Some(hs) = self.state.as_handshake()
4305                        && hs.allow_server_migration
4306                    {
4307                        trace!(
4308                            %network_path,
4309                            prev = %self.path_data(path_id).network_path,
4310                            "server migrated to new remote",
4311                        );
4312                        self.path_data_mut(path_id).network_path = network_path;
4313                        self.qlog.emit_tuple_assigned(path_id, network_path, now);
4314                    } else {
4315                        debug!(
4316                            recv_path = %network_path,
4317                            expected_path = %self.path_data_mut(path_id).network_path,
4318                            "discarding packet with unexpected remote during handshake",
4319                        );
4320                        return;
4321                    }
4322                }
4323
4324                let dedup = self.spaces[packet.header.space()]
4325                    .path_space_mut(path_id)
4326                    .map(|pns| &mut pns.dedup);
4327                if pn.zip(dedup).is_some_and(|(n, d)| d.insert(n)) {
4328                    debug!("discarding possible duplicate packet");
4329                    self.qlog.emit_packet_received(qlog, now);
4330                    return;
4331                } else if self.state.is_handshake() && packet.header.is_short() {
4332                    // TODO: SHOULD buffer these to improve reordering tolerance.
4333                    trace!("dropping short packet during handshake");
4334                    self.qlog.emit_packet_received(qlog, now);
4335                    return;
4336                } else {
4337                    if let Header::Initial(InitialHeader { ref token, .. }) = packet.header
4338                        && let Some(hs) = self.state.as_handshake()
4339                        && self.side.is_server()
4340                        && token != &hs.expected_token
4341                    {
4342                        // Clients must send the same retry token in every Initial. Initial
4343                        // packets can be spoofed, so we discard rather than killing the
4344                        // connection.
4345                        warn!("discarding Initial with invalid retry token");
4346                        self.qlog.emit_packet_received(qlog, now);
4347                        return;
4348                    }
4349
4350                    if !self.state.is_closed() {
4351                        let spin = match packet.header {
4352                            Header::Short { spin, .. } => spin,
4353                            _ => false,
4354                        };
4355
4356                        if self.side().is_server() && !self.abandoned_paths.contains(&path_id) {
4357                            // Only the client is allowed to open paths
4358                            self.create_path(path_id, network_path, now, pn);
4359                        }
4360                        if self.paths.contains_key(&path_id) {
4361                            self.on_packet_authenticated(
4362                                now,
4363                                packet.header.space(),
4364                                path_id,
4365                                ecn,
4366                                pn,
4367                                spin,
4368                                packet.header.is_1rtt(),
4369                                &network_path,
4370                            );
4371                        }
4372                    }
4373
4374                    let res = self.process_decrypted_packet(
4375                        now,
4376                        network_path,
4377                        path_id,
4378                        pn,
4379                        packet,
4380                        &mut qlog,
4381                    );
4382
4383                    self.qlog.emit_packet_received(qlog, now);
4384                    res
4385                }
4386            }
4387        };
4388
4389        // State transitions for error cases
4390        if let Err(conn_err) = result {
4391            match conn_err {
4392                ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason),
4393                ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason),
4394                ConnectionError::Reset
4395                | ConnectionError::TransportError(TransportError {
4396                    code: TransportErrorCode::AEAD_LIMIT_REACHED,
4397                    ..
4398                }) => {
4399                    if !self.state.is_drained() {
4400                        self.state
4401                            .move_to_drained(Some(conn_err), &mut self.endpoint_events);
4402                    }
4403                }
4404                ConnectionError::TimedOut => {
4405                    unreachable!("timeouts aren't generated by packet processing");
4406                }
4407                ConnectionError::TransportError(err) => {
4408                    debug!("closing connection due to transport error: {}", err);
4409                    self.state.move_to_closed(err);
4410                }
4411                ConnectionError::VersionMismatch => {
4412                    self.state
4413                        .move_to_draining(Some(conn_err), &mut self.endpoint_events);
4414                }
4415                ConnectionError::LocallyClosed => {
4416                    unreachable!("LocallyClosed isn't generated by packet processing");
4417                }
4418                ConnectionError::CidsExhausted => {
4419                    unreachable!("CidsExhausted isn't generated by packet processing");
4420                }
4421            };
4422        }
4423
4424        if !was_closed && self.state.is_closed() {
4425            self.close_common();
4426            if !self.state.is_drained() {
4427                self.set_close_timer(now);
4428            }
4429        }
4430        if !was_drained && self.state.is_drained() {
4431            // Close timer may have been started previously, e.g. if we sent a close and got a
4432            // stateless reset in response
4433            self.timers
4434                .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now));
4435        }
4436
4437        // Transmit CONNECTION_CLOSE if necessary.
4438        //
4439        // If we received a valid packet and we are in the closed state we should respond
4440        // with a CONNECTION_CLOSE frame.
4441        // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but
4442        //    that does not yet happen. This is triggered by each received packet.
4443        if matches!(self.state.as_type(), StateType::Closed) {
4444            // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7
4445            //
4446            // While in the closing state we must either:
4447            // - discard packets coming from an un-validated remote OR
4448            // - ensure we do not send more than 3 times the received data
4449            //
4450            // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer
4451            // who was (involuntary) migrated just at the time we initiated immediate
4452            // close. It is a lot more work though. So while we would like to do this for
4453            // now we only do 1.
4454            //
4455            // Another shortcoming of the current implementation is that when we have a
4456            // previous PathData which is validated and the remote matches that path, we
4457            // should schedule CONNECTION_CLOSE on that path. However currently we can not
4458            // schedule such a packet. We should also fix this some day. This makes us
4459            // vulnerable to an attacker faking a migration at the right time and then we'd
4460            // be unable to send the CONNECTION_CLOSE to the real remote.
4461            if self
4462                .paths
4463                .get(&path_id)
4464                .map(|p| p.data.validated && p.data.network_path == network_path)
4465                .unwrap_or(false)
4466            {
4467                self.connection_close_pending = true;
4468            }
4469        }
4470    }
4471
4472    fn process_decrypted_packet(
4473        &mut self,
4474        now: Instant,
4475        network_path: FourTuple,
4476        path_id: PathId,
4477        number: Option<u64>,
4478        packet: Packet,
4479        qlog: &mut QlogRecvPacket,
4480    ) -> Result<(), ConnectionError> {
4481        if !self.paths.contains_key(&path_id) {
4482            // There is a chance this is a server side, first (for this path) packet, which would
4483            // be a protocol violation. It's more likely, however, that this is a packet of a
4484            // pruned path
4485            trace!(%path_id, ?number, "discarding packet for unknown path");
4486            return Ok(());
4487        }
4488        let state = match self.state.as_type() {
4489            StateType::Established => {
4490                match packet.header.space() {
4491                    SpaceKind::Data => self.process_payload(
4492                        now,
4493                        network_path,
4494                        path_id,
4495                        number.unwrap(),
4496                        packet,
4497                        qlog,
4498                    )?,
4499                    _ if packet.header.has_frames() => {
4500                        self.process_early_payload(now, path_id, packet, qlog)?
4501                    }
4502                    _ => {
4503                        trace!("discarding unexpected pre-handshake packet");
4504                    }
4505                }
4506                return Ok(());
4507            }
4508            StateType::Closed => {
4509                for result in frame::Iter::new(packet.payload.freeze())? {
4510                    let frame = match result {
4511                        Ok(frame) => frame,
4512                        Err(err) => {
4513                            debug!("frame decoding error: {err:?}");
4514                            continue;
4515                        }
4516                    };
4517                    qlog.frame(&frame);
4518
4519                    if let Frame::Padding = frame {
4520                        continue;
4521                    };
4522
4523                    trace!(?frame, "processing frame in closed state");
4524
4525                    self.path_stats.get_mut(path_id).frame_rx.record(frame.ty());
4526
4527                    if let Frame::Close(_error) = frame {
4528                        self.state.move_to_draining(None, &mut self.endpoint_events);
4529                        break;
4530                    }
4531                }
4532                return Ok(());
4533            }
4534            StateType::Draining | StateType::Drained => return Ok(()),
4535            StateType::Handshake => self.state.as_handshake_mut().expect("checked"),
4536        };
4537
4538        match packet.header {
4539            Header::Retry {
4540                src_cid: remote_cid,
4541                ..
4542            } => {
4543                debug_assert_eq!(path_id, PathId::ZERO);
4544                if self.side.is_server() {
4545                    return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into());
4546                }
4547
4548                let is_valid_retry = self
4549                    .remote_cids
4550                    .get(&path_id)
4551                    .map(|cids| cids.active())
4552                    .map(|orig_dst_cid| {
4553                        self.crypto_state.session.is_valid_retry(
4554                            orig_dst_cid,
4555                            &packet.header_data,
4556                            &packet.payload,
4557                        )
4558                    })
4559                    .unwrap_or_default();
4560                if self.total_authed_packets > 1
4561                            || packet.payload.len() <= 16 // token + 16 byte tag
4562                            || !is_valid_retry
4563                {
4564                    trace!("discarding invalid Retry");
4565                    // - After the client has received and processed an Initial or Retry packet from
4566                    //   the server, it MUST discard any subsequent Retry packets that it receives.
4567                    // - A client MUST discard a Retry packet with a zero-length Retry Token field.
4568                    // - Clients MUST discard Retry packets that have a Retry Integrity Tag that
4569                    //   cannot be validated
4570                    return Ok(());
4571                }
4572
4573                trace!("retrying with CID {}", remote_cid);
4574                let client_hello = state.client_hello.take().unwrap();
4575                self.retry_src_cid = Some(remote_cid);
4576                self.remote_cids
4577                    .get_mut(&path_id)
4578                    .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false")
4579                    .update_initial_cid(remote_cid);
4580                self.remote_handshake_cid = remote_cid;
4581
4582                let space = &mut self.spaces[SpaceId::Initial];
4583                if let Some(info) = space.for_path(PathId::ZERO).take(0) {
4584                    self.on_packet_acked(now, PathId::ZERO, 0, info);
4585                };
4586
4587                self.discard_space(now, SpaceKind::Initial); // Make sure we clean up after
4588                // any retransmitted Initials
4589                let crypto_space = &mut self.crypto_state.spaces[SpaceKind::Initial];
4590                crypto_space.keys = Some(
4591                    self.crypto_state
4592                        .session
4593                        .initial_keys(remote_cid, self.side.side()),
4594                );
4595                crypto_space.crypto_offset = client_hello.len() as u64;
4596
4597                let next_pn = self.spaces[SpaceId::Initial]
4598                    .for_path(path_id)
4599                    .next_packet_number;
4600                self.spaces[SpaceId::Initial] = {
4601                    let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng);
4602                    space.for_path(path_id).next_packet_number = next_pn;
4603                    space.pending.crypto.push_back(frame::Crypto {
4604                        offset: 0,
4605                        data: client_hello,
4606                    });
4607                    space
4608                };
4609
4610                // Retransmit all 0-RTT data
4611                let zero_rtt = mem::take(
4612                    &mut self.spaces[SpaceId::Data]
4613                        .for_path(PathId::ZERO)
4614                        .sent_packets,
4615                );
4616                for (_, info) in zero_rtt.into_iter() {
4617                    self.paths
4618                        .get_mut(&PathId::ZERO)
4619                        .unwrap()
4620                        .remove_in_flight(&info);
4621                    self.spaces[SpaceId::Data].pending |= info.retransmits;
4622                }
4623                self.streams.retransmit_all_for_0rtt();
4624
4625                let token_len = packet.payload.len() - 16;
4626                let ConnectionSide::Client { ref mut token, .. } = self.side else {
4627                    unreachable!("we already short-circuited if we're server");
4628                };
4629                *token = packet.payload.freeze().split_to(token_len);
4630
4631                self.state = State::handshake(state::Handshake {
4632                    expected_token: Bytes::new(),
4633                    remote_cid_set: false,
4634                    client_hello: None,
4635                    allow_server_migration: self.config.server_handshake_migration,
4636                });
4637                Ok(())
4638            }
4639            Header::Long {
4640                ty: LongType::Handshake,
4641                src_cid: remote_cid,
4642                dst_cid: local_cid,
4643                ..
4644            } => {
4645                debug_assert_eq!(path_id, PathId::ZERO);
4646                if remote_cid != self.remote_handshake_cid {
4647                    debug!(
4648                        "discarding packet with mismatched remote CID: {} != {}",
4649                        self.remote_handshake_cid, remote_cid
4650                    );
4651                    return Ok(());
4652                }
4653                self.on_path_validated(path_id);
4654
4655                self.process_early_payload(now, path_id, packet, qlog)?;
4656                if self.state.is_closed() {
4657                    return Ok(());
4658                }
4659
4660                if self.crypto_state.session.is_handshaking() {
4661                    trace!("handshake ongoing");
4662                    return Ok(());
4663                }
4664
4665                if self.side.is_client() {
4666                    // Client-only because server params were set from the client's Initial
4667                    let params = self
4668                        .crypto_state
4669                        .session
4670                        .transport_parameters()?
4671                        .ok_or_else(|| {
4672                            TransportError::new(
4673                                TransportErrorCode::crypto(0x6d),
4674                                "transport parameters missing".to_owned(),
4675                            )
4676                        })?;
4677
4678                    if self.has_0rtt() {
4679                        if !self.crypto_state.session.early_data_accepted().unwrap() {
4680                            debug_assert!(self.side.is_client());
4681                            debug!("0-RTT rejected");
4682                            self.crypto_state.accepted_0rtt = false;
4683                            self.streams.zero_rtt_rejected();
4684
4685                            // Discard already-queued frames
4686                            self.spaces[SpaceId::Data].pending = Retransmits::default();
4687
4688                            // Discard 0-RTT packets
4689                            let sent_packets = mem::take(
4690                                &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets,
4691                            );
4692                            for (_, packet) in sent_packets.into_iter() {
4693                                self.paths
4694                                    .get_mut(&path_id)
4695                                    .unwrap()
4696                                    .remove_in_flight(&packet);
4697                            }
4698                        } else {
4699                            self.crypto_state.accepted_0rtt = true;
4700                            params.validate_resumption_from(&self.peer_params)?;
4701                        }
4702                    }
4703                    if let Some(token) = params.stateless_reset_token {
4704                        let remote = self.path_data(path_id).network_path.remote;
4705                        debug_assert!(!self.state.is_drained()); // requirement for endpoint events, checked above
4706                        self.endpoint_events
4707                            .push_back(EndpointEventInner::ResetToken(path_id, remote, token));
4708                    }
4709                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
4710                    self.issue_first_cids(now);
4711                } else {
4712                    // Server-only
4713                    self.spaces[SpaceId::Data].pending.handshake_done = true;
4714                    self.discard_space(now, SpaceKind::Handshake);
4715                    self.events.push_back(Event::HandshakeConfirmed);
4716                    trace!("handshake confirmed");
4717                }
4718
4719                self.events.push_back(Event::Connected);
4720                self.state.move_to_established();
4721                trace!("established");
4722
4723                // Multipath can only be enabled after the state has reached Established.
4724                // So this can not happen any earlier.
4725                self.issue_first_path_cids(now);
4726                self.rearm_path_max_idle_timer(now, self.highest_space, path_id);
4727                Ok(())
4728            }
4729            Header::Initial(InitialHeader {
4730                src_cid: remote_cid,
4731                dst_cid: local_cid,
4732                ..
4733            }) => {
4734                debug_assert_eq!(path_id, PathId::ZERO);
4735                if !state.remote_cid_set {
4736                    trace!("switching remote CID to {}", remote_cid);
4737                    let mut state = state.clone();
4738                    self.remote_cids
4739                        .get_mut(&path_id)
4740                        .expect("PathId::ZERO not yet abandoned")
4741                        .update_initial_cid(remote_cid);
4742                    self.remote_handshake_cid = remote_cid;
4743                    self.original_remote_cid = remote_cid;
4744                    state.remote_cid_set = true;
4745                    self.state.move_to_handshake(state);
4746                } else if remote_cid != self.remote_handshake_cid {
4747                    debug!(
4748                        "discarding packet with mismatched remote CID: {} != {}",
4749                        self.remote_handshake_cid, remote_cid
4750                    );
4751                    return Ok(());
4752                }
4753
4754                let starting_space = self.highest_space;
4755                self.process_early_payload(now, path_id, packet, qlog)?;
4756
4757                if self.side.is_server()
4758                    && starting_space == SpaceKind::Initial
4759                    && self.highest_space != SpaceKind::Initial
4760                {
4761                    let params = self
4762                        .crypto_state
4763                        .session
4764                        .transport_parameters()?
4765                        .ok_or_else(|| {
4766                            TransportError::new(
4767                                TransportErrorCode::crypto(0x6d),
4768                                "transport parameters missing".to_owned(),
4769                            )
4770                        })?;
4771                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
4772                    self.issue_first_cids(now);
4773                    self.init_0rtt(now);
4774                }
4775                Ok(())
4776            }
4777            Header::Long {
4778                ty: LongType::ZeroRtt,
4779                ..
4780            } => {
4781                self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?;
4782                Ok(())
4783            }
4784            Header::VersionNegotiate { .. } => {
4785                if self.total_authed_packets > 1 {
4786                    return Ok(());
4787                }
4788                let supported = packet
4789                    .payload
4790                    .chunks(4)
4791                    .any(|x| match <[u8; 4]>::try_from(x) {
4792                        Ok(version) => self.version == u32::from_be_bytes(version),
4793                        Err(_) => false,
4794                    });
4795                if supported {
4796                    return Ok(());
4797                }
4798                debug!("remote doesn't support our version");
4799                Err(ConnectionError::VersionMismatch)
4800            }
4801            Header::Short { .. } => unreachable!(
4802                "short packets received during handshake are discarded in handle_packet"
4803            ),
4804        }
4805    }
4806
4807    /// Process an Initial or Handshake packet payload
4808    fn process_early_payload(
4809        &mut self,
4810        now: Instant,
4811        path_id: PathId,
4812        packet: Packet,
4813        #[allow(unused)] qlog: &mut QlogRecvPacket,
4814    ) -> Result<(), TransportError> {
4815        debug_assert_ne!(packet.header.space(), SpaceKind::Data);
4816        debug_assert_eq!(path_id, PathId::ZERO);
4817        let payload_len = packet.payload.len();
4818        let mut ack_eliciting = false;
4819        for result in frame::Iter::new(packet.payload.freeze())? {
4820            let frame = result?;
4821            qlog.frame(&frame);
4822            let span = match frame {
4823                Frame::Padding => continue,
4824                _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)),
4825            };
4826
4827            self.path_stats.get_mut(path_id).frame_rx.record(frame.ty());
4828
4829            let _guard = span.as_ref().map(|x| x.enter());
4830            ack_eliciting |= frame.is_ack_eliciting();
4831
4832            // Process frames
4833            if frame.is_1rtt() && packet.header.space() != SpaceKind::Data {
4834                return Err(TransportError::PROTOCOL_VIOLATION(
4835                    "illegal frame type in handshake",
4836                ));
4837            }
4838
4839            match frame {
4840                Frame::Padding | Frame::Ping => {}
4841                Frame::Crypto(frame) => {
4842                    self.read_crypto(packet.header.space().into(), &frame, payload_len)?;
4843                }
4844                Frame::Ack(ack) => {
4845                    self.on_ack_received(now, packet.header.space().into(), ack)?;
4846                }
4847                Frame::PathAck(ack) => {
4848                    span.as_ref()
4849                        .map(|span| span.record("path", tracing::field::display(&ack.path_id)));
4850                    self.on_path_ack_received(now, packet.header.space().into(), ack)?;
4851                }
4852                Frame::Close(reason) => {
4853                    self.state
4854                        .move_to_draining(Some(reason.into()), &mut self.endpoint_events);
4855                    return Ok(());
4856                }
4857                _ => {
4858                    let mut err =
4859                        TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake");
4860                    err.frame = frame::MaybeFrame::Known(frame.ty());
4861                    return Err(err);
4862                }
4863            }
4864        }
4865
4866        if ack_eliciting {
4867            // In the initial and handshake spaces, ACKs must be sent immediately
4868            self.spaces[packet.header.space()]
4869                .for_path(path_id)
4870                .pending_acks
4871                .set_immediate_ack_required();
4872        }
4873
4874        self.write_crypto();
4875        Ok(())
4876    }
4877
4878    /// Processes the decrypted packet payload, always in the data space.
4879    fn process_payload(
4880        &mut self,
4881        now: Instant,
4882        network_path: FourTuple,
4883        path_id: PathId,
4884        number: u64,
4885        packet: Packet,
4886        #[allow(unused)] qlog: &mut QlogRecvPacket,
4887    ) -> Result<(), TransportError> {
4888        let payload = packet.payload.freeze();
4889        let mut is_probing_packet = true;
4890        let mut close = None;
4891        let payload_len = payload.len();
4892        let mut ack_eliciting = false;
4893        // if this packet triggers a path migration and includes a observed address frame, it's
4894        // stored here
4895        let mut migration_observed_addr = None;
4896        for result in frame::Iter::new(payload)? {
4897            let frame = result?;
4898            qlog.frame(&frame);
4899            let span = match frame {
4900                Frame::Padding => continue,
4901                _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty),
4902            };
4903
4904            self.path_stats.get_mut(path_id).frame_rx.record(frame.ty());
4905            // Crypto, Stream and Datagram frames are special cased in order no pollute
4906            // the log with payload data
4907            match &frame {
4908                Frame::Crypto(f) => {
4909                    trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO");
4910                }
4911                Frame::Stream(f) => {
4912                    trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM");
4913                }
4914                Frame::Datagram(f) => {
4915                    trace!(len = f.data.len(), "got frame DATAGRAM");
4916                }
4917                f => {
4918                    trace!("got frame {f}");
4919                }
4920            }
4921
4922            let _guard = span.enter();
4923            if packet.header.is_0rtt() {
4924                match frame {
4925                    Frame::Crypto(_) | Frame::Close(Close::Application(_)) => {
4926                        return Err(TransportError::PROTOCOL_VIOLATION(
4927                            "illegal frame type in 0-RTT",
4928                        ));
4929                    }
4930                    _ => {
4931                        if frame.is_1rtt() {
4932                            return Err(TransportError::PROTOCOL_VIOLATION(
4933                                "illegal frame type in 0-RTT",
4934                            ));
4935                        }
4936                    }
4937                }
4938            }
4939            ack_eliciting |= frame.is_ack_eliciting();
4940
4941            // Check whether this could be a probing packet
4942            match frame {
4943                Frame::Padding
4944                | Frame::PathChallenge(_)
4945                | Frame::PathResponse(_)
4946                | Frame::NewConnectionId(_)
4947                | Frame::ObservedAddr(_) => {}
4948                _ => {
4949                    is_probing_packet = false;
4950                }
4951            }
4952
4953            match frame {
4954                Frame::Crypto(frame) => {
4955                    self.read_crypto(SpaceId::Data, &frame, payload_len)?;
4956                }
4957                Frame::Stream(frame) => {
4958                    if self.streams.received(frame, payload_len)?.should_transmit() {
4959                        self.spaces[SpaceId::Data].pending.max_data = true;
4960                    }
4961                }
4962                Frame::Ack(ack) => {
4963                    self.on_ack_received(now, SpaceId::Data, ack)?;
4964                }
4965                Frame::PathAck(ack) => {
4966                    if !self.is_multipath_negotiated() {
4967                        return Err(TransportError::PROTOCOL_VIOLATION(
4968                            "received PATH_ACK frame when multipath was not negotiated",
4969                        ));
4970                    }
4971                    span.record("path", tracing::field::display(&ack.path_id));
4972                    self.on_path_ack_received(now, SpaceId::Data, ack)?;
4973                }
4974                Frame::Padding | Frame::Ping => {}
4975                Frame::Close(reason) => {
4976                    close = Some(reason);
4977                }
4978                Frame::PathChallenge(challenge) => {
4979                    self.spaces[SpaceKind::Data]
4980                        .for_path(path_id)
4981                        .pending_path_responses
4982                        .push(number, challenge.0, network_path);
4983                    // If we were passively migrated (e.g. NAT rebinding), our local_ip will
4984                    // not match. Once we processed a non-probing packet the local_ip will
4985                    // finally be updated.
4986                    let path = &mut self
4987                        .path_mut(path_id)
4988                        .expect("payload is processed only after the path becomes known");
4989                    if network_path.remote == path.network_path.remote {
4990                        // PATH_CHALLENGE on active path, possible off-path packet
4991                        // forwarding attack. Send a non-probing packet to recover the
4992                        // active path. See
4993                        // https://www.rfc-editor.org/rfc/rfc9000.html#section-9.3.3-3. In
4994                        // rare cases NAT probes might also appear on-path and would also
4995                        // get a non-probing packet as response. There is little harm in
4996                        // this.
4997                        match self.peer_supports_ack_frequency() {
4998                            true => self.immediate_ack(path_id),
4999                            false => {
5000                                self.ping_path(path_id).ok();
5001                            }
5002                        }
5003                    }
5004                }
5005                Frame::PathResponse(response) => {
5006                    // First try to see if this is a NAT probe response.
5007                    if self
5008                        .n0_nat_traversal
5009                        .handle_path_response(network_path, response.0)
5010                    {
5011                        self.open_nat_traversed_paths(now);
5012                    } else {
5013                        // Try to see if this is a response to an on-path PATH_CHALLENGE.
5014                        self.handle_path_response_on_path(now, response, path_id);
5015                    }
5016                }
5017                Frame::MaxData(frame::MaxData(bytes)) => {
5018                    self.streams.received_max_data(bytes);
5019                }
5020                Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => {
5021                    self.streams.received_max_stream_data(id, offset)?;
5022                }
5023                Frame::MaxStreams(frame::MaxStreams { dir, count }) => {
5024                    self.streams.received_max_streams(dir, count)?;
5025                }
5026                Frame::ResetStream(frame) => {
5027                    if self.streams.received_reset(frame)?.should_transmit() {
5028                        self.spaces[SpaceId::Data].pending.max_data = true;
5029                    }
5030                }
5031                Frame::DataBlocked(DataBlocked(offset)) => {
5032                    debug!(offset, "peer claims to be blocked at connection level");
5033                }
5034                Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
5035                    if id.initiator() == self.side.side() && id.dir() == Dir::Uni {
5036                        debug!("got STREAM_DATA_BLOCKED on send-only {}", id);
5037                        return Err(TransportError::STREAM_STATE_ERROR(
5038                            "STREAM_DATA_BLOCKED on send-only stream",
5039                        ));
5040                    }
5041                    debug!(
5042                        stream = %id,
5043                        offset, "peer claims to be blocked at stream level"
5044                    );
5045                }
5046                Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => {
5047                    if limit > MAX_STREAM_COUNT {
5048                        return Err(TransportError::FRAME_ENCODING_ERROR(
5049                            "unrepresentable stream limit",
5050                        ));
5051                    }
5052                    debug!(
5053                        "peer claims to be blocked opening more than {} {} streams",
5054                        limit, dir
5055                    );
5056                }
5057                Frame::StopSending(frame::StopSending { id, error_code }) => {
5058                    if id.initiator() != self.side.side() {
5059                        if id.dir() == Dir::Uni {
5060                            debug!("got STOP_SENDING on recv-only {}", id);
5061                            return Err(TransportError::STREAM_STATE_ERROR(
5062                                "STOP_SENDING on recv-only stream",
5063                            ));
5064                        }
5065                    } else if self.streams.is_local_unopened(id) {
5066                        return Err(TransportError::STREAM_STATE_ERROR(
5067                            "STOP_SENDING on unopened stream",
5068                        ));
5069                    }
5070                    self.streams.received_stop_sending(id, error_code);
5071                }
5072                Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => {
5073                    if let Some(ref path_id) = path_id {
5074                        span.record("path", tracing::field::display(&path_id));
5075                    }
5076                    let path_id = path_id.unwrap_or_default();
5077                    match self.local_cid_state.get_mut(&path_id) {
5078                        None => debug!(?path_id, "RETIRE_CONNECTION_ID for unknown path"),
5079                        Some(cid_state) => {
5080                            let allow_more_cids = cid_state
5081                                .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?;
5082
5083                            // If the path has closed, we do not issue more CIDs for this path
5084                            // For details see  https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2
5085                            // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames.
5086                            let has_path = !self.abandoned_paths.contains(&path_id);
5087                            let allow_more_cids = allow_more_cids && has_path;
5088
5089                            debug_assert!(!self.state.is_drained()); // required for adding endpoint events, process_payload is never called for drained connections
5090                            self.endpoint_events
5091                                .push_back(EndpointEventInner::RetireConnectionId(
5092                                    now,
5093                                    path_id,
5094                                    sequence,
5095                                    allow_more_cids,
5096                                ));
5097                        }
5098                    }
5099                }
5100                Frame::NewConnectionId(frame) => {
5101                    let path_id = if let Some(path_id) = frame.path_id {
5102                        if !self.is_multipath_negotiated() {
5103                            return Err(TransportError::PROTOCOL_VIOLATION(
5104                                "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated",
5105                            ));
5106                        }
5107                        if path_id > self.local_max_path_id {
5108                            return Err(TransportError::PROTOCOL_VIOLATION(
5109                                "PATH_NEW_CONNECTION_ID contains path_id exceeding current max",
5110                            ));
5111                        }
5112                        path_id
5113                    } else {
5114                        PathId::ZERO
5115                    };
5116
5117                    if let Some(ref path_id) = frame.path_id {
5118                        span.record("path", tracing::field::display(&path_id));
5119                    }
5120
5121                    if self.abandoned_paths.contains(&path_id) {
5122                        trace!("ignoring issued CID for abandoned path");
5123                        continue;
5124                    }
5125                    let remote_cids = self
5126                        .remote_cids
5127                        .entry(path_id)
5128                        .or_insert_with(|| CidQueue::new(frame.id));
5129                    if remote_cids.active().is_empty() {
5130                        return Err(TransportError::PROTOCOL_VIOLATION(
5131                            "NEW_CONNECTION_ID when CIDs aren't in use",
5132                        ));
5133                    }
5134                    if frame.retire_prior_to > frame.sequence {
5135                        return Err(TransportError::PROTOCOL_VIOLATION(
5136                            "NEW_CONNECTION_ID retiring unissued CIDs",
5137                        ));
5138                    }
5139
5140                    use crate::cid_queue::InsertError;
5141                    match remote_cids.insert(frame) {
5142                        Ok(None) => {
5143                            self.open_nat_traversed_paths(now);
5144                        }
5145                        Ok(Some((retired, reset_token))) => {
5146                            let pending_retired =
5147                                &mut self.spaces[SpaceId::Data].pending.retire_cids;
5148                            /// Ensure `pending_retired` cannot grow without bound. Limit is
5149                            /// somewhat arbitrary but very permissive.
5150                            const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10;
5151                            // We don't bother counting in-flight frames because those are bounded
5152                            // by congestion control.
5153                            if (pending_retired.len() as u64)
5154                                .saturating_add(retired.end.saturating_sub(retired.start))
5155                                > MAX_PENDING_RETIRED_CIDS
5156                            {
5157                                return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(
5158                                    "queued too many retired CIDs",
5159                                ));
5160                            }
5161                            pending_retired.extend(retired.map(|seq| (path_id, seq)));
5162                            self.set_reset_token(path_id, network_path.remote, reset_token);
5163                            self.open_nat_traversed_paths(now);
5164                        }
5165                        Err(InsertError::ExceedsLimit) => {
5166                            return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(""));
5167                        }
5168                        Err(InsertError::Retired) => {
5169                            trace!("discarding already-retired");
5170                            // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a
5171                            // range of connection IDs larger than the active connection ID limit
5172                            // was retired all at once via retire_prior_to.
5173                            self.spaces[SpaceId::Data]
5174                                .pending
5175                                .retire_cids
5176                                .push((path_id, frame.sequence));
5177                            continue;
5178                        }
5179                    };
5180
5181                    if self.side.is_server()
5182                        && path_id == PathId::ZERO
5183                        && self
5184                            .remote_cids
5185                            .get(&PathId::ZERO)
5186                            .map(|cids| cids.active_seq() == 0)
5187                            .unwrap_or_default()
5188                    {
5189                        // We're a server still using the initial remote CID for the client, so
5190                        // let's switch immediately to enable clientside stateless resets.
5191                        self.update_remote_cid(PathId::ZERO);
5192                    }
5193                }
5194                Frame::NewToken(NewToken { token }) => {
5195                    let ConnectionSide::Client {
5196                        token_store,
5197                        server_name,
5198                        ..
5199                    } = &self.side
5200                    else {
5201                        return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN"));
5202                    };
5203                    if token.is_empty() {
5204                        return Err(TransportError::FRAME_ENCODING_ERROR("empty token"));
5205                    }
5206                    trace!("got new token");
5207                    token_store.insert(server_name, token);
5208                }
5209                Frame::Datagram(datagram) => {
5210                    if self
5211                        .datagrams
5212                        .received(datagram, &self.config.datagram_receive_buffer_size)?
5213                    {
5214                        self.events.push_back(Event::DatagramReceived);
5215                    }
5216                }
5217                Frame::AckFrequency(ack_frequency) => {
5218                    // This frame can only be sent in the Data space
5219
5220                    if !self.ack_frequency.ack_frequency_received(&ack_frequency)? {
5221                        // The AckFrequency frame is stale (we have already received a more
5222                        // recent one)
5223                        continue;
5224                    }
5225
5226                    // Update the params for all of our paths
5227                    for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() {
5228                        space.pending_acks.set_ack_frequency_params(&ack_frequency);
5229
5230                        // Our `max_ack_delay` has been updated, so we may need to adjust
5231                        // its associated timeout.
5232                        // Packets received on abandoned paths are always acknowledged immediately.
5233                        if !self.abandoned_paths.contains(path_id)
5234                            && let Some(timeout) = space
5235                                .pending_acks
5236                                .max_ack_delay_timeout(self.ack_frequency.max_ack_delay)
5237                        {
5238                            self.timers.set(
5239                                Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
5240                                timeout,
5241                                self.qlog.with_time(now),
5242                            );
5243                        }
5244                    }
5245                }
5246                Frame::ImmediateAck => {
5247                    // This frame can only be sent in the Data space
5248                    for pns in self.spaces[SpaceId::Data].iter_paths_mut() {
5249                        pns.pending_acks.set_immediate_ack_required();
5250                    }
5251                }
5252                Frame::HandshakeDone => {
5253                    if self.side.is_server() {
5254                        return Err(TransportError::PROTOCOL_VIOLATION(
5255                            "client sent HANDSHAKE_DONE",
5256                        ));
5257                    }
5258                    if self.crypto_state.has_keys(EncryptionLevel::Handshake) {
5259                        self.discard_space(now, SpaceKind::Handshake);
5260                        self.events.push_back(Event::HandshakeConfirmed);
5261                        trace!("handshake confirmed");
5262                    }
5263                }
5264                Frame::ObservedAddr(observed) => {
5265                    // check if params allows the peer to send report and this node to receive it
5266                    trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port);
5267                    if !self
5268                        .peer_params
5269                        .address_discovery_role
5270                        .should_report(&self.config.address_discovery_role)
5271                    {
5272                        return Err(TransportError::PROTOCOL_VIOLATION(
5273                            "received OBSERVED_ADDRESS frame when not negotiated",
5274                        ));
5275                    }
5276                    // must only be sent in data space
5277                    if packet.header.space() != SpaceKind::Data {
5278                        return Err(TransportError::PROTOCOL_VIOLATION(
5279                            "OBSERVED_ADDRESS frame outside data space",
5280                        ));
5281                    }
5282
5283                    let space_open_status =
5284                        self.spaces[SpaceKind::Data].for_path(path_id).open_status;
5285                    let path = self.path_data_mut(path_id);
5286                    if path.network_path.remote == network_path.remote {
5287                        if let Some(updated) = path.update_observed_addr_report(observed)
5288                            && space_open_status == OpenStatus::Informed
5289                        {
5290                            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
5291                                id: path_id,
5292                                addr: updated,
5293                            }));
5294                            // otherwise the event is reported when the path is deemed open
5295                        }
5296                    } else {
5297                        // include in migration
5298                        migration_observed_addr = Some(observed)
5299                    }
5300                }
5301                Frame::PathAbandon(frame::PathAbandon {
5302                    path_id,
5303                    error_code,
5304                }) => {
5305                    span.record("path", tracing::field::display(&path_id));
5306                    match self.close_path_inner(
5307                        now,
5308                        path_id,
5309                        PathAbandonReason::RemoteAbandoned {
5310                            error_code: error_code.into(),
5311                        },
5312                    ) {
5313                        Ok(()) => {
5314                            trace!("peer abandoned path");
5315                        }
5316                        Err(ClosePathError::ClosedPath) => {
5317                            trace!("peer abandoned already closed path");
5318                        }
5319                        Err(ClosePathError::MultipathNotNegotiated) => {
5320                            return Err(TransportError::PROTOCOL_VIOLATION(
5321                                "received PATH_ABANDON frame when multipath was not negotiated",
5322                            ));
5323                        }
5324                        Err(ClosePathError::LastOpenPath) => {
5325                            // Not reachable: close_path_inner allows remote abandons
5326                            // for the last path. But handle gracefully just in case.
5327                            error!(
5328                                "peer abandoned last path but close_path_inner returned LastOpenPath"
5329                            );
5330                        }
5331                    };
5332
5333                    // Start draining the path if it still exists and hasn't started draining yet.
5334                    if let Some(path) = self.paths.get_mut(&path_id)
5335                        && !mem::replace(&mut path.data.draining, true)
5336                    {
5337                        let ack_delay = self.ack_frequency.max_ack_delay_for_pto();
5338                        let pto = path.data.rtt.pto_base() + ack_delay;
5339                        self.timers.set(
5340                            Timer::PerPath(path_id, PathTimer::PathDrained),
5341                            now + 3 * pto,
5342                            self.qlog.with_time(now),
5343                        );
5344
5345                        self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8));
5346                    }
5347                }
5348                Frame::PathStatusAvailable(info) => {
5349                    span.record("path", tracing::field::display(&info.path_id));
5350                    if self.is_multipath_negotiated() {
5351                        self.on_path_status(
5352                            info.path_id,
5353                            PathStatus::Available,
5354                            info.status_seq_no,
5355                        );
5356                    } else {
5357                        return Err(TransportError::PROTOCOL_VIOLATION(
5358                            "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated",
5359                        ));
5360                    }
5361                }
5362                Frame::PathStatusBackup(info) => {
5363                    span.record("path", tracing::field::display(&info.path_id));
5364                    if self.is_multipath_negotiated() {
5365                        self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no);
5366                    } else {
5367                        return Err(TransportError::PROTOCOL_VIOLATION(
5368                            "received PATH_STATUS_BACKUP frame when multipath was not negotiated",
5369                        ));
5370                    }
5371                }
5372                Frame::MaxPathId(frame::MaxPathId(path_id)) => {
5373                    span.record("path", tracing::field::display(&path_id));
5374                    if !self.is_multipath_negotiated() {
5375                        return Err(TransportError::PROTOCOL_VIOLATION(
5376                            "received MAX_PATH_ID frame when multipath was not negotiated",
5377                        ));
5378                    }
5379                    // frames that do not increase the path id are ignored
5380                    if path_id > self.remote_max_path_id {
5381                        self.remote_max_path_id = path_id;
5382                        self.issue_first_path_cids(now);
5383                        self.open_nat_traversed_paths(now);
5384                    }
5385                }
5386                Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => {
5387                    // Receipt of a value of Maximum Path Identifier or Path Identifier that
5388                    // is higher than the local maximum value MUST be treated as a
5389                    // connection error of type PROTOCOL_VIOLATION. Ref
5390                    // <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
5391                    if self.is_multipath_negotiated() {
5392                        if max_path_id > self.local_max_path_id {
5393                            return Err(TransportError::PROTOCOL_VIOLATION(
5394                                "PATHS_BLOCKED maximum path identifier was larger than local maximum",
5395                            ));
5396                        }
5397                    } else {
5398                        return Err(TransportError::PROTOCOL_VIOLATION(
5399                            "received PATHS_BLOCKED frame when not multipath was not negotiated",
5400                        ));
5401                    }
5402                }
5403                Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => {
5404                    // Nothing to do.  This is recorded in the frame stats, but otherwise we
5405                    // always issue all CIDs we're allowed to issue, so either this is an
5406                    // impatient peer or a bug on our side.
5407
5408                    // Receipt of a value of Maximum Path Identifier or Path Identifier that
5409                    // is higher than the local maximum value MUST be treated as a
5410                    // connection error of type PROTOCOL_VIOLATION. Ref
5411                    // <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
5412                    if self.is_multipath_negotiated() {
5413                        if path_id > self.local_max_path_id {
5414                            return Err(TransportError::PROTOCOL_VIOLATION(
5415                                "PATH_CIDS_BLOCKED path identifier was larger than local maximum",
5416                            ));
5417                        }
5418                        if self
5419                            .local_cid_state
5420                            .get(&path_id)
5421                            // The PATH_CIDS_BLOCKED frame may arrive after we've discarded the path
5422                            // state. In that case, we can't check for
5423                            // the protocol violation.
5424                            .is_some_and(|cid_state| next_seq.0 > cid_state.active_seq().1 + 1)
5425                        {
5426                            return Err(TransportError::PROTOCOL_VIOLATION(
5427                                "PATH_CIDS_BLOCKED next sequence number larger than in local state",
5428                            ));
5429                        }
5430                        debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED");
5431                    } else {
5432                        return Err(TransportError::PROTOCOL_VIOLATION(
5433                            "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated",
5434                        ));
5435                    }
5436                }
5437                Frame::AddAddress(addr) => {
5438                    let client_state = match self.n0_nat_traversal.client_side_mut() {
5439                        Ok(state) => state,
5440                        Err(err) => {
5441                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
5442                                "Nat traversal(ADD_ADDRESS): {err}"
5443                            )));
5444                        }
5445                    };
5446
5447                    if !client_state.check_remote_address(&addr) {
5448                        // if the address is not valid we flag it, but update anyway
5449                        warn!(?addr, "server sent illegal ADD_ADDRESS frame");
5450                    }
5451
5452                    match client_state.add_remote_address(addr) {
5453                        Ok(maybe_added) => {
5454                            if let Some(added) = maybe_added {
5455                                self.events.push_back(Event::NatTraversal(
5456                                    n0_nat_traversal::Event::AddressAdded(added),
5457                                ));
5458                            }
5459                        }
5460                        Err(e) => {
5461                            warn!(%e, "failed to add remote address")
5462                        }
5463                    }
5464                }
5465                Frame::RemoveAddress(addr) => {
5466                    let client_state = match self.n0_nat_traversal.client_side_mut() {
5467                        Ok(state) => state,
5468                        Err(err) => {
5469                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
5470                                "Nat traversal(REMOVE_ADDRESS): {err}"
5471                            )));
5472                        }
5473                    };
5474                    if let Some(removed_addr) = client_state.remove_remote_address(addr) {
5475                        self.events.push_back(Event::NatTraversal(
5476                            n0_nat_traversal::Event::AddressRemoved(removed_addr),
5477                        ));
5478                    }
5479                }
5480                Frame::ReachOut(reach_out) => {
5481                    let ipv6 = self.is_ipv6();
5482                    let server_state = match self.n0_nat_traversal.server_side_mut() {
5483                        Ok(state) => state,
5484                        Err(err) => {
5485                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
5486                                "Nat traversal(REACH_OUT): {err}"
5487                            )));
5488                        }
5489                    };
5490
5491                    let round_before = server_state.current_round();
5492
5493                    if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) {
5494                        return Err(TransportError::PROTOCOL_VIOLATION(format!(
5495                            "Nat traversal(REACH_OUT): {err}"
5496                        )));
5497                    }
5498
5499                    if server_state.current_round() > round_before {
5500                        // A new round was started, reset the NAT probe retry timer.
5501                        if let Some(delay) =
5502                            self.n0_nat_traversal.retry_delay(self.config.initial_rtt)
5503                        {
5504                            self.timers.set(
5505                                Timer::Conn(ConnTimer::NatTraversalProbeRetry),
5506                                now + delay,
5507                                self.qlog.with_time(now),
5508                            );
5509                        }
5510                    }
5511                }
5512            }
5513        }
5514
5515        let space = self.spaces[SpaceId::Data].for_path(path_id);
5516        if space
5517            .pending_acks
5518            .packet_received(now, number, ack_eliciting, &space.dedup)
5519        {
5520            if self.abandoned_paths.contains(&path_id) {
5521                // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from
5522                // abandoned paths.
5523                space.pending_acks.set_immediate_ack_required();
5524            } else {
5525                self.timers.set(
5526                    Timer::PerPath(path_id, PathTimer::MaxAckDelay),
5527                    now + self.ack_frequency.max_ack_delay,
5528                    self.qlog.with_time(now),
5529                );
5530            }
5531        }
5532
5533        // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets
5534        // on stopped streams. Incoming finishes/resets on open streams are not handled here as they
5535        // are only freed, and hence only issue credit, once the application has been notified
5536        // during a read on the stream.
5537        let pending = &mut self.spaces[SpaceId::Data].pending;
5538        self.streams.queue_max_stream_id(pending);
5539
5540        if let Some(reason) = close {
5541            self.state
5542                .move_to_draining(Some(reason.into()), &mut self.endpoint_events);
5543            self.connection_close_pending = true;
5544        }
5545
5546        // For Multipath any packet triggers migration. For RFC9000 or QNT (+ Multipath)
5547        // only non-probing packets trigger migration.
5548        let migrate_on_any_packet =
5549            self.is_multipath_negotiated() && !self.n0_nat_traversal.is_negotiated();
5550
5551        // Only migrate if this is the largest packet number seen.
5552        let is_largest_received_pn = Some(number)
5553            == self.spaces[SpaceId::Data]
5554                .for_path(path_id)
5555                .largest_received_packet_number;
5556
5557        // If we receive a non-probing packet on a new local IP that means we had a NAT
5558        // rebinding-like migration. We update our local address but do not otherwise
5559        // validate the new path, we only need to validate the path if the peer migrates per
5560        // RFC9000 §9: https://www.rfc-editor.org/rfc/rfc9000.html#section-9-4
5561        if (migrate_on_any_packet || !is_probing_packet)
5562            && is_largest_received_pn
5563            && self.local_ip_may_migrate()
5564            && let Some(new_local_ip) = network_path.local_ip
5565        {
5566            let path_data = self.path_data_mut(path_id);
5567            if path_data
5568                .network_path
5569                .local_ip
5570                .is_some_and(|ip| ip != new_local_ip)
5571            {
5572                debug!(
5573                    %path_id,
5574                    new_4tuple = %network_path,
5575                    prev_4tuple = %path_data.network_path,
5576                    "local address passive migration"
5577                );
5578            }
5579            path_data.network_path.local_ip = Some(new_local_ip)
5580        }
5581
5582        // If the peer migrated to a new address, trigger migration.
5583        if self.peer_may_migrate()
5584            && (migrate_on_any_packet || !is_probing_packet)
5585            && is_largest_received_pn
5586            && network_path.remote != self.path_data(path_id).network_path.remote
5587        {
5588            self.migrate(path_id, now, network_path, migration_observed_addr);
5589            // Break linkability, if possible
5590            self.update_remote_cid(path_id);
5591            self.spin = false;
5592        }
5593
5594        Ok(())
5595    }
5596
5597    /// Handles any on-path PATH_RESPONSE frames.
5598    ///
5599    /// *path_id* and *network_path* are those on which the PATH_RESPONSE was received.
5600    fn handle_path_response_on_path(
5601        &mut self,
5602        now: Instant,
5603        response: frame::PathResponse,
5604        path_id: PathId,
5605    ) {
5606        let is_multipath_negotiated = self.is_multipath_negotiated();
5607        let path = self
5608            .paths
5609            .get_mut(&path_id)
5610            .expect("payload is processed only after the path becomes known");
5611        match path.data.on_path_response_received(now, response.0) {
5612            paths::OnPathResponseReceived::OnPath if !self.abandoned_paths.contains(&path_id) => {
5613                let qlog = self.qlog.with_time(now);
5614                self.timers.stop(
5615                    Timer::PerPath(path_id, PathTimer::PathValidationFailed),
5616                    qlog.clone(),
5617                );
5618                let next_challenge = path
5619                    .data
5620                    .earliest_on_path_expiring_challenge()
5621                    .map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
5622                self.timers.set_or_stop(
5623                    Timer::PerPath(path_id, PathTimer::PathChallengeLost),
5624                    next_challenge,
5625                    qlog,
5626                );
5627                let pns = self.spaces[SpaceKind::Data].for_path(path_id);
5628                if !matches!(pns.open_status, OpenStatus::Informed) {
5629                    if is_multipath_negotiated {
5630                        self.events
5631                            .push_back(Event::Path(PathEvent::Established { id: path_id }));
5632                    }
5633                    pns.open_status = OpenStatus::Informed;
5634                    if let Some(observed) = path.data.last_observed_addr_report.as_ref() {
5635                        self.events.push_back(Event::Path(PathEvent::ObservedAddr {
5636                            id: path_id,
5637                            addr: observed.socket_addr(),
5638                        }));
5639                    }
5640                }
5641                if let Some((_, ref mut prev)) = path.prev {
5642                    // If an on-path response was received while there is a
5643                    // previous path from a migration, then the new path is
5644                    // validated and we can stop sending challenges that try to
5645                    // re-validate the previous path.
5646                    prev.reset_on_path_challenges();
5647                }
5648            }
5649            paths::OnPathResponseReceived::OnPath => {
5650                trace!(
5651                    %response,
5652                    "ignoring PATH_RESPONSE received after path is abandoned"
5653                );
5654            }
5655            paths::OnPathResponseReceived::Unknown => {
5656                debug!(%response, "ignoring invalid PATH_RESPONSE");
5657            }
5658            paths::OnPathResponseReceived::Ignored {
5659                sent_on,
5660                current_path,
5661            } => {
5662                debug!(%sent_on, %current_path, %response, "ignoring valid PATH_RESPONSE");
5663            }
5664        }
5665    }
5666
5667    /// Opens any paths that have been successfully NAT traversed.
5668    fn open_nat_traversed_paths(&mut self, now: Instant) {
5669        while let Some(network_path) = self
5670            .n0_nat_traversal
5671            .client_side_mut()
5672            .ok()
5673            .and_then(|s| s.pop_pending_path_open())
5674        {
5675            match self.open_path_ensure(network_path, PathStatus::Backup, now) {
5676                Ok((path_id, already_existed)) => {
5677                    debug!(
5678                        %path_id,
5679                        ?network_path,
5680                        new_path = !already_existed,
5681                        "Opened NAT traversal path",
5682                    );
5683                }
5684                Err(err) => match err {
5685                    PathError::MultipathNotNegotiated
5686                    | PathError::ServerSideNotAllowed
5687                    | PathError::ValidationFailed
5688                    | PathError::InvalidRemoteAddress(_) => {
5689                        error!(
5690                            ?err,
5691                            ?network_path,
5692                            "Failed to open path for successful NAT traversal"
5693                        );
5694                    }
5695                    PathError::MaxPathIdReached | PathError::RemoteCidsExhausted => {
5696                        // Temporary error, put back.
5697                        self.n0_nat_traversal
5698                            .client_side_mut()
5699                            .map(|s| s.push_pending_path_open(network_path))
5700                            .ok();
5701                        debug!(
5702                            ?err,
5703                            ?network_path,
5704                            "Blocked opening NAT traversal path, enqueued"
5705                        );
5706                        return;
5707                    }
5708                },
5709            }
5710        }
5711    }
5712
5713    /// Migrates the 4-tuple of the path.
5714    ///
5715    /// This creates a new [`PathData`] for the migrated path and stores the previous
5716    /// [`PathData`] in [`PathState::prev`].
5717    fn migrate(
5718        &mut self,
5719        path_id: PathId,
5720        now: Instant,
5721        network_path: FourTuple,
5722        observed_addr: Option<ObservedAddr>,
5723    ) {
5724        trace!(
5725            new_4tuple = %network_path,
5726            prev_4tuple = %self.path_data(path_id).network_path,
5727            %path_id,
5728            "migration initiated",
5729        );
5730        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
5731        // TODO(@divma): conditions for path migration in multipath are very specific, check them
5732        // again to prevent path migrations that should actually create a new path
5733
5734        // Reset rtt/congestion state for new path unless it looks like a NAT rebinding.
5735        // Note that the congestion window will not grow until validation terminates. Helps mitigate
5736        // amplification attacks performed by spoofing source addresses.
5737        let prev_pto = self.pto(SpaceKind::Data, path_id);
5738        let path = self.paths.get_mut(&path_id).expect("known path");
5739        let mut new_path_data = if network_path.remote.is_ipv4()
5740            && network_path.remote.ip() == path.data.network_path.remote.ip()
5741        {
5742            PathData::from_previous(network_path, &path.data, self.path_generation_counter, now)
5743        } else {
5744            let peer_max_udp_payload_size =
5745                u16::try_from(self.peer_params.max_udp_payload_size.into_inner())
5746                    .unwrap_or(u16::MAX);
5747            PathData::new(
5748                network_path,
5749                self.allow_mtud,
5750                Some(peer_max_udp_payload_size),
5751                self.path_generation_counter,
5752                now,
5753                &self.config,
5754            )
5755        };
5756        new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone();
5757        if let Some(report) = observed_addr
5758            && let Some(updated) = new_path_data.update_observed_addr_report(report)
5759        {
5760            tracing::info!("adding observed addr event from migration");
5761            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
5762                id: path_id,
5763                addr: updated,
5764            }));
5765        }
5766        new_path_data.pending_challenge = true;
5767        new_path_data.pending.observed_address = self
5768            .config
5769            .address_discovery_role
5770            .should_report(&self.peer_params.address_discovery_role);
5771
5772        let mut prev_path_data = mem::replace(&mut path.data, new_path_data);
5773
5774        // Only store this as previous path if it was validated. For all we know there could
5775        // already be a previous path stored which might have been validated in the past,
5776        // which is more valuable than one that's not yet validated.
5777        //
5778        // With multipath it is possible that there are no remote CIDs for the path ID
5779        // yet. In this case we would never have sent on this path yet and would not be able
5780        // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair
5781        // anyway. So don't store such a path either.
5782        if !prev_path_data.validated
5783            && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active)
5784        {
5785            prev_path_data.pending_challenge = true;
5786            // We haven't updated the remote CID yet, this captures the remote CID we were using on
5787            // the previous path.
5788            path.prev = Some((cid, prev_path_data));
5789        }
5790
5791        // We need to re-assign the correct remote to this path in qlog
5792        self.qlog.emit_tuple_assigned(path_id, network_path, now);
5793
5794        self.timers.set(
5795            Timer::PerPath(path_id, PathTimer::PathValidationFailed),
5796            now + 3 * cmp::max(self.pto(SpaceKind::Data, path_id), prev_pto),
5797            self.qlog.with_time(now),
5798        );
5799    }
5800
5801    /// Handle a change in the local address, i.e. an active migration
5802    ///
5803    /// In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged
5804    /// for a liveness check. This is the behaviour of a path assumed to be recoverable, even if
5805    /// this is not the case.
5806    ///
5807    /// Clients in a connection in which multipath has been negotiated should migrate paths to new
5808    /// [`PathId`]s. For paths that are known to be non-recoverable can be migrated to a new
5809    /// [`PathId`] by closing the current path, and opening a new one to the same remote. Treating
5810    /// paths as non recoverable when necessary accelerates connectivity re-establishment, or might
5811    /// allow it altogether.
5812    ///
5813    /// The optional `hint` allows callers to indicate when paths are non-recoverable and should be
5814    /// migrated to new a [`PathId`].
5815    // NOTE: only clients are allowed to migrate, but generally dealing with RFC9000 migrations is
5816    // lacking <https://github.com/n0-computer/noq/issues/364>
5817    pub fn handle_network_change(&mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant) {
5818        debug!("network changed");
5819        if self.state.is_drained() {
5820            return;
5821        }
5822        if self.highest_space < SpaceKind::Data {
5823            for path in self.paths.values_mut() {
5824                // Clear the local address for it to be obtained from the socket again.
5825                path.data.network_path.local_ip = None;
5826            }
5827
5828            self.update_remote_cid(PathId::ZERO);
5829            self.ping();
5830
5831            return;
5832        }
5833
5834        // Paths that can't recover so a new path should be open instead. If multipath is not
5835        // negotiated, this will be empty.
5836        let mut non_recoverable_paths = Vec::default();
5837        let mut recoverable_paths = Vec::default();
5838        let mut open_paths = 0;
5839
5840        let is_multipath_negotiated = self.is_multipath_negotiated();
5841        let is_client = self.side().is_client();
5842        let immediate_ack_allowed = self.peer_supports_ack_frequency();
5843
5844        for (path_id, path) in self.paths.iter_mut() {
5845            if self.abandoned_paths.contains(path_id) {
5846                continue;
5847            }
5848            open_paths += 1;
5849
5850            // Read the network path BEFORE clearing local_ip, so the hint can
5851            // check which interface the path was using.
5852            let network_path = path.data.network_path;
5853
5854            // Clear the local address for it to be obtained from the socket again. This applies to
5855            // all paths, regardless of being considered recoverable or not
5856            path.data.network_path.local_ip = None;
5857            let remote = network_path.remote;
5858
5859            // Without multipath, the connection tries to recover the single path, whereas with
5860            // multipath, even in a single-path scenario, we attempt to migrate the path to a new
5861            // PathId.
5862            let attempt_to_recover = if is_multipath_negotiated {
5863                // Use the hint to determine if the path can recover. When no hint is
5864                // provided, clients default to non-recoverable (abandon and re-open)
5865                // while servers default to recoverable (attempt in-place recovery).
5866                hint.map(|h| h.is_path_recoverable(*path_id, network_path))
5867                    .unwrap_or(!is_client)
5868            } else {
5869                // In the non multipath case, we try to recover the single active path
5870                true
5871            };
5872
5873            if attempt_to_recover {
5874                recoverable_paths.push((*path_id, remote));
5875            } else {
5876                non_recoverable_paths.push((*path_id, remote, path.data.local_status()))
5877            }
5878        }
5879
5880        /* NON RECOVERABLE PATHS */
5881        // This are handled first, so that in case the treatment intended for these fails, we can
5882        // go the recoverable route instead.
5883
5884        // Decide if we need to close first or open first in the multipath case.
5885        // - Opening first has a higher risk of getting limited by the negotiated MAX_PATH_ID.
5886        // - Closing first risks this being the only open path.
5887        // We prefer closing paths first unless we identify this is the last open path.
5888        let open_first = open_paths == non_recoverable_paths.len();
5889
5890        for (path_id, remote, status) in non_recoverable_paths.into_iter() {
5891            let network_path = FourTuple {
5892                remote,
5893                local_ip: None, /* allow the local ip to be discovered */
5894            };
5895
5896            if open_first && let Err(e) = self.open_path(network_path, status, now) {
5897                if self.side().is_client() {
5898                    debug!(%e, "Failed to open new path for network change");
5899                }
5900                // if this fails, let the path try to recover itself
5901                recoverable_paths.push((path_id, remote));
5902                continue;
5903            }
5904
5905            if let Err(e) =
5906                self.close_path_inner(now, path_id, PathAbandonReason::UnusableAfterNetworkChange)
5907            {
5908                debug!(%e,"Failed to close unrecoverable path after network change");
5909                recoverable_paths.push((path_id, remote));
5910                continue;
5911            }
5912
5913            if !open_first && let Err(e) = self.open_path(network_path, status, now) {
5914                // Path has already been closed if we got here. Since the path was not recoverable,
5915                // this might be desirable in any case, because other paths exist (!open_first) and
5916                // this was is considered non recoverable
5917                debug!(%e,"Failed to open new path for network change");
5918            }
5919        }
5920
5921        /* RECOVERABLE PATHS */
5922
5923        for (path_id, remote) in recoverable_paths.into_iter() {
5924            // Schedule a Ping for a liveness check.
5925            if let Some(path_space) = self.spaces[SpaceId::Data].number_spaces.get_mut(&path_id) {
5926                path_space.pending_ping = true;
5927
5928                if immediate_ack_allowed {
5929                    path_space.pending_immediate_ack = true;
5930                }
5931            }
5932
5933            // Reset PTO backoff so retransmits resume promptly. Congestion controller and
5934            // RTT are intentionally preserved for recoverable paths. We explicitly allow
5935            // this reset also during the handshake, so do not check
5936            // Self::peer_competed_handshake_address_validation.
5937            if let Some(path) = self.paths.get_mut(&path_id) {
5938                path.data.pto_count = 0;
5939            }
5940            self.set_loss_detection_timer(now, path_id);
5941
5942            let Some((reset_token, retired)) =
5943                self.remote_cids.get_mut(&path_id).and_then(CidQueue::next)
5944            else {
5945                continue;
5946            };
5947
5948            // Retire the current remote CID and any CIDs we had to skip.
5949            self.spaces[SpaceId::Data]
5950                .pending
5951                .retire_cids
5952                .extend(retired.map(|seq| (path_id, seq)));
5953
5954            debug_assert!(!self.state.is_drained()); // required for endpoint_events, checked above
5955            self.endpoint_events
5956                .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
5957        }
5958    }
5959
5960    /// Switch to a previously unused remote connection ID, if possible
5961    fn update_remote_cid(&mut self, path_id: PathId) {
5962        let Some((reset_token, retired)) = self
5963            .remote_cids
5964            .get_mut(&path_id)
5965            .and_then(|cids| cids.next())
5966        else {
5967            return;
5968        };
5969
5970        // Retire the current remote CID and any CIDs we had to skip.
5971        self.spaces[SpaceId::Data]
5972            .pending
5973            .retire_cids
5974            .extend(retired.map(|seq| (path_id, seq)));
5975        let remote = self.path_data(path_id).network_path.remote;
5976        self.set_reset_token(path_id, remote, reset_token);
5977    }
5978
5979    /// Sends this reset token to the endpoint
5980    ///
5981    /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer
5982    /// sends a reset token it knows to route it to this connection. See RFC 9000 section
5983    /// 10.3. Stateless Reset.
5984    ///
5985    /// Reset tokens are different for each path, the endpoint identifies paths by peer
5986    /// socket address however, not by path ID.
5987    fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) {
5988        debug_assert!(!self.state.is_drained()); // required for endpoint events, set_reset_token is never called for drained connections
5989        self.endpoint_events
5990            .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
5991
5992        // During the handshake the server sends a reset token in the transport
5993        // parameters. When we are the client and we receive the reset token during the
5994        // handshake we want this to affect our peer transport parameters.
5995        // TODO(flub): Pretty sure this is pointless, the entire params is overwritten
5996        //    shortly after this was called.  And then the params don't have this anymore.
5997        if path_id == PathId::ZERO {
5998            self.peer_params.stateless_reset_token = Some(reset_token);
5999        }
6000    }
6001
6002    /// Issue an initial set of connection IDs to the peer upon connection
6003    fn issue_first_cids(&mut self, now: Instant) {
6004        if self
6005            .local_cid_state
6006            .get(&PathId::ZERO)
6007            .expect("PathId::ZERO exists when the connection is created")
6008            .cid_len()
6009            == 0
6010        {
6011            return;
6012        }
6013
6014        // Subtract 1 to account for the CID we supplied while handshaking
6015        let mut n = self.peer_params.issue_cids_limit() - 1;
6016        if let ConnectionSide::Server { server_config } = &self.side
6017            && server_config.has_preferred_address()
6018        {
6019            // We also sent a CID in the transport parameters
6020            n -= 1;
6021        }
6022        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events
6023        self.endpoint_events
6024            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
6025    }
6026
6027    /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued
6028    ///
6029    /// Later CIDs are issued when CIDs expire or are retired by the peer.
6030    fn issue_first_path_cids(&mut self, now: Instant) {
6031        if let Some(max_path_id) = self.max_path_id() {
6032            let mut path_id = self.max_path_id_with_cids.next();
6033            while path_id <= max_path_id {
6034                self.endpoint_events
6035                    .push_back(EndpointEventInner::NeedIdentifiers(
6036                        path_id,
6037                        now,
6038                        self.peer_params.issue_cids_limit(),
6039                    ));
6040                path_id = path_id.next();
6041            }
6042            self.max_path_id_with_cids = max_path_id;
6043        }
6044    }
6045
6046    /// Populates a packet with frames
6047    ///
6048    /// This tries to fit as many frames as possible into the packet.
6049    ///
6050    /// *path_exclusive_only* means to only build frames which can only be sent on this
6051    /// *path.  This is used in multipath for backup paths while there is still an active
6052    /// *path.
6053    fn populate_packet<'a, 'b>(
6054        &mut self,
6055        now: Instant,
6056        space_id: SpaceId,
6057        path_id: PathId,
6058        scheduling_info: &PathSchedulingInfo,
6059        builder: &mut PacketBuilder<'a, 'b>,
6060    ) {
6061        let is_multipath_negotiated = self.is_multipath_negotiated();
6062        let space_has_keys = self.crypto_state.has_keys(space_id.encryption_level());
6063        let is_0rtt = space_id == SpaceId::Data && !space_has_keys;
6064        let stats = &mut self.path_stats.get_mut(path_id).frame_tx;
6065        let space = &mut self.spaces[space_id];
6066        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
6067        space
6068            .for_path(path_id)
6069            .pending_acks
6070            .maybe_ack_non_eliciting();
6071
6072        // HANDSHAKE_DONE
6073        if !is_0rtt
6074            && !scheduling_info.is_abandoned
6075            && scheduling_info.may_send_data
6076            && mem::replace(&mut space.pending.handshake_done, false)
6077        {
6078            builder.write_frame(frame::HandshakeDone, stats);
6079        }
6080
6081        // PING
6082        if !scheduling_info.is_abandoned
6083            && mem::replace(&mut space.for_path(path_id).pending_ping, false)
6084        {
6085            builder.write_frame(frame::Ping, stats);
6086        }
6087
6088        // IMMEDIATE_ACK
6089        if !scheduling_info.is_abandoned
6090            && mem::replace(&mut space.for_path(path_id).pending_immediate_ack, false)
6091        {
6092            debug_assert_eq!(
6093                space_id,
6094                SpaceId::Data,
6095                "immediate acks must be sent in the data space"
6096            );
6097            builder.write_frame(frame::ImmediateAck, stats);
6098        }
6099
6100        // ACK
6101        if !scheduling_info.is_abandoned && scheduling_info.may_send_data {
6102            for path_id in space
6103                .number_spaces
6104                .iter_mut()
6105                .filter(|(_, pns)| pns.pending_acks.can_send())
6106                .map(|(&path_id, _)| path_id)
6107                .collect::<Vec<_>>()
6108            {
6109                Self::populate_acks(
6110                    now,
6111                    self.receiving_ecn,
6112                    path_id,
6113                    space_id,
6114                    space,
6115                    is_multipath_negotiated,
6116                    builder,
6117                    stats,
6118                    space_has_keys,
6119                );
6120            }
6121        }
6122
6123        // ACK_FREQUENCY
6124        if !scheduling_info.is_abandoned
6125            && scheduling_info.may_send_data
6126            && mem::replace(&mut space.pending.ack_frequency, false)
6127        {
6128            let sequence_number = self.ack_frequency.next_sequence_number();
6129
6130            // Safe to unwrap because this is always provided when ACK frequency is enabled
6131            let config = self.config.ack_frequency_config.as_ref().unwrap();
6132
6133            // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error
6134            let max_ack_delay = self.ack_frequency.candidate_max_ack_delay(
6135                path.rtt.get(),
6136                config,
6137                &self.peer_params,
6138            );
6139
6140            let frame = frame::AckFrequency {
6141                sequence: sequence_number,
6142                ack_eliciting_threshold: config.ack_eliciting_threshold,
6143                request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX),
6144                reordering_threshold: config.reordering_threshold,
6145            };
6146            builder.write_frame(frame, stats);
6147
6148            self.ack_frequency
6149                .ack_frequency_sent(path_id, builder.packet_number, max_ack_delay);
6150        }
6151
6152        // PATH_CHALLENGE (on-path)
6153        if !scheduling_info.is_abandoned
6154            && space_id == SpaceId::Data
6155            && path.pending_challenge
6156            // we don't want to send new challenges if we are already closing
6157            && !self.state.is_closed()
6158            && builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND
6159            // An on-path PATH_CHALLENGE must be part of datagrams expanded to the
6160            // MIN_INITIAL_SIZE (1200 bytes).
6161            && builder.buf.segment_size() >= usize::from(MIN_INITIAL_SIZE)
6162        {
6163            path.pending_challenge = false;
6164
6165            let token = self.rng.random();
6166            path.record_path_challenge_sent(now, token, path.network_path);
6167            // Generate a new challenge every time we send a new PATH_CHALLENGE
6168            let challenge = frame::PathChallenge(token);
6169            builder.write_frame(challenge, stats);
6170            builder.require_padding();
6171
6172            // On-path challenges need a PATH_RESPONSE and not only an ACK which can be
6173            // received on any path. So set a timer manually instead of relying on the usual
6174            // LossDetection/PTO timer. This timer interval keeps exponentially increasing
6175            // with missing responses like the normal PTO interval.
6176            self.timers.set(
6177                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
6178                now + path.on_path_challenge_pto(),
6179                self.qlog.with_time(now),
6180            );
6181
6182            if is_multipath_negotiated && !path.validated && path.pending_challenge {
6183                // queue informing the path status along with the challenge
6184                space.pending.path_status.insert(path_id);
6185            }
6186
6187            // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
6188            // of whether one has already been sent on this path.
6189            path.pending.observed_address = self
6190                .config
6191                .address_discovery_role
6192                .should_report(&self.peer_params.address_discovery_role);
6193        }
6194
6195        // PATH_RESPONSE (on-path)
6196        if !scheduling_info.is_abandoned
6197            && space_id == SpaceId::Data
6198            && builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND
6199            // An on-path PATH_RESPONSE must be part of datagrams expanded to the
6200            // MIN_INITIAL_SIZE (1200 bytes).
6201            && builder.buf.segment_size() >= usize::from(MIN_INITIAL_SIZE)
6202            && let Some(token) = space.for_path(path_id).pending_path_responses.pop_on_path(path.network_path)
6203        {
6204            let response = frame::PathResponse(token);
6205            builder.write_frame(response, stats);
6206            builder.require_padding();
6207
6208            // NOTE: this is technically not required but might be useful to ride the
6209            // request/response nature of path challenges to refresh an observation
6210            // Since PATH_RESPONSE is a probing frame, this is allowed by the spec.
6211            path.pending.observed_address = self
6212                .config
6213                .address_discovery_role
6214                .should_report(&self.peer_params.address_discovery_role);
6215        }
6216
6217        // ADD_ADDRESS
6218        while space_id == SpaceId::Data
6219            && !scheduling_info.is_abandoned
6220            && scheduling_info.may_send_data
6221            && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining()
6222        {
6223            if let Some(added_address) = space.pending.add_address.pop_last() {
6224                builder.write_frame(added_address, stats);
6225            } else {
6226                break;
6227            }
6228        }
6229
6230        // REMOVE_ADDRESS
6231        while space_id == SpaceId::Data
6232            && !scheduling_info.is_abandoned
6233            && scheduling_info.may_send_data
6234            && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining()
6235        {
6236            if let Some(removed_address) = space.pending.remove_address.pop_last() {
6237                builder.write_frame(removed_address, stats);
6238            } else {
6239                break;
6240            }
6241        }
6242
6243        // REACH_OUT
6244        while !scheduling_info.is_abandoned
6245            && scheduling_info.may_send_data
6246            && let Some(reach_out) = space
6247                .pending
6248                .reach_out
6249                .pop_if(|frame| builder.frame_space_remaining() >= frame.size())
6250        {
6251            builder.write_frame(reach_out, stats);
6252        }
6253
6254        // PATH_ABANDON
6255        if space_id == SpaceId::Data
6256            && scheduling_info.is_abandoned
6257            && scheduling_info.may_self_abandon
6258            && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining()
6259            && let Some(error_code) = space.pending.path_abandon.remove(&path_id)
6260        {
6261            let frame = frame::PathAbandon {
6262                path_id,
6263                error_code,
6264            };
6265            builder.write_frame(frame, stats);
6266
6267            // Consider remotely issued CIDs as retired now that we have sent this frame at
6268            // least once.
6269            self.remote_cids.remove(&path_id);
6270        }
6271        while space_id == SpaceId::Data
6272            && scheduling_info.may_send_data
6273            && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining()
6274            && let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first()
6275        {
6276            let frame = frame::PathAbandon {
6277                path_id: abandoned_path_id,
6278                error_code,
6279            };
6280            builder.write_frame(frame, stats);
6281
6282            // Consider remotely issued CIDs as retired now that we have sent this frame at
6283            // least once.
6284            self.remote_cids.remove(&abandoned_path_id);
6285        }
6286
6287        // OBSERVED_ADDR
6288        if !scheduling_info.is_abandoned
6289            && space_id == SpaceId::Data
6290            && path.pending.observed_address
6291        {
6292            let frame = ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no);
6293            if builder.frame_space_remaining() > frame.size() {
6294                builder.write_frame(frame, stats);
6295
6296                self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8);
6297                path.pending.observed_address = false;
6298            }
6299        }
6300
6301        // CRYPTO
6302        while !is_0rtt
6303            && !scheduling_info.is_abandoned
6304            && scheduling_info.may_send_data
6305            && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND
6306        {
6307            let Some(mut frame) = space.pending.crypto.pop_front() else {
6308                break;
6309            };
6310
6311            // Calculate the maximum amount of crypto data we can store in the buffer.
6312            // Since the offset is known, we can reserve the exact size required to encode it.
6313            // For length we reserve 2bytes which allows to encode up to 2^14,
6314            // which is more than what fits into normally sized QUIC frames.
6315            let max_crypto_data_size = builder.frame_space_remaining()
6316                - 1 // Frame Type
6317                - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) })
6318                - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes
6319
6320            let len = frame
6321                .data
6322                .len()
6323                .min(2usize.pow(14) - 1)
6324                .min(max_crypto_data_size);
6325
6326            let data = frame.data.split_to(len);
6327            let offset = frame.offset;
6328            let truncated = frame::Crypto { offset, data };
6329            builder.write_frame(truncated, stats);
6330
6331            if !frame.data.is_empty() {
6332                frame.offset += len as u64;
6333                space.pending.crypto.push_front(frame);
6334            }
6335        }
6336
6337        // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP
6338        while space_id == SpaceId::Data
6339            && !scheduling_info.is_abandoned
6340            && scheduling_info.may_send_data
6341            && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining()
6342        {
6343            let Some(path_id) = space.pending.path_status.pop_first() else {
6344                break;
6345            };
6346            let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else {
6347                trace!(%path_id, "discarding queued path status for unknown path");
6348                continue;
6349            };
6350
6351            let seq = path.status.seq();
6352            match path.local_status() {
6353                PathStatus::Available => {
6354                    let frame = frame::PathStatusAvailable {
6355                        path_id,
6356                        status_seq_no: seq,
6357                    };
6358                    builder.write_frame(frame, stats);
6359                }
6360                PathStatus::Backup => {
6361                    let frame = frame::PathStatusBackup {
6362                        path_id,
6363                        status_seq_no: seq,
6364                    };
6365                    builder.write_frame(frame, stats);
6366                }
6367            }
6368        }
6369
6370        // MAX_PATH_ID
6371        if space_id == SpaceId::Data
6372            && !scheduling_info.is_abandoned
6373            && scheduling_info.may_send_data
6374            && space.pending.max_path_id
6375            && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining()
6376        {
6377            let frame = frame::MaxPathId(self.local_max_path_id);
6378            builder.write_frame(frame, stats);
6379            space.pending.max_path_id = false;
6380        }
6381
6382        // PATHS_BLOCKED
6383        if space_id == SpaceId::Data
6384            && !scheduling_info.is_abandoned
6385            && scheduling_info.may_send_data
6386            && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
6387            && let Some(remote_max_path_id) = space.pending.paths_blocked.take()
6388        {
6389            let frame = frame::PathsBlocked(remote_max_path_id);
6390            builder.write_frame(frame, stats);
6391        }
6392
6393        // PATH_CIDS_BLOCKED
6394        while space_id == SpaceId::Data
6395            && !scheduling_info.is_abandoned
6396            && scheduling_info.may_send_data
6397            && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
6398        {
6399            let Some((path_id, next_seq)) = space.pending.path_cids_blocked.pop_first() else {
6400                break;
6401            };
6402            let frame = frame::PathCidsBlocked { path_id, next_seq };
6403            builder.write_frame(frame, stats);
6404        }
6405
6406        // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS
6407        if space_id == SpaceId::Data
6408            && !scheduling_info.is_abandoned
6409            && scheduling_info.may_send_data
6410        {
6411            self.streams
6412                .write_control_frames(builder, &mut space.pending, stats);
6413        }
6414
6415        // NEW_CONNECTION_ID
6416        let cid_len = self
6417            .local_cid_state
6418            .values()
6419            .map(|cid_state| cid_state.cid_len())
6420            .max()
6421            .expect("some local CID state must exist");
6422        let new_cid_size_bound =
6423            frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len);
6424        while !scheduling_info.is_abandoned
6425            && scheduling_info.may_send_data
6426            && builder.frame_space_remaining() > new_cid_size_bound
6427        {
6428            let Some(issued) = space.pending.new_cids.pop() else {
6429                break;
6430            };
6431            // Path was discarded after this CID was queued, drop.
6432            let Some(cid_state) = self.local_cid_state.get(&issued.path_id) else {
6433                debug!(
6434                    path = %issued.path_id, seq = issued.sequence,
6435                    "dropping queued NEW_CONNECTION_ID for discarded path",
6436                );
6437                continue;
6438            };
6439            let retire_prior_to = cid_state.retire_prior_to();
6440
6441            let cid_path_id = match is_multipath_negotiated {
6442                true => Some(issued.path_id),
6443                false => {
6444                    debug_assert_eq!(issued.path_id, PathId::ZERO);
6445                    None
6446                }
6447            };
6448            let frame = frame::NewConnectionId {
6449                path_id: cid_path_id,
6450                sequence: issued.sequence,
6451                retire_prior_to,
6452                id: issued.id,
6453                reset_token: issued.reset_token,
6454            };
6455            builder.write_frame(frame, stats);
6456        }
6457
6458        // RETIRE_CONNECTION_ID
6459        let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated);
6460        while !scheduling_info.is_abandoned
6461            && scheduling_info.may_send_data
6462            && builder.frame_space_remaining() > retire_cid_bound
6463        {
6464            let (path_id, sequence) = match space.pending.retire_cids.pop() {
6465                Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq),
6466                Some((path_id, seq)) => (Some(path_id), seq),
6467                None => break,
6468            };
6469            let frame = frame::RetireConnectionId { path_id, sequence };
6470            builder.write_frame(frame, stats);
6471        }
6472
6473        // DATAGRAM
6474        let mut sent_datagrams = false;
6475        while !scheduling_info.is_abandoned
6476            && scheduling_info.may_send_data
6477            && builder.frame_space_remaining() > Datagram::SIZE_BOUND
6478            && space_id == SpaceId::Data
6479        {
6480            match self.datagrams.write(builder, stats) {
6481                true => {
6482                    sent_datagrams = true;
6483                }
6484                false => break,
6485            }
6486        }
6487        if self.datagrams.send_blocked && sent_datagrams {
6488            self.events.push_back(Event::DatagramsUnblocked);
6489            self.datagrams.send_blocked = false;
6490        }
6491
6492        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
6493
6494        // NEW_TOKEN
6495        if !scheduling_info.is_abandoned && scheduling_info.may_send_data {
6496            while let Some(network_path) = space.pending.new_tokens.pop() {
6497                debug_assert_eq!(space_id, SpaceId::Data);
6498                let ConnectionSide::Server { server_config } = &self.side else {
6499                    panic!("NEW_TOKEN frames should not be enqueued by clients");
6500                };
6501
6502                if !network_path.is_probably_same_path(&path.network_path) {
6503                    // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only
6504                    // useful if used from the same IP address.  Thus, we abandon enqueued NEW_TOKEN
6505                    // frames upon an path change. Instead, when the new path becomes validated,
6506                    // NEW_TOKEN frames may be enqueued for the new path instead.
6507                    continue;
6508                }
6509
6510                let token = Token::new(
6511                    TokenPayload::Validation {
6512                        ip: network_path.remote.ip(),
6513                        issued: server_config.time_source.now(),
6514                    },
6515                    &mut self.rng,
6516                );
6517                let new_token = NewToken {
6518                    token: token.encode(&*server_config.token_key).into(),
6519                };
6520
6521                if builder.frame_space_remaining() < new_token.size() {
6522                    space.pending.new_tokens.push(network_path);
6523                    break;
6524                }
6525
6526                builder.write_frame(new_token, stats);
6527                builder.retransmits_mut().new_tokens.push(network_path);
6528            }
6529        }
6530
6531        // STREAM
6532        if !scheduling_info.is_abandoned
6533            && scheduling_info.may_send_data
6534            && space_id == SpaceId::Data
6535        {
6536            self.streams
6537                .write_stream_frames(builder, self.config.send_fairness, stats);
6538        }
6539    }
6540
6541    /// Write pending ACKs into a buffer
6542    fn populate_acks<'a, 'b>(
6543        now: Instant,
6544        receiving_ecn: bool,
6545        path_id: PathId,
6546        space_id: SpaceId,
6547        space: &mut PacketSpace,
6548        is_multipath_negotiated: bool,
6549        builder: &mut PacketBuilder<'a, 'b>,
6550        stats: &mut FrameStats,
6551        space_has_keys: bool,
6552    ) {
6553        // 0-RTT packets must never carry acks (which would have to be of handshake packets)
6554        debug_assert!(space_has_keys, "tried to send ACK in 0-RTT");
6555
6556        debug_assert!(
6557            is_multipath_negotiated || path_id == PathId::ZERO,
6558            "Only PathId::ZERO allowed without multipath (have {path_id:?})"
6559        );
6560        if is_multipath_negotiated {
6561            debug_assert!(
6562                space_id == SpaceId::Data || path_id == PathId::ZERO,
6563                "path acks must be sent in 1RTT space (have {space_id:?})"
6564            );
6565        }
6566
6567        let pns = space.for_path(path_id);
6568        let ranges = pns.pending_acks.ranges();
6569        debug_assert!(!ranges.is_empty(), "can not send empty ACK range");
6570        let ecn = if receiving_ecn {
6571            Some(&pns.ecn_counters)
6572        } else {
6573            None
6574        };
6575
6576        let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64;
6577        // TODO: This should come from `TransportConfig` if that gets configurable.
6578        let ack_delay_exp = TransportParameters::default().ack_delay_exponent;
6579        let delay = delay_micros >> ack_delay_exp.into_inner();
6580
6581        if is_multipath_negotiated && space_id == SpaceId::Data {
6582            if !ranges.is_empty() {
6583                let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn);
6584                builder.write_frame(frame, stats);
6585            }
6586        } else {
6587            builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats);
6588        }
6589    }
6590
6591    fn close_common(&mut self) {
6592        trace!("connection closed");
6593        self.timers.reset();
6594    }
6595
6596    fn set_close_timer(&mut self, now: Instant) {
6597        // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of
6598        // all paths.
6599        let pto_max = self.max_pto_for_space(self.highest_space);
6600        self.timers.set(
6601            Timer::Conn(ConnTimer::Close),
6602            now + 3 * pto_max,
6603            self.qlog.with_time(now),
6604        );
6605    }
6606
6607    /// Handle transport parameters received from the peer
6608    ///
6609    /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the
6610    /// *packet into which the transport parameters arrived.
6611    fn handle_peer_params(
6612        &mut self,
6613        params: TransportParameters,
6614        local_cid: ConnectionId,
6615        remote_cid: ConnectionId,
6616        now: Instant,
6617    ) -> Result<(), TransportError> {
6618        if Some(self.original_remote_cid) != params.initial_src_cid
6619            || (self.side.is_client()
6620                && (Some(self.initial_dst_cid) != params.original_dst_cid
6621                    || self.retry_src_cid != params.retry_src_cid))
6622        {
6623            return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
6624                "CID authentication failure",
6625            ));
6626        }
6627        if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) {
6628            return Err(TransportError::PROTOCOL_VIOLATION(
6629                "multipath must not use zero-length CIDs",
6630            ));
6631        }
6632
6633        self.set_peer_params(params);
6634        self.qlog.emit_peer_transport_params_received(self, now);
6635
6636        Ok(())
6637    }
6638
6639    fn set_peer_params(&mut self, params: TransportParameters) {
6640        self.streams.set_params(&params);
6641        self.idle_timeout =
6642            negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout));
6643        trace!("negotiated max idle timeout {:?}", self.idle_timeout);
6644
6645        if let Some(ref info) = params.preferred_address {
6646            // During the handshake PathId::ZERO exists.
6647            self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId {
6648                path_id: None,
6649                sequence: 1,
6650                id: info.connection_id,
6651                reset_token: info.stateless_reset_token,
6652                retire_prior_to: 0,
6653            })
6654            .expect(
6655                "preferred address CID is the first received, and hence is guaranteed to be legal",
6656            );
6657            let remote = self.path_data(PathId::ZERO).network_path.remote;
6658            self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token);
6659        }
6660        self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params);
6661
6662        let mut multipath_enabled = false;
6663        if let (Some(local_max_path_id), Some(remote_max_path_id)) = (
6664            self.config.get_initial_max_path_id(),
6665            params.initial_max_path_id,
6666        ) {
6667            // multipath is enabled, register the local and remote maximums
6668            self.local_max_path_id = local_max_path_id;
6669            self.remote_max_path_id = remote_max_path_id;
6670            let initial_max_path_id = local_max_path_id.min(remote_max_path_id);
6671            debug!(%initial_max_path_id, "multipath negotiated");
6672            multipath_enabled = true;
6673        }
6674
6675        if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) =
6676            self.config
6677                .max_remote_nat_traversal_addresses
6678                .zip(params.max_remote_nat_traversal_addresses)
6679        {
6680            if multipath_enabled {
6681                let max_local_addresses = max_remotely_allowed_remote_addresses.get();
6682                let max_remote_addresses = max_locally_allowed_remote_addresses.get();
6683                self.n0_nat_traversal = n0_nat_traversal::State::new(
6684                    max_remote_addresses,
6685                    max_local_addresses,
6686                    self.side(),
6687                );
6688                debug!(
6689                    %max_remote_addresses, %max_local_addresses,
6690                    "n0's nat traversal negotiated"
6691                );
6692            } else {
6693                debug!("n0 nat traversal enabled for both endpoints, but multipath is missing")
6694            }
6695        }
6696
6697        self.peer_params = params;
6698        let peer_max_udp_payload_size =
6699            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
6700        let address_discovery_negotiated = self
6701            .config
6702            .address_discovery_role
6703            .should_report(&self.peer_params.address_discovery_role);
6704
6705        let path = self.path_data_mut(PathId::ZERO);
6706        path.pending.observed_address = address_discovery_negotiated;
6707        path.mtud
6708            .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
6709    }
6710
6711    /// Decrypts a packet, returning the packet number on success
6712    fn decrypt_packet(
6713        &mut self,
6714        now: Instant,
6715        path_id: PathId,
6716        packet: &mut Packet,
6717    ) -> Result<Option<u64>, Option<TransportError>> {
6718        let result = self
6719            .crypto_state
6720            .decrypt_packet_body(packet, path_id, &self.spaces)?;
6721
6722        let Some(result) = result else {
6723            return Ok(None);
6724        };
6725
6726        if result.outgoing_key_update_acked
6727            && let Some(prev) = self.crypto_state.prev_crypto.as_mut()
6728        {
6729            prev.end_packet = Some((result.packet_number, now));
6730            self.set_key_discard_timer(now, packet.header.space());
6731        }
6732
6733        if result.incoming_key_update {
6734            trace!("key update authenticated");
6735            self.crypto_state
6736                .update_keys(Some((result.packet_number, now)), true);
6737            self.set_key_discard_timer(now, packet.header.space());
6738        }
6739
6740        Ok(Some(result.packet_number))
6741    }
6742
6743    fn peer_supports_ack_frequency(&self) -> bool {
6744        self.peer_params.min_ack_delay.is_some()
6745    }
6746
6747    /// Send an IMMEDIATE_ACK frame to the remote endpoint
6748    ///
6749    /// According to the spec, this will result in an error if the remote endpoint does not support
6750    /// the Acknowledgement Frequency extension
6751    pub(crate) fn immediate_ack(&mut self, path_id: PathId) {
6752        debug_assert_eq!(
6753            self.highest_space,
6754            SpaceKind::Data,
6755            "immediate ack must be written in the data space"
6756        );
6757        self.spaces[SpaceId::Data]
6758            .for_path(path_id)
6759            .pending_immediate_ack = true;
6760    }
6761
6762    /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests
6763    #[cfg(test)]
6764    pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> {
6765        let ConnectionEventInner::Datagram(DatagramConnectionEvent {
6766            path_id,
6767            first_decode,
6768            remaining,
6769            ..
6770        }) = &event.0
6771        else {
6772            return None;
6773        };
6774
6775        if remaining.is_some() {
6776            panic!("Packets should never be coalesced in tests");
6777        }
6778
6779        let decrypted_header = self
6780            .crypto_state
6781            .unprotect_header(first_decode.clone(), self.peer_params.stateless_reset_token)?;
6782
6783        let mut packet = decrypted_header.packet?;
6784        self.crypto_state
6785            .decrypt_packet_body(&mut packet, *path_id, &self.spaces)
6786            .ok()?;
6787
6788        Some(packet.payload.to_vec())
6789    }
6790
6791    /// The number of bytes of packets containing retransmittable frames that have not been
6792    /// acknowledged or declared lost.
6793    #[cfg(test)]
6794    pub(crate) fn bytes_in_flight(&self) -> u64 {
6795        // TODO(@divma): consider including for multipath?
6796        self.path_data(PathId::ZERO).in_flight.bytes
6797    }
6798
6799    /// Number of bytes worth of non-ack-only packets that may be sent
6800    #[cfg(test)]
6801    pub(crate) fn congestion_window(&self) -> u64 {
6802        let path = self.path_data(PathId::ZERO);
6803        path.congestion
6804            .window()
6805            .saturating_sub(path.in_flight.bytes)
6806    }
6807
6808    /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running
6809    #[cfg(test)]
6810    pub(crate) fn is_idle(&self) -> bool {
6811        let current_timers = self.timers.values();
6812        current_timers
6813            .into_iter()
6814            .filter(|(timer, _)| {
6815                !matches!(
6816                    timer,
6817                    Timer::Conn(ConnTimer::KeepAlive)
6818                        | Timer::PerPath(_, PathTimer::PathKeepAlive)
6819                        | Timer::Conn(ConnTimer::PushNewCid)
6820                        | Timer::Conn(ConnTimer::KeyDiscard)
6821                )
6822            })
6823            .min_by_key(|(_, time)| *time)
6824            .is_none_or(|(timer, _)| {
6825                matches!(
6826                    timer,
6827                    Timer::Conn(ConnTimer::Idle) | Timer::PerPath(_, PathTimer::PathIdle)
6828                )
6829            })
6830    }
6831
6832    /// Whether explicit congestion notification is in use on outgoing packets.
6833    #[cfg(test)]
6834    pub(crate) fn using_ecn(&self) -> bool {
6835        self.path_data(PathId::ZERO).sending_ecn
6836    }
6837
6838    /// The number of received bytes in the current path
6839    #[cfg(test)]
6840    pub(crate) fn total_recvd(&self) -> u64 {
6841        self.path_data(PathId::ZERO).total_recvd
6842    }
6843
6844    #[cfg(test)]
6845    pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) {
6846        self.local_cid_state
6847            .get(&PathId::ZERO)
6848            .unwrap()
6849            .active_seq()
6850    }
6851
6852    #[cfg(test)]
6853    #[track_caller]
6854    pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) {
6855        self.local_cid_state
6856            .get(&PathId(path_id))
6857            .unwrap()
6858            .active_seq()
6859    }
6860
6861    /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame
6862    /// with updated `retire_prior_to` field set to `v`
6863    #[cfg(test)]
6864    pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) {
6865        let n = self
6866            .local_cid_state
6867            .get_mut(&PathId::ZERO)
6868            .unwrap()
6869            .assign_retire_seq(v);
6870        debug_assert!(!self.state.is_drained()); // requirement for endpoint_events
6871        self.endpoint_events
6872            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
6873    }
6874
6875    /// Check the current active remote CID sequence for `PathId::ZERO`
6876    #[cfg(test)]
6877    pub(crate) fn active_remote_cid_seq(&self) -> u64 {
6878        self.remote_cids.get(&PathId::ZERO).unwrap().active_seq()
6879    }
6880
6881    /// Returns the detected maximum udp payload size for the current path
6882    #[cfg(test)]
6883    pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 {
6884        self.path_data(path_id).current_mtu()
6885    }
6886
6887    /// Triggers path validation on all paths
6888    #[cfg(test)]
6889    pub(crate) fn trigger_path_validation(&mut self) {
6890        for path in self.paths.values_mut() {
6891            path.data.pending_challenge = true;
6892        }
6893    }
6894
6895    /// Simulates a protocol violation error for test purposes.
6896    #[cfg(test)]
6897    pub fn simulate_protocol_violation(&mut self, now: Instant) {
6898        if !self.state.is_closed() {
6899            self.state
6900                .move_to_closed(TransportError::PROTOCOL_VIOLATION("simulated violation"));
6901            self.close_common();
6902            if !self.state.is_drained() {
6903                self.set_close_timer(now);
6904            }
6905            self.connection_close_pending = true;
6906        }
6907    }
6908
6909    /// Whether we have **on-path** 1-RTT data to send.
6910    ///
6911    /// This checks for frames that can only be sent in the data space (1-RTT):
6912    /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated.
6913    /// - Pending PATH_RESPONSE frames.
6914    /// - Pending data to send in STREAM frames.
6915    /// - Pending DATAGRAM frames to send.
6916    ///
6917    /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that
6918    /// may need to be sent.
6919    fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames {
6920        let network_path = self.path_data(path_id).network_path;
6921        let space_specific = self
6922            .paths
6923            .get(&path_id)
6924            .is_some_and(|path| path.data.pending_challenge || !path.data.pending.is_empty())
6925            || self.spaces[SpaceKind::Data]
6926                .number_spaces
6927                .get(&path_id)
6928                .is_some_and(|pns| pns.pending_path_responses.has_pending_on_path(network_path));
6929
6930        // Stream control frames are checked in PacketSpace::can_send, only check data here.
6931        let other = self.streams.can_send_stream_data()
6932            || self
6933                .datagrams
6934                .outgoing
6935                .front()
6936                .is_some_and(|x| x.size(true) <= max_size);
6937
6938        // All `false` fields are set in PacketSpace::can_send.
6939        SendableFrames {
6940            acks: false,
6941            close: false,
6942            space_specific,
6943            other,
6944        }
6945    }
6946
6947    /// Terminate the connection instantly, without sending a close packet
6948    fn kill(&mut self, reason: ConnectionError) {
6949        self.close_common();
6950        self.state
6951            .move_to_drained(Some(reason), &mut self.endpoint_events);
6952    }
6953
6954    /// Storage size required for the largest packet that can be transmitted on all currently
6955    /// available paths
6956    ///
6957    /// Buffers passed to [`Connection::poll_transmit`] should be at least this large.
6958    ///
6959    /// When multipath is enabled, this value is the minimum MTU across all available paths.
6960    pub fn current_mtu(&self) -> u16 {
6961        self.paths
6962            .iter()
6963            .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id))
6964            .map(|(_path_id, path_state)| path_state.data.current_mtu())
6965            .min()
6966            .unwrap_or(INITIAL_MTU)
6967    }
6968
6969    /// Size of non-frame data for a 1-RTT packet
6970    ///
6971    /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are
6972    /// frames. Changes if the length of the remote connection ID changes, which is expected to be
6973    /// rare. If `pn` is specified, may additionally change unpredictably due to variations in
6974    /// latency and packet loss.
6975    fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize {
6976        let pn_len = PacketNumber::new(
6977            pn,
6978            self.spaces[SpaceId::Data]
6979                .for_path(path)
6980                .largest_acked_packet_pn
6981                .unwrap_or(0),
6982        )
6983        .len();
6984
6985        // 1 byte for flags
6986        1 + self
6987            .remote_cids
6988            .get(&path)
6989            .map(|cids| cids.active().len())
6990            .unwrap_or(20)      // Max CID len in QUIC v1
6991            + pn_len
6992            + self.tag_len_1rtt()
6993    }
6994
6995    fn predict_1rtt_overhead_no_pn(&self) -> usize {
6996        let pn_len = 4;
6997
6998        let cid_len = self
6999            .remote_cids
7000            .values()
7001            .map(|cids| cids.active().len())
7002            .max()
7003            .unwrap_or(20); // Max CID len in QUIC v1
7004
7005        // 1 byte for flags
7006        1 + cid_len + pn_len + self.tag_len_1rtt()
7007    }
7008
7009    fn tag_len_1rtt(&self) -> usize {
7010        // encryption_keys for Data space returns 1-RTT keys if available, otherwise 0-RTT keys
7011        let packet_crypto = self
7012            .crypto_state
7013            .encryption_keys(SpaceKind::Data, self.side.side())
7014            .map(|(_header, packet, _level)| packet);
7015        // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of
7016        // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead,
7017        // but that would needlessly prevent sending datagrams during 0-RTT.
7018        packet_crypto.map_or(16, |x| x.tag_len())
7019    }
7020
7021    /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate
7022    fn on_path_validated(&mut self, path_id: PathId) {
7023        self.path_data_mut(path_id).validated = true;
7024        let ConnectionSide::Server { server_config } = &self.side else {
7025            return;
7026        };
7027        let network_path = self.path_data(path_id).network_path;
7028        let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens;
7029        new_tokens.clear();
7030        for _ in 0..server_config.validation_token.sent {
7031            new_tokens.push(network_path);
7032        }
7033    }
7034
7035    /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP
7036    fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) {
7037        if let Some(path) = self.paths.get_mut(&path_id) {
7038            path.data.status.remote_update(status, status_seq_no);
7039        } else {
7040            debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id);
7041        }
7042        self.events.push_back(
7043            PathEvent::RemoteStatus {
7044                id: path_id,
7045                status,
7046            }
7047            .into(),
7048        );
7049    }
7050
7051    /// Returns the maximum [`PathId`] to be used for sending in this connection.
7052    ///
7053    /// This is calculated as minimum between the local and remote's maximums when multipath is
7054    /// enabled, or `None` when disabled.
7055    ///
7056    /// For data that's received, we should use [`Self::local_max_path_id`] instead.
7057    /// The reasoning is that the remote might already have updated to its own newer
7058    /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered.
7059    fn max_path_id(&self) -> Option<PathId> {
7060        if self.is_multipath_negotiated() {
7061            Some(self.remote_max_path_id.min(self.local_max_path_id))
7062        } else {
7063            None
7064        }
7065    }
7066
7067    /// Returns whether this connection has a socket that supports IPv6.
7068    ///
7069    /// TODO(matheus23): This is related to noq endpoint state's `ipv6` bool. We should move that
7070    /// info here instead of trying to hack around not knowing it exactly.
7071    pub(crate) fn is_ipv6(&self) -> bool {
7072        self.paths
7073            .values()
7074            .any(|p| p.data.network_path.remote.is_ipv6())
7075    }
7076
7077    /// Add addresses the local endpoint considers are reachable for nat traversal.
7078    pub fn add_nat_traversal_address(
7079        &mut self,
7080        address: SocketAddr,
7081    ) -> Result<(), n0_nat_traversal::Error> {
7082        if let Some(added) = self.n0_nat_traversal.add_local_address(address)? {
7083            self.spaces[SpaceId::Data].pending.add_address.insert(added);
7084        };
7085        Ok(())
7086    }
7087
7088    /// Removes an address the endpoing no longer considers reachable for nat traversal
7089    ///
7090    /// Addresses not present in the set will be silently ignored.
7091    pub fn remove_nat_traversal_address(
7092        &mut self,
7093        address: SocketAddr,
7094    ) -> Result<(), n0_nat_traversal::Error> {
7095        if let Some(removed) = self.n0_nat_traversal.remove_local_address(address)? {
7096            self.spaces[SpaceId::Data]
7097                .pending
7098                .remove_address
7099                .insert(removed);
7100        }
7101        Ok(())
7102    }
7103
7104    /// Get the current local nat traversal addresses
7105    pub fn get_local_nat_traversal_addresses(
7106        &self,
7107    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
7108        self.n0_nat_traversal.get_local_nat_traversal_addresses()
7109    }
7110
7111    /// Get the currently advertised nat traversal addresses by the server
7112    pub fn get_remote_nat_traversal_addresses(
7113        &self,
7114    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
7115        Ok(self
7116            .n0_nat_traversal
7117            .client_side()?
7118            .get_remote_nat_traversal_addresses())
7119    }
7120
7121    /// Initiates a new nat traversal round
7122    ///
7123    /// A nat traversal round involves advertising the client's local addresses in
7124    /// `REACH_OUT` frames, and initiating probing of the known remote addresses. When a new
7125    /// round is initiated, the previous one is cancelled.
7126    ///
7127    /// For all probes that succeed, if any, a new path will be opened on the successful
7128    /// 4-tuple.
7129    ///
7130    /// Returns the server addresses that are now being probed. If addresses fail due to
7131    /// spurious errors, these might succeed later and not be returned in this set.
7132    pub fn initiate_nat_traversal_round(
7133        &mut self,
7134        now: Instant,
7135    ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> {
7136        if self.state.is_closed() {
7137            return Err(n0_nat_traversal::Error::Closed);
7138        }
7139
7140        let ipv6 = self.is_ipv6();
7141        let client_state = self.n0_nat_traversal.client_side_mut()?;
7142        let (mut reach_out_frames, probed_addrs) =
7143            client_state.initiate_nat_traversal_round(ipv6)?;
7144        if let Some(delay) = self.n0_nat_traversal.retry_delay(self.config.initial_rtt) {
7145            self.timers.set(
7146                Timer::Conn(ConnTimer::NatTraversalProbeRetry),
7147                now + delay,
7148                self.qlog.with_time(now),
7149            );
7150        }
7151
7152        self.spaces[SpaceId::Data]
7153            .pending
7154            .reach_out
7155            .append(&mut reach_out_frames);
7156
7157        Ok(probed_addrs)
7158    }
7159
7160    /// Whether the handshake is considered **confirmed**.
7161    ///
7162    /// <https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2> defines a handshake to be
7163    /// confirmed when you know the peer successfully received and successfully processed
7164    /// your TLS Finished message.
7165    ///
7166    /// Implementation-wise this is the point at which the handshake crypto keys are
7167    /// discarded. So we can use this to know if the handshake is confirmed.
7168    fn is_handshake_confirmed(&self) -> bool {
7169        !self.is_handshaking() && !self.crypto_state.has_keys(EncryptionLevel::Handshake)
7170    }
7171}
7172
7173impl fmt::Debug for Connection {
7174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7175        f.debug_struct("Connection")
7176            .field("handshake_cid", &self.handshake_cid)
7177            .finish()
7178    }
7179}
7180
7181/// The set of abandoned paths.
7182///
7183/// Implementation based on ArrayRangeSet to share more code. The memory space is
7184/// proportional to the number of concurrently open paths allowed. So does not grow
7185/// unbounded.
7186#[derive(Debug, Default)]
7187struct AbandonedPaths(ArrayRangeSet<ABANDONED_PATH_INLINE_RANGES, u32>);
7188
7189/// Size of the stack-allocated array in the [`ArrayRangeSet`].
7190///
7191/// A range is 2 u32's, so this is 16 * (4 + 4) = 128 bytes. A good size for inline data,
7192/// with plenty of ranges for common multipath use.
7193const ABANDONED_PATH_INLINE_RANGES: usize = 16;
7194
7195impl AbandonedPaths {
7196    /// The number of abandoned paths.
7197    fn len(&self) -> u32 {
7198        self.0.elts_count()
7199    }
7200
7201    /// The largest abandoned path.
7202    fn max(&self) -> Option<PathId> {
7203        self.0.max().map(PathId::from)
7204    }
7205
7206    /// Whether the path is already abandoned.
7207    fn contains(&self, val: &PathId) -> bool {
7208        self.0.contains(val.as_u32())
7209    }
7210
7211    /// Adds another abandoned path.
7212    fn insert(&mut self, val: PathId) {
7213        self.0.insert_one(val.as_u32());
7214    }
7215}
7216
7217/// Hints when the caller identifies a network change.
7218pub trait NetworkChangeHint: fmt::Debug + 'static {
7219    /// Inform the connection if a path may recover after a network change.
7220    ///
7221    /// After network changes, paths may not be recoverable. In this case, waiting for the path to
7222    /// become idle may take longer than what is desirable. If [`Self::is_path_recoverable`]
7223    /// returns `false`, a multipath-enabled, client-side connection will establish a new path to
7224    /// the same remote, closing the current one, instead of migrating the path.
7225    ///
7226    /// Paths that are deemed recoverable will simply be sent a PING for a liveness check.
7227    fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool;
7228}
7229
7230/// Return value for [`Connection::poll_transmit_path_space`].
7231#[derive(Debug)]
7232enum PollPathSpaceStatus {
7233    /// Nothing to send in the space, nothing was written into the [`TransmitBuf`].
7234    NothingToSend {
7235        /// If true there was data to send but congestion control did not allow so.
7236        congestion_blocked: bool,
7237    },
7238    /// One or more packets have been written into the [`TransmitBuf`].
7239    WrotePacket {
7240        /// The highest packet number.
7241        last_packet_number: u64,
7242        /// Whether to pad an already started datagram in the next packet.
7243        ///
7244        /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire
7245        /// datagram they may decide to coalesce with the next packet from a higher
7246        /// encryption level on the same path. But the earlier packet may require specific
7247        /// size requirements for the datagram they are sent in.
7248        ///
7249        /// If a space did not complete the datagram, they use this to request the correct
7250        /// padding in the final packet of the datagram so that the final datagram will have
7251        /// the correct size.
7252        ///
7253        /// If a space did fill an entire datagram, it leaves this to the default of
7254        /// [`PadDatagram::No`].
7255        pad_datagram: PadDatagram,
7256    },
7257    /// Send the contents of the transmit immediately.
7258    ///
7259    /// Packets were written and the GSO batch must end now, regardless from whether higher
7260    /// spaces still have frames to write. This is used when the last datagram written would
7261    /// require too much padding to continue a GSO batch, which would waste space on the
7262    /// wire.
7263    Send {
7264        /// The highest packet number written into the transmit.
7265        last_packet_number: u64,
7266    },
7267}
7268
7269/// Information used to decide what frames to schedule into which packets.
7270///
7271/// Primarily used by [`Connection::poll_transmit_on_path`] and the functions that help
7272/// building packets for it: [`Connection::poll_transmit_path_space`] and
7273/// [`Connection::populate_packet`].
7274#[derive(Debug, Copy, Clone)]
7275struct PathSchedulingInfo {
7276    /// Whether the path is abandoned.
7277    ///
7278    /// Note that a path that is abandoned but still has CIDs can still send a packet. After
7279    /// sending that packet the CIDs issued by the remote have to be considered retired as
7280    /// well.
7281    is_abandoned: bool,
7282    /// Whether the path may send [`SpaceKind::Data`] frames.
7283    ///
7284    /// Some paths should only send frames from [`SendableFrames::space_specific`]. All other
7285    /// frames are essentially frames that can be sent on any [`SpaceKind::Data`] space. For
7286    /// those we want to respect packet scheduling rules however.
7287    ///
7288    /// Roughly speaking data frames are only sent on spaces that have CIDs, are not
7289    /// abandoned and have no *better* spaces. However see to comments where this is
7290    /// populated for the exact packet scheduling implementation.
7291    ///
7292    /// This essentially marks this paths as the best validated space ID. Except during
7293    /// the handshake in which case it does not need to be validated. Several paths could be
7294    /// equally good and all have this set to `true`, in that case packet scheduling can
7295    /// choose which path to use. Currently it chooses the lowest path that is not
7296    /// congestion blocked.
7297    ///
7298    /// Note that once in the closed or draining states this will never be true.
7299    may_send_data: bool,
7300    /// Whether the path may send a CONNECTION_CLOSE frame.
7301    ///
7302    /// This essentially marks this path as the best validated space ID with a fallback
7303    /// to unvalidated spaces if there are no validated spaces. Like for
7304    /// [`Self::may_send_data`] other paths could be equally good.
7305    may_send_close: bool,
7306    may_self_abandon: bool,
7307}
7308
7309#[derive(Debug, Copy, Clone, PartialEq, Eq)]
7310enum PathBlocked {
7311    No,
7312    AntiAmplification,
7313    Congestion,
7314    Pacing,
7315}
7316
7317/// Fields of `Connection` specific to it being client-side or server-side
7318enum ConnectionSide {
7319    Client {
7320        /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded
7321        token: Bytes,
7322        token_store: Arc<dyn TokenStore>,
7323        server_name: String,
7324    },
7325    Server {
7326        server_config: Arc<ServerConfig>,
7327    },
7328}
7329
7330impl ConnectionSide {
7331    fn is_client(&self) -> bool {
7332        self.side().is_client()
7333    }
7334
7335    fn is_server(&self) -> bool {
7336        self.side().is_server()
7337    }
7338
7339    fn side(&self) -> Side {
7340        match *self {
7341            Self::Client { .. } => Side::Client,
7342            Self::Server { .. } => Side::Server,
7343        }
7344    }
7345}
7346
7347impl From<SideArgs> for ConnectionSide {
7348    fn from(side: SideArgs) -> Self {
7349        match side {
7350            SideArgs::Client {
7351                token_store,
7352                server_name,
7353            } => Self::Client {
7354                token: token_store.take(&server_name).unwrap_or_default(),
7355                token_store,
7356                server_name,
7357            },
7358            SideArgs::Server {
7359                server_config,
7360                pref_addr_cid: _,
7361                path_validated: _,
7362            } => Self::Server { server_config },
7363        }
7364    }
7365}
7366
7367/// Parameters to `Connection::new` specific to it being client-side or server-side
7368pub(crate) enum SideArgs {
7369    Client {
7370        token_store: Arc<dyn TokenStore>,
7371        server_name: String,
7372    },
7373    Server {
7374        server_config: Arc<ServerConfig>,
7375        pref_addr_cid: Option<ConnectionId>,
7376        path_validated: bool,
7377    },
7378}
7379
7380impl SideArgs {
7381    pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> {
7382        match *self {
7383            Self::Client { .. } => None,
7384            Self::Server { pref_addr_cid, .. } => pref_addr_cid,
7385        }
7386    }
7387
7388    pub(crate) fn path_validated(&self) -> bool {
7389        match *self {
7390            Self::Client { .. } => true,
7391            Self::Server { path_validated, .. } => path_validated,
7392        }
7393    }
7394
7395    pub(crate) fn side(&self) -> Side {
7396        match *self {
7397            Self::Client { .. } => Side::Client,
7398            Self::Server { .. } => Side::Server,
7399        }
7400    }
7401}
7402
7403/// Reasons why a connection might be lost
7404#[derive(Debug, Error, Clone, PartialEq, Eq)]
7405pub enum ConnectionError {
7406    /// The peer doesn't implement any supported version
7407    #[error("peer doesn't implement any supported version")]
7408    VersionMismatch,
7409    /// The peer violated the QUIC specification as understood by this implementation
7410    #[error(transparent)]
7411    TransportError(#[from] TransportError),
7412    /// The peer's QUIC stack aborted the connection automatically
7413    #[error("aborted by peer: {0}")]
7414    ConnectionClosed(frame::ConnectionClose),
7415    /// The peer closed the connection
7416    #[error("closed by peer: {0}")]
7417    ApplicationClosed(frame::ApplicationClose),
7418    /// The peer is unable to continue processing this connection, usually due to having restarted
7419    #[error("reset by peer")]
7420    Reset,
7421    /// Communication with the peer has lapsed for longer than the negotiated idle timeout
7422    ///
7423    /// If neither side is sending keep-alives, a connection will time out after a long enough idle
7424    /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`]
7425    /// and [`TransportConfig::keep_alive_interval()`].
7426    #[error("timed out")]
7427    TimedOut,
7428    /// The local application closed the connection
7429    #[error("closed")]
7430    LocallyClosed,
7431    /// The connection could not be created because not enough of the CID space is available
7432    ///
7433    /// Try using longer connection IDs.
7434    #[error("CIDs exhausted")]
7435    CidsExhausted,
7436}
7437
7438impl From<Close> for ConnectionError {
7439    fn from(x: Close) -> Self {
7440        match x {
7441            Close::Connection(reason) => Self::ConnectionClosed(reason),
7442            Close::Application(reason) => Self::ApplicationClosed(reason),
7443        }
7444    }
7445}
7446
7447// For compatibility with API consumers
7448impl From<ConnectionError> for io::Error {
7449    fn from(x: ConnectionError) -> Self {
7450        use ConnectionError::*;
7451        let kind = match x {
7452            TimedOut => io::ErrorKind::TimedOut,
7453            Reset => io::ErrorKind::ConnectionReset,
7454            ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted,
7455            TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => {
7456                io::ErrorKind::Other
7457            }
7458        };
7459        Self::new(kind, x)
7460    }
7461}
7462
7463/// Errors that might trigger a path being closed
7464// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API
7465#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
7466pub enum PathError {
7467    /// The extension was not negotiated with the peer
7468    #[error("multipath extension not negotiated")]
7469    MultipathNotNegotiated,
7470    /// Paths can only be opened client-side
7471    #[error("the server side may not open a path")]
7472    ServerSideNotAllowed,
7473    /// Current limits do not allow us to open more paths
7474    #[error("maximum number of concurrent paths reached")]
7475    MaxPathIdReached,
7476    /// No remote CIDs available to open a new path
7477    #[error("remoted CIDs exhausted")]
7478    RemoteCidsExhausted,
7479    /// Path could not be validated and will be abandoned
7480    #[error("path validation failed")]
7481    ValidationFailed,
7482    /// The remote address for the path is not supported by the endpoint
7483    #[error("invalid remote address")]
7484    InvalidRemoteAddress(SocketAddr),
7485}
7486
7487/// Errors triggered when abandoning a path
7488#[derive(Debug, Error, Clone, Eq, PartialEq)]
7489pub enum ClosePathError {
7490    /// Multipath is not negotiated
7491    #[error("Multipath extension not negotiated")]
7492    MultipathNotNegotiated,
7493    /// The path is already closed or was never opened
7494    #[error("closed path")]
7495    ClosedPath,
7496    /// Cannot close the last remaining open path via the local API.
7497    ///
7498    /// Use [`Connection::close`] to end the connection instead.
7499    #[error("last open path")]
7500    LastOpenPath,
7501}
7502
7503/// Error when the multipath extension was not negotiated, but attempted to be used.
7504#[derive(Debug, Error, Clone, Copy)]
7505#[error("Multipath extension not negotiated")]
7506pub struct MultipathNotNegotiated {
7507    _private: (),
7508}
7509
7510/// Events of interest to the application
7511#[derive(Debug)]
7512pub enum Event {
7513    /// The connection's handshake data is ready
7514    HandshakeDataReady,
7515    /// The connection was successfully established
7516    Connected,
7517    /// The TLS handshake was confirmed
7518    HandshakeConfirmed,
7519    /// The connection was lost
7520    ///
7521    /// Emitted when the connection is closed due to an error, a timeout, or the peer closing it.
7522    /// This is **not** emitted when the local application closes the connection via
7523    /// [`Connection::close()`](crate::Connection::close). In that case, pending operations will
7524    /// fail with [`ConnectionError::LocallyClosed`].
7525    ConnectionLost {
7526        /// Reason that the connection was closed
7527        reason: ConnectionError,
7528    },
7529    /// Stream events
7530    Stream(StreamEvent),
7531    /// One or more application datagrams have been received
7532    DatagramReceived,
7533    /// One or more application datagrams have been sent after blocking
7534    DatagramsUnblocked,
7535    /// (Multi)Path events
7536    Path(PathEvent),
7537    /// n0's nat traversal events
7538    NatTraversal(n0_nat_traversal::Event),
7539}
7540
7541impl From<PathEvent> for Event {
7542    fn from(source: PathEvent) -> Self {
7543        Self::Path(source)
7544    }
7545}
7546
7547fn get_max_ack_delay(params: &TransportParameters) -> Duration {
7548    Duration::from_micros(params.max_ack_delay.0 * 1000)
7549}
7550
7551/// Prevents overflow and improves behavior in extreme circumstances.
7552const MAX_BACKOFF_EXPONENT: u32 = 16;
7553
7554/// The max interval between successive tail-loss probes.
7555///
7556/// This is the "normal" value we use.
7557const MAX_PTO_INTERVAL: Duration = Duration::from_secs(2);
7558
7559/// The idle time, below which we use the shorter [`MAX_PTO_FAST_INTERVAL`].
7560const MIN_IDLE_FOR_FAST_PTO: Duration = Duration::from_secs(25);
7561
7562/// The max interval between successive tail-loss probes with short idle times.
7563///
7564/// If the path or connection idle time is less than [`MIN_IDLE_FOR_FAST_PTO`] then we use
7565/// this value to ensure we have plenty of retransmits before we reach the idle time.
7566const MAX_PTO_FAST_INTERVAL: Duration = Duration::from_secs(1);
7567
7568/// The RTT threshold above which we cap the PTO interval to 1.5 * smoothed_rtt
7569///
7570/// This is RTT time above which 1.5 * RTT > [`MAX_PTO_INTERVAL`], for these links we want
7571/// to extend the interval between tail-loss probes to not fill the entire pipe with them.
7572const SLOW_RTT_THRESHOLD: Duration =
7573    Duration::from_millis((MAX_PTO_INTERVAL.as_millis() as u64 * 2) / 3);
7574
7575/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag
7576///
7577/// This must be at least as large as the header for a well-formed empty packet to be coalesced,
7578/// plus some space for frames. We only care about handshake headers because short header packets
7579/// necessarily have smaller headers, and initial packets are only ever the first packet in a
7580/// datagram (because we coalesce in ascending packet space order and the only reason to split a
7581/// packet is when packet space changes).
7582const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32;
7583
7584/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header
7585///
7586/// Excludes packet-type-specific fields such as packet number or Initial token
7587// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid +
7588// scid len + scid + length + pn
7589const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize =
7590    1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4;
7591
7592#[derive(Default)]
7593struct SentFrames {
7594    retransmits: ThinRetransmits,
7595    path_retransmits: PathRetransmits,
7596    /// The packet number of the largest acknowledged packet for each path
7597    largest_acked: FxHashMap<PathId, u64>,
7598    stream_frames: StreamMetaVec,
7599    /// Whether the packet contains non-retransmittable frames (like datagrams)
7600    non_retransmits: bool,
7601    /// If the datagram containing these frames should be padded to the min MTU
7602    requires_padding: bool,
7603}
7604
7605impl SentFrames {
7606    /// Returns whether the packet contains only ACKs
7607    fn is_ack_only(&self, streams: &StreamsState) -> bool {
7608        !self.largest_acked.is_empty()
7609            && !self.non_retransmits
7610            && self.stream_frames.is_empty()
7611            && self.retransmits.is_empty(streams)
7612    }
7613
7614    fn retransmits_mut(&mut self) -> &mut Retransmits {
7615        self.retransmits.get_or_create()
7616    }
7617
7618    fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) {
7619        use frame::EncodableFrame::*;
7620        match frame {
7621            PathAck(path_ack_encoder) => {
7622                if let Some(max) = path_ack_encoder.ranges.max() {
7623                    self.largest_acked.insert(path_ack_encoder.path_id, max);
7624                }
7625            }
7626            Ack(ack_encoder) => {
7627                if let Some(max) = ack_encoder.ranges.max() {
7628                    self.largest_acked.insert(PathId::ZERO, max);
7629                }
7630            }
7631            Close(_) => { /* non retransmittable, but after this we don't really care */ }
7632            PathResponse(_) => self.non_retransmits = true,
7633            HandshakeDone(_) => self.retransmits_mut().handshake_done = true,
7634            ReachOut(frame) => self.retransmits_mut().reach_out.push(frame),
7635            ObservedAddr(_) => self.path_retransmits.observed_address = true,
7636            Ping(_) => self.non_retransmits = true,
7637            ImmediateAck(_) => self.non_retransmits = true,
7638            AckFrequency(_) => self.retransmits_mut().ack_frequency = true,
7639            PathChallenge(_) => self.non_retransmits = true,
7640            Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto),
7641            PathAbandon(path_abandon) => {
7642                self.retransmits_mut()
7643                    .path_abandon
7644                    .entry(path_abandon.path_id)
7645                    .or_insert(path_abandon.error_code);
7646            }
7647            PathStatusAvailable(frame::PathStatusAvailable { path_id, .. })
7648            | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => {
7649                self.retransmits_mut().path_status.insert(path_id);
7650            }
7651            MaxPathId(_) => self.retransmits_mut().max_path_id = true,
7652            PathsBlocked(frame::PathsBlocked(path_id)) => {
7653                let paths_blocked = &mut self.retransmits_mut().paths_blocked;
7654                *paths_blocked = cmp::max(*paths_blocked, Some(path_id));
7655            }
7656            PathCidsBlocked(path_cids_blocked) => {
7657                self.retransmits_mut()
7658                    .path_cids_blocked
7659                    .entry(path_cids_blocked.path_id)
7660                    .and_modify(|next_seq| {
7661                        *next_seq = cmp::max(*next_seq, path_cids_blocked.next_seq);
7662                    })
7663                    .or_insert(path_cids_blocked.next_seq);
7664            }
7665            ResetStream(reset) => self
7666                .retransmits_mut()
7667                .reset_stream
7668                .push((reset.id, reset.error_code)),
7669            StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending),
7670            NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()),
7671            RetireConnectionId(retire_cid) => self
7672                .retransmits_mut()
7673                .retire_cids
7674                .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)),
7675            Datagram(_) => self.non_retransmits = true,
7676            NewToken(_) => {}
7677            AddAddress(add_address) => {
7678                self.retransmits_mut().add_address.insert(add_address);
7679            }
7680            RemoveAddress(remove_address) => {
7681                self.retransmits_mut().remove_address.insert(remove_address);
7682            }
7683            StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta),
7684            MaxData(_) => self.retransmits_mut().max_data = true,
7685            MaxStreamData(max) => {
7686                self.retransmits_mut().max_stream_data.insert(max.id);
7687            }
7688            MaxStreams(max_streams) => {
7689                self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true
7690            }
7691            StreamsBlocked(streams_blocked) => {
7692                self.retransmits_mut().streams_blocked[streams_blocked.dir as usize] = true
7693            }
7694        }
7695    }
7696}
7697
7698/// Computes the negotiated idle timeout based on the transport parameters.
7699///
7700/// According to the definition of max_idle_timeout, a value of `0` means the timeout is
7701/// disabled; see <https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.4.1.>
7702///
7703/// According to the negotiation procedure, either the minimum of the timeouts or one
7704/// specified is used as the negotiated value; see
7705/// <https://www.rfc-editor.org/rfc/rfc9000#section-10.1-2.>
7706///
7707/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have
7708/// opted out of idle timeout.
7709fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> {
7710    match (x, y) {
7711        (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None,
7712        (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)),
7713        (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)),
7714        (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)),
7715    }
7716}
7717
7718#[cfg(test)]
7719mod tests {
7720    use super::*;
7721
7722    #[test]
7723    fn negotiate_max_idle_timeout_commutative() {
7724        let test_params = [
7725            (None, None, None),
7726            (None, Some(VarInt(0)), None),
7727            (None, Some(VarInt(2)), Some(Duration::from_millis(2))),
7728            (Some(VarInt(0)), Some(VarInt(0)), None),
7729            (
7730                Some(VarInt(2)),
7731                Some(VarInt(0)),
7732                Some(Duration::from_millis(2)),
7733            ),
7734            (
7735                Some(VarInt(1)),
7736                Some(VarInt(4)),
7737                Some(Duration::from_millis(1)),
7738            ),
7739        ];
7740
7741        for (left, right, result) in test_params {
7742            assert_eq!(negotiate_max_idle_timeout(left, right), result);
7743            assert_eq!(negotiate_max_idle_timeout(right, left), result);
7744        }
7745    }
7746
7747    #[test]
7748    fn abandoned_paths() {
7749        let mut t = AbandonedPaths::default();
7750
7751        t.insert(PathId(0));
7752        t.insert(PathId(1));
7753        assert_eq!(t.len(), 2);
7754        assert_eq!(t.0.range_count(), 1); // 2 elements compacted into one range
7755        assert!(t.contains(&PathId(0)));
7756        assert!(t.contains(&PathId(1)));
7757        assert!(!t.contains(&PathId(2)));
7758        assert!(!t.contains(&PathId(3)));
7759        assert_eq!(t.max(), Some(PathId(1)));
7760
7761        t.insert(PathId(3));
7762        assert_eq!(t.len(), 3);
7763        assert_eq!(t.0.range_count(), 2); // 3 elements compacted into 2 ranges
7764        assert!(t.contains(&PathId(0)));
7765        assert!(t.contains(&PathId(1)));
7766        assert!(!t.contains(&PathId(2)));
7767        assert!(t.contains(&PathId(3)));
7768        assert_eq!(t.max(), Some(PathId(3)));
7769
7770        t.insert(PathId(2));
7771        assert_eq!(t.len(), 4);
7772        assert_eq!(t.0.range_count(), 1); // 4 elements compacted into 1 range
7773        assert!(t.contains(&PathId(0)));
7774        assert!(t.contains(&PathId(1)));
7775        assert!(t.contains(&PathId(2)));
7776        assert!(t.contains(&PathId(3)));
7777        assert_eq!(t.max(), Some(PathId(3)));
7778    }
7779}