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