Skip to main content

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