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