noq_proto/connection/
mod.rs

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