s2n_quic_core/connection/
limits.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3#[cfg(feature = "alloc")]
4use crate::application::ServerName;
5use crate::{
6    ack,
7    event::{api::SocketAddress, IntoEvent},
8    inet, recovery, stream,
9    transport::parameters::{
10        AckDelayExponent, ActiveConnectionIdLimit, InitialFlowControlLimits, InitialMaxData,
11        InitialMaxStreamDataBidiLocal, InitialMaxStreamDataBidiRemote, InitialMaxStreamDataUni,
12        InitialMaxStreamsBidi, InitialMaxStreamsUni, InitialStreamLimits, MaxAckDelay,
13        MaxDatagramFrameSize, MaxIdleTimeout, MigrationSupport, TransportParameters,
14    },
15};
16#[cfg(feature = "alloc")]
17use bytes::Bytes;
18use core::time::Duration;
19use s2n_codec::decoder_invariant;
20
21pub use crate::transport::parameters::ValidationError;
22
23const MAX_HANDSHAKE_DURATION_DEFAULT: Duration = Duration::from_secs(10);
24
25//= https://www.rfc-editor.org/rfc/rfc9000#section-10.1.2
26//# A connection will time out if no packets are sent or received for a
27//# period longer than the time negotiated using the max_idle_timeout
28//# transport parameter; see Section 10.  However, state in middleboxes
29//# might time out earlier than that.  Though REQ-5 in [RFC4787]
30//# recommends a 2-minute timeout interval, experience shows that sending
31//# packets every 30 seconds is necessary to prevent the majority of
32//# middleboxes from losing state for UDP flows [GATEWAY].
33const MAX_KEEP_ALIVE_PERIOD_DEFAULT: Duration = Duration::from_secs(30);
34
35//= https://www.rfc-editor.org/rfc/rfc9000#section-8.1
36//# Prior to validating the client address, servers MUST NOT send more
37//# than three times as many bytes as the number of bytes they have
38//# received.
39pub const ANTI_AMPLIFICATION_MULTIPLIER: u8 = 3;
40
41pub const DEFAULT_STREAM_BATCH_SIZE: u8 = 1;
42
43// Maximum allowed PTO jitter percentage. Limited to 50% to prevent PTO timers
44// from becoming too short (which could cause premature timeouts) or too long
45// (which could delay loss recovery).
46pub const MAX_PTO_JITTER_PERCENTAGE: u8 = 50;
47pub const DEFAULT_PTO_JITTER_PERCENTAGE: u8 = 0;
48
49#[non_exhaustive]
50#[derive(Debug)]
51pub struct ConnectionInfo<'a> {
52    pub remote_address: SocketAddress<'a>,
53}
54
55impl<'a> ConnectionInfo<'a> {
56    #[inline]
57    #[doc(hidden)]
58    pub fn new(remote_address: &'a inet::SocketAddress) -> Self {
59        Self {
60            remote_address: remote_address.into_event(),
61        }
62    }
63}
64
65#[non_exhaustive]
66#[derive(Debug)]
67#[cfg(feature = "alloc")]
68pub struct HandshakeInfo<'a> {
69    pub remote_address: SocketAddress<'a>,
70    pub server_name: Option<&'a ServerName>,
71    pub application_protocol: &'a Bytes,
72}
73
74#[cfg(feature = "alloc")]
75impl<'a> HandshakeInfo<'a> {
76    pub fn new(
77        remote_address: &'a inet::SocketAddress,
78        server_name: Option<&'a ServerName>,
79        application_protocol: &'a Bytes,
80    ) -> HandshakeInfo<'a> {
81        Self {
82            remote_address: remote_address.into_event(),
83            server_name,
84            application_protocol,
85        }
86    }
87}
88
89#[derive(Clone, Copy, Debug)]
90pub struct Limits {
91    pub(crate) max_idle_timeout: MaxIdleTimeout,
92    pub(crate) data_window: InitialMaxData,
93    pub(crate) bidirectional_local_data_window: InitialMaxStreamDataBidiLocal,
94    pub(crate) bidirectional_remote_data_window: InitialMaxStreamDataBidiRemote,
95    pub(crate) unidirectional_data_window: InitialMaxStreamDataUni,
96    pub(crate) max_open_local_bidirectional_streams: stream::limits::LocalBidirectional,
97    pub(crate) max_open_local_unidirectional_streams: stream::limits::LocalUnidirectional,
98    pub(crate) max_open_remote_bidirectional_streams: InitialMaxStreamsBidi,
99    pub(crate) max_open_remote_unidirectional_streams: InitialMaxStreamsUni,
100    pub(crate) max_ack_delay: MaxAckDelay,
101    pub(crate) ack_delay_exponent: AckDelayExponent,
102    pub(crate) max_active_connection_ids: ActiveConnectionIdLimit,
103    pub(crate) ack_elicitation_interval: u8,
104    pub(crate) ack_ranges_limit: u8,
105    pub(crate) max_send_buffer_size: stream::limits::MaxSendBufferSize,
106    pub(crate) max_handshake_duration: Duration,
107    pub(crate) max_keep_alive_period: Duration,
108    pub(crate) max_datagram_frame_size: MaxDatagramFrameSize,
109    pub(crate) initial_round_trip_time: Duration,
110    pub(crate) migration_support: MigrationSupport,
111    pub(crate) anti_amplification_multiplier: u8,
112    pub(crate) stream_batch_size: u8,
113    pub(crate) pto_jitter_percentage: u8,
114}
115
116impl Default for Limits {
117    fn default() -> Self {
118        Self::new()
119    }
120}
121
122macro_rules! setter {
123    ($(#[doc = $doc:literal])* $name:ident, $field:ident, $inner:ty $(, |$validate_value:ident| $validation:block)?) => {
124        $(#[doc = $doc])*
125        pub fn $name(mut self, value: $inner) -> Result<Self, ValidationError> {
126            $(
127                let $validate_value = value;
128                $validation
129            )?
130            self.$field = value.try_into()?;
131            Ok(self)
132        }
133    };
134}
135
136impl Limits {
137    pub const fn new() -> Self {
138        Self {
139            max_idle_timeout: MaxIdleTimeout::RECOMMENDED,
140            data_window: InitialMaxData::RECOMMENDED,
141            bidirectional_local_data_window: InitialMaxStreamDataBidiLocal::RECOMMENDED,
142            bidirectional_remote_data_window: InitialMaxStreamDataBidiRemote::RECOMMENDED,
143            unidirectional_data_window: InitialMaxStreamDataUni::RECOMMENDED,
144            max_open_local_bidirectional_streams: stream::limits::LocalBidirectional::RECOMMENDED,
145            max_open_local_unidirectional_streams: stream::limits::LocalUnidirectional::RECOMMENDED,
146            max_open_remote_bidirectional_streams: InitialMaxStreamsBidi::RECOMMENDED,
147            max_open_remote_unidirectional_streams: InitialMaxStreamsUni::RECOMMENDED,
148            max_ack_delay: MaxAckDelay::RECOMMENDED,
149            ack_delay_exponent: AckDelayExponent::RECOMMENDED,
150            max_active_connection_ids: ActiveConnectionIdLimit::RECOMMENDED,
151            ack_elicitation_interval: ack::Settings::RECOMMENDED.ack_elicitation_interval,
152            ack_ranges_limit: ack::Settings::RECOMMENDED.ack_ranges_limit,
153            max_send_buffer_size: stream::Limits::RECOMMENDED.max_send_buffer_size,
154            max_handshake_duration: MAX_HANDSHAKE_DURATION_DEFAULT,
155            max_keep_alive_period: MAX_KEEP_ALIVE_PERIOD_DEFAULT,
156            max_datagram_frame_size: MaxDatagramFrameSize::DEFAULT,
157            initial_round_trip_time: recovery::DEFAULT_INITIAL_RTT,
158            migration_support: MigrationSupport::RECOMMENDED,
159            anti_amplification_multiplier: ANTI_AMPLIFICATION_MULTIPLIER,
160            stream_batch_size: DEFAULT_STREAM_BATCH_SIZE,
161            pto_jitter_percentage: DEFAULT_PTO_JITTER_PERCENTAGE,
162        }
163    }
164
165    // We limit the initial data limit to u32::MAX (4GB), which far
166    // exceeds the reasonable amount of data a connection is
167    // initially allowed to send.
168    //
169    // By representing the flow control value as a u32, we save space
170    // on the connection state.
171    setter!(with_data_window, data_window, u64, |validate_value| {
172        decoder_invariant!(
173            validate_value <= u32::MAX.into(),
174            "data_window must be <= u32::MAX"
175        );
176    });
177    setter!(
178        with_bidirectional_local_data_window,
179        bidirectional_local_data_window,
180        u64,
181        |validate_value| {
182            decoder_invariant!(
183                validate_value <= u32::MAX.into(),
184                "bidirectional_local_data_window must be <= u32::MAX"
185            );
186        }
187    );
188    setter!(
189        with_bidirectional_remote_data_window,
190        bidirectional_remote_data_window,
191        u64,
192        |validate_value| {
193            decoder_invariant!(
194                validate_value <= u32::MAX.into(),
195                "bidirectional_remote_data_window must be <= u32::MAX"
196            );
197        }
198    );
199    setter!(
200        with_unidirectional_data_window,
201        unidirectional_data_window,
202        u64,
203        |validate_value| {
204            decoder_invariant!(
205                validate_value <= u32::MAX.into(),
206                "unidirectional_data_window must be <= u32::MAX"
207            );
208        }
209    );
210
211    setter!(with_max_idle_timeout, max_idle_timeout, Duration);
212
213    /// Sets both the max local and remote limits for bidirectional streams.
214    #[deprecated(
215        note = "use with_max_open_local_bidirectional_streams and with_max_open_remote_bidirectional_streams instead"
216    )]
217    pub fn with_max_open_bidirectional_streams(
218        mut self,
219        value: u64,
220    ) -> Result<Self, ValidationError> {
221        self.max_open_local_bidirectional_streams = value.try_into()?;
222        self.max_open_remote_bidirectional_streams = value.try_into()?;
223        Ok(self)
224    }
225
226    /// Sets the max local limits for bidirectional streams
227    ///
228    /// The value set is used instead of `with_max_open_bidirectional_streams` when set.
229    pub fn with_max_open_local_bidirectional_streams(
230        mut self,
231        value: u64,
232    ) -> Result<Self, ValidationError> {
233        self.max_open_local_bidirectional_streams = value.try_into()?;
234        Ok(self)
235    }
236
237    /// Sets the max remote limits for bidirectional streams.
238    ///
239    /// The value set is used instead of `with_max_open_bidirectional_streams` when set.
240    pub fn with_max_open_remote_bidirectional_streams(
241        mut self,
242        value: u64,
243    ) -> Result<Self, ValidationError> {
244        self.max_open_remote_bidirectional_streams = value.try_into()?;
245        Ok(self)
246    }
247
248    setter!(
249        with_max_open_local_unidirectional_streams,
250        max_open_local_unidirectional_streams,
251        u64
252    );
253    setter!(
254        with_max_open_remote_unidirectional_streams,
255        max_open_remote_unidirectional_streams,
256        u64
257    );
258    setter!(with_max_ack_delay, max_ack_delay, Duration);
259    setter!(
260        with_max_active_connection_ids,
261        max_active_connection_ids,
262        u64
263    );
264    setter!(with_stream_batch_size, stream_batch_size, u8);
265    setter!(with_ack_elicitation_interval, ack_elicitation_interval, u8);
266    setter!(with_max_ack_ranges, ack_ranges_limit, u8);
267    setter!(
268        /// Sets the maximum send buffer size for a Stream
269        ///
270        /// The send buffer contains unacknowledged application data. Constraining the maximum
271        /// size of this buffer limits the amount of memory a given Stream may consume. On
272        /// high bandwidth/high RTT connections this may act as a bottleneck, as the connection may be
273        /// waiting for data to be acknowledged by the peer before allowing more data to be sent.
274        /// Increasing this value should be carefully weighed against the potential downsides
275        /// of additional memory utilization as well as increased latency due to the capacity of the
276        /// send buffer exceeding the rate at which the network link and peer are able to drain from it.
277        /// Ideally, the max_send_buffer_size is configured to the minimum value that can support the
278        /// throughput requirements for the connection.
279        with_max_send_buffer_size,
280        max_send_buffer_size,
281        u32
282    );
283    setter!(
284        with_max_handshake_duration,
285        max_handshake_duration,
286        Duration
287    );
288    setter!(with_max_keep_alive_period, max_keep_alive_period, Duration);
289    /// Sets whether active connection migration is supported for a server endpoint (default: true)
290    ///
291    /// If set to false, the `disable_active_migration` transport parameter will be sent to the
292    /// peer, and any attempt by the peer to perform an active connection migration will be ignored.
293    pub fn with_active_connection_migration(
294        mut self,
295        enabled: bool,
296    ) -> Result<Self, ValidationError> {
297        if enabled {
298            self.migration_support = MigrationSupport::Enabled
299        } else {
300            self.migration_support = MigrationSupport::Disabled
301        }
302        Ok(self)
303    }
304
305    /// Sets the initial round trip time (RTT) for use in recovery mechanisms prior to
306    /// measuring an actual RTT sample.
307    ///
308    /// This is useful for environments where RTTs are mostly predictable (e.g. data centers)
309    /// and are much lower than the default 333 milliseconds.
310    pub fn with_initial_round_trip_time(
311        mut self,
312        value: Duration,
313    ) -> Result<Self, ValidationError> {
314        ensure!(
315            value >= recovery::MIN_RTT,
316            Err(ValidationError(
317                "provided value must be at least 1 microsecond",
318            ))
319        );
320
321        self.initial_round_trip_time = value;
322        Ok(self)
323    }
324
325    #[cfg(feature = "unstable-limits")]
326    setter!(
327        /// Limit how many bytes the Server sends prior to address validation (default: 3)
328        ///
329        /// Prior to validating the client address, servers will not send more
330        /// than `anti_amplification_multiplier` times as many bytes as the
331        /// number of bytes it has received.
332        with_anti_amplification_multiplier,
333        anti_amplification_multiplier,
334        u8
335    );
336
337    /// Sets the PTO jitter percentage (default: 0)
338    ///
339    /// Adds random jitter to Probe Timeout (PTO) calculations to prevent synchronized
340    /// timeouts across multiple connections. The jitter is applied as a percentage
341    /// of the base PTO period, with values between -X% and +X% where X is the
342    /// configured percentage.
343    ///
344    /// Valid range: 0-50%
345    /// - 0%: No jitter (default)
346    /// - 1-50%: Applies random jitter within ±percentage of base PTO
347    pub fn with_pto_jitter_percentage(mut self, value: u8) -> Result<Self, ValidationError> {
348        ensure!(
349            value <= MAX_PTO_JITTER_PERCENTAGE,
350            Err(ValidationError(
351                "PTO jitter percentage must be between 0 and 50"
352            ))
353        );
354        self.pto_jitter_percentage = value;
355        Ok(self)
356    }
357
358    #[doc(hidden)]
359    #[inline]
360    pub fn pto_jitter_percentage(&self) -> u8 {
361        self.pto_jitter_percentage
362    }
363
364    // internal APIs
365
366    #[doc(hidden)]
367    #[inline]
368    pub fn load_peer<A, B, C, D>(&mut self, peer_parameters: &TransportParameters<A, B, C, D>) {
369        self.max_idle_timeout
370            .load_peer(&peer_parameters.max_idle_timeout);
371    }
372
373    #[doc(hidden)]
374    #[inline]
375    pub const fn ack_settings(&self) -> ack::Settings {
376        ack::Settings {
377            ack_delay_exponent: self.ack_delay_exponent.as_u8(),
378            max_ack_delay: self.max_ack_delay.as_duration(),
379            ack_ranges_limit: self.ack_ranges_limit,
380            ack_elicitation_interval: self.ack_elicitation_interval,
381        }
382    }
383
384    #[doc(hidden)]
385    #[inline]
386    pub const fn initial_flow_control_limits(&self) -> InitialFlowControlLimits {
387        InitialFlowControlLimits {
388            stream_limits: self.initial_stream_limits(),
389            max_data: self.data_window.as_varint(),
390            max_open_remote_bidirectional_streams: self
391                .max_open_remote_bidirectional_streams
392                .as_varint(),
393            max_open_remote_unidirectional_streams: self
394                .max_open_remote_unidirectional_streams
395                .as_varint(),
396        }
397    }
398
399    #[doc(hidden)]
400    #[inline]
401    pub const fn initial_stream_limits(&self) -> InitialStreamLimits {
402        InitialStreamLimits {
403            max_data_bidi_local: self.bidirectional_local_data_window.as_varint(),
404            max_data_bidi_remote: self.bidirectional_remote_data_window.as_varint(),
405            max_data_uni: self.unidirectional_data_window.as_varint(),
406        }
407    }
408
409    #[doc(hidden)]
410    #[inline]
411    pub fn stream_limits(&self) -> stream::Limits {
412        stream::Limits {
413            max_send_buffer_size: self.max_send_buffer_size,
414            max_open_local_unidirectional_streams: self.max_open_local_unidirectional_streams,
415            max_open_local_bidirectional_streams: self.max_open_local_bidirectional_streams,
416        }
417    }
418
419    #[doc(hidden)]
420    #[inline]
421    pub fn max_idle_timeout(&self) -> Option<Duration> {
422        self.max_idle_timeout.as_duration()
423    }
424
425    #[doc(hidden)]
426    #[inline]
427    pub fn max_handshake_duration(&self) -> Duration {
428        self.max_handshake_duration
429    }
430
431    #[doc(hidden)]
432    #[inline]
433    pub fn max_keep_alive_period(&self) -> Duration {
434        self.max_keep_alive_period
435    }
436
437    #[doc(hidden)]
438    #[inline]
439    pub fn initial_round_trip_time(&self) -> Duration {
440        self.initial_round_trip_time
441    }
442
443    #[doc(hidden)]
444    #[inline]
445    pub fn active_migration_enabled(&self) -> bool {
446        matches!(self.migration_support, MigrationSupport::Enabled)
447    }
448
449    #[doc(hidden)]
450    #[inline]
451    pub fn anti_amplification_multiplier(&self) -> u8 {
452        self.anti_amplification_multiplier
453    }
454
455    #[doc(hidden)]
456    #[inline]
457    pub fn stream_batch_size(&self) -> u8 {
458        self.stream_batch_size
459    }
460}
461
462#[must_use]
463#[derive(Debug)]
464pub struct UpdatableLimits<'a>(&'a mut Limits);
465
466impl<'a> UpdatableLimits<'a> {
467    pub fn new(limits: &'a mut Limits) -> UpdatableLimits<'a> {
468        UpdatableLimits(limits)
469    }
470
471    pub fn with_stream_batch_size(&mut self, size: u8) {
472        self.0.stream_batch_size = size;
473    }
474}
475
476/// Creates limits for a given connection
477pub trait Limiter: 'static + Send {
478    fn on_connection(&mut self, info: &ConnectionInfo) -> Limits;
479
480    /// Provides another opportunity to change connection limits with information
481    /// from the handshake
482    #[inline]
483    #[cfg(feature = "alloc")]
484    fn on_post_handshake(&mut self, info: &HandshakeInfo, limits: &mut UpdatableLimits) {
485        let _ = info;
486        let _ = limits;
487    }
488}
489
490/// Implement Limiter for a Limits struct
491impl Limiter for Limits {
492    fn on_connection(&mut self, _into: &ConnectionInfo) -> Limits {
493        *self
494    }
495    #[cfg(feature = "alloc")]
496    fn on_post_handshake(&mut self, _info: &HandshakeInfo, _limits: &mut UpdatableLimits) {}
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502
503    // Local max data limits should be <= u32::MAX
504    #[test]
505    fn limit_validation() {
506        let mut data = u32::MAX as u64 + 1;
507        let limits = Limits::default();
508        assert!(limits.with_data_window(data).is_err());
509        assert!(limits.with_bidirectional_local_data_window(data).is_err());
510        assert!(limits.with_bidirectional_remote_data_window(data).is_err());
511        assert!(limits.with_unidirectional_data_window(data).is_err());
512
513        data = u32::MAX as u64;
514        assert!(limits.with_data_window(data).is_ok());
515        assert!(limits.with_bidirectional_local_data_window(data).is_ok());
516        assert!(limits.with_bidirectional_remote_data_window(data).is_ok());
517        assert!(limits.with_unidirectional_data_window(data).is_ok());
518    }
519
520    // Limits can be updated through the UpdatableLimits wrapper
521    #[test]
522    fn updatable_limits() {
523        let mut limits = Limits::default();
524        assert_eq!(limits.stream_batch_size, 1);
525        let mut updatable_limits = UpdatableLimits::new(&mut limits);
526        let new_size = 10;
527        updatable_limits.with_stream_batch_size(new_size);
528        assert_eq!(limits.stream_batch_size, new_size);
529    }
530
531    #[test]
532    fn pto_jitter_percentage_default() {
533        let limits = Limits::new();
534        assert_eq!(
535            limits.pto_jitter_percentage(),
536            DEFAULT_PTO_JITTER_PERCENTAGE
537        );
538
539        let limits = Limits::default();
540        assert_eq!(
541            limits.pto_jitter_percentage(),
542            DEFAULT_PTO_JITTER_PERCENTAGE
543        );
544    }
545
546    #[test]
547    fn pto_jitter_percentage_valid_values() {
548        let limits = Limits::new();
549
550        // Test valid values (0-MAX_PTO_JITTER_PERCENTAGE)
551        for value in 0..=MAX_PTO_JITTER_PERCENTAGE {
552            let result = limits.with_pto_jitter_percentage(value);
553            assert!(result.is_ok(), "Value {} should be valid", value);
554            let limits = result.unwrap();
555            assert_eq!(limits.pto_jitter_percentage(), value);
556        }
557    }
558
559    #[test]
560    fn pto_jitter_percentage_invalid_values() {
561        let limits = Limits::new();
562
563        // Test invalid values (> MAX_PTO_JITTER_PERCENTAGE)
564        for value in (MAX_PTO_JITTER_PERCENTAGE + 1)..=255 {
565            let result = limits.with_pto_jitter_percentage(value);
566            assert!(result.is_err(), "Value {} should be invalid", value);
567
568            if let Err(ValidationError(msg)) = result {
569                assert_eq!(msg, "PTO jitter percentage must be between 0 and 50");
570            } else {
571                panic!("Expected ValidationError for value {}", value);
572            }
573        }
574    }
575
576    #[test]
577    fn pto_jitter_percentage_edge_cases() {
578        let limits = Limits::new();
579
580        // Test boundary values
581        let result = limits.with_pto_jitter_percentage(0);
582        assert!(result.is_ok());
583        assert_eq!(result.unwrap().pto_jitter_percentage(), 0);
584
585        let result = limits.with_pto_jitter_percentage(MAX_PTO_JITTER_PERCENTAGE);
586        assert!(result.is_ok());
587        assert_eq!(
588            result.unwrap().pto_jitter_percentage(),
589            MAX_PTO_JITTER_PERCENTAGE
590        );
591
592        let result = limits.with_pto_jitter_percentage(MAX_PTO_JITTER_PERCENTAGE + 1);
593        assert!(result.is_err());
594    }
595
596    #[test]
597    fn pto_jitter_percentage_chaining() {
598        // Test that the setter can be chained with other setters
599        let result = Limits::new()
600            .with_pto_jitter_percentage(25)
601            .and_then(|l| l.with_stream_batch_size(5));
602
603        assert!(result.is_ok());
604        let limits = result.unwrap();
605        assert_eq!(limits.pto_jitter_percentage(), 25);
606        assert_eq!(limits.stream_batch_size(), 5);
607    }
608
609    #[test]
610    fn pto_jitter_percentage_getter() {
611        let mut limits = Limits::new();
612
613        // Test initial value
614        assert_eq!(limits.pto_jitter_percentage(), 0);
615
616        // Test after setting value
617        limits = limits.with_pto_jitter_percentage(30).unwrap();
618        assert_eq!(limits.pto_jitter_percentage(), 30);
619
620        // Test that getter returns the correct value
621        limits = limits.with_pto_jitter_percentage(15).unwrap();
622        assert_eq!(limits.pto_jitter_percentage(), 15);
623    }
624}