Skip to content
File

Blob: firmware/vendor/sctp-proto/src/config.rs

rust486 lines
1use crate::util::{AssociationIdGenerator, RandomAssociationIdGenerator};
2 
3use alloc::boxed::Box;
4use alloc::sync::Arc;
5use bytes::Bytes;
6use core::fmt;
7 
8/// MTU for inbound packet (from DTLS)
9pub(crate) const RECEIVE_MTU: usize = 8192;
10/// initial MTU for outgoing packets (to DTLS)
11pub(crate) const INITIAL_MTU: u32 = 1228;
12pub(crate) const INITIAL_RECV_BUF_SIZE: u32 = 1024 * 1024;
13pub(crate) const COMMON_HEADER_SIZE: u32 = 12;
14pub(crate) const DATA_CHUNK_HEADER_SIZE: u32 = 16;
15pub(crate) const DEFAULT_MAX_MESSAGE_SIZE: u32 = 65536;
16 
17// Default RTO values in milliseconds (RFC 4960)
18pub(crate) const RTO_INITIAL: u64 = 3000;
19pub(crate) const RTO_MIN: u64 = 1000;
20pub(crate) const RTO_MAX: u64 = 60000;
21 
22// Default max retransmit value (RFC 4960 Section 15)
23const DEFAULT_MAX_INIT_RETRANS: usize = 8;
24 
25/// Optional hard limits on retained inbound DATA state.
26///
27/// These limits apply before adding a new fragment, including data retained for
28/// missing TSNs and stream resets. They do not bound all association heap use,
29/// allocator overhead, or control-chunk state. Exceeding a limit closes the
30/// association; the limits are a resource policy, not SCTP flow control.
31#[derive(Debug, Clone, Copy)]
32pub struct ReceiveLimits {
33 max_message_size: u32,
34 max_buffered_bytes: u32,
35 max_buffered_chunks: usize,
36 max_streams: usize,
37}
38 
39impl ReceiveLimits {
40 /// Construct a receive resource policy.
41 ///
42 /// Stream count is the number of live stream states, independent of stream
43 /// identifier values. It includes locally opened streams.
44 ///
45 /// # Panics
46 ///
47 /// Panics if any limit is zero or a message cannot fit in the byte budget.
48 pub fn new(
49 max_message_size: u32,
50 max_buffered_bytes: u32,
51 max_buffered_chunks: usize,
52 max_streams: usize,
53 ) -> Self {
54 assert!(max_message_size > 0 && max_message_size <= max_buffered_bytes);
55 assert!(max_buffered_chunks > 0 && max_streams > 0);
56 Self {
57 max_message_size,
58 max_buffered_bytes,
59 max_buffered_chunks,
60 max_streams,
61 }
62 }
63 
64 /// Maximum size of an individual received message, enforced in reassembly.
65 pub fn max_message_size(self) -> u32 {
66 self.max_message_size
67 }
68 
69 /// Maximum retained DATA payload bytes across receive queues.
70 pub fn max_buffered_bytes(self) -> u32 {
71 self.max_buffered_bytes
72 }
73 
74 /// Maximum retained DATA fragments across receive queues.
75 pub fn max_buffered_chunks(self) -> usize {
76 self.max_buffered_chunks
77 }
78 
79 /// Maximum live stream states; this does not limit identifier values.
80 pub fn max_streams(self) -> usize {
81 self.max_streams
82 }
83}
84 
85/// Config collects the arguments to create_association construction into
86/// a single structure
87#[derive(Debug)]
88pub struct TransportConfig {
89 max_receive_buffer_size: u32,
90 receive_limits: Option<ReceiveLimits>,
91 max_num_outbound_streams: u16,
92 max_num_inbound_streams: u16,
93 
94 /// Maximum message size we will SEND (respects remote's advertised limit)
95 /// Can be updated after association creation via set_max_send_message_size()
96 max_send_message_size: u32,
97 
98 /// Maximum message size we will RECEIVE (what we advertise in SDP)
99 /// Enforced during reassembly - messages exceeding this are rejected
100 max_receive_message_size: u32,
101 
102 /// Maximum number of retransmissions for INIT chunks during handshake.
103 /// Set to `None` for unlimited retries (recommended for WebRTC).
104 /// Default: Some(8)
105 max_init_retransmits: Option<usize>,
106 
107 /// Maximum number of retransmissions for DATA chunks.
108 /// Set to `None` for unlimited retries (recommended for WebRTC).
109 /// Default: None (unlimited)
110 max_data_retransmits: Option<usize>,
111 
112 /// Initial retransmission timeout in milliseconds.
113 /// Default: 3000
114 rto_initial_ms: u64,
115 
116 /// Minimum retransmission timeout in milliseconds.
117 /// Default: 1000
118 rto_min_ms: u64,
119 
120 /// Maximum retransmission timeout in milliseconds.
121 /// Default: 60000
122 rto_max_ms: u64,
123}
124 
125impl Default for TransportConfig {
126 fn default() -> Self {
127 TransportConfig {
128 max_receive_buffer_size: INITIAL_RECV_BUF_SIZE,
129 receive_limits: None,
130 max_send_message_size: DEFAULT_MAX_MESSAGE_SIZE,
131 max_receive_message_size: DEFAULT_MAX_MESSAGE_SIZE,
132 max_num_outbound_streams: u16::MAX,
133 max_num_inbound_streams: u16::MAX,
134 max_init_retransmits: Some(DEFAULT_MAX_INIT_RETRANS),
135 max_data_retransmits: None,
136 rto_initial_ms: RTO_INITIAL,
137 rto_min_ms: RTO_MIN,
138 rto_max_ms: RTO_MAX,
139 }
140 }
141}
142 
143impl TransportConfig {
144 /// Set an optional hard policy for retained inbound DATA state.
145 ///
146 /// Also sets the advertised receive window and per-message limit. The hard
147 /// policy is disabled by default, preserving ordinary SCTP window behavior.
148 pub fn with_receive_limits(mut self, limits: ReceiveLimits) -> Self {
149 self.max_receive_buffer_size = limits.max_buffered_bytes;
150 self.max_receive_message_size = limits.max_message_size;
151 self.receive_limits = Some(limits);
152 self
153 }
154 
155 pub(crate) fn receive_limits(&self) -> Option<ReceiveLimits> {
156 self.receive_limits
157 }
158 
159 pub fn with_max_receive_buffer_size(mut self, value: u32) -> Self {
160 self.max_receive_buffer_size = value;
161 self
162 }
163 
164 pub fn with_max_send_message_size(mut self, value: u32) -> Self {
165 self.max_send_message_size = value;
166 self
167 }
168 
169 /// Set maximum size of messages we will accept
170 pub fn with_max_receive_message_size(mut self, value: u32) -> Self {
171 self.max_receive_message_size = value;
172 self
173 }
174 
175 #[deprecated(note = "Use with_max_send_message_size instead")]
176 pub fn with_max_message_size(self, value: u32) -> Self {
177 self.with_max_send_message_size(value)
178 }
179 
180 pub fn with_max_num_outbound_streams(mut self, value: u16) -> Self {
181 self.max_num_outbound_streams = value;
182 self
183 }
184 
185 pub fn with_max_num_inbound_streams(mut self, value: u16) -> Self {
186 self.max_num_inbound_streams = value;
187 self
188 }
189 
190 pub(crate) fn max_receive_buffer_size(&self) -> u32 {
191 self.receive_limits
192 .map_or(self.max_receive_buffer_size, |limits| {
193 self.max_receive_buffer_size.min(limits.max_buffered_bytes)
194 })
195 }
196 
197 pub(crate) fn max_send_message_size(&self) -> u32 {
198 self.max_send_message_size
199 }
200 
201 pub(crate) fn max_receive_message_size(&self) -> u32 {
202 self.receive_limits
203 .map_or(self.max_receive_message_size, |limits| {
204 self.max_receive_message_size.min(limits.max_message_size)
205 })
206 }
207 
208 pub(crate) fn max_num_outbound_streams(&self) -> u16 {
209 self.max_num_outbound_streams
210 }
211 
212 pub(crate) fn max_num_inbound_streams(&self) -> u16 {
213 self.max_num_inbound_streams
214 }
215 
216 /// Set maximum INIT retransmissions. `None` means unlimited.
217 pub fn with_max_init_retransmits(mut self, value: Option<usize>) -> Self {
218 self.max_init_retransmits = value;
219 self
220 }
221 
222 /// Set maximum DATA retransmissions. `None` means unlimited.
223 pub fn with_max_data_retransmits(mut self, value: Option<usize>) -> Self {
224 self.max_data_retransmits = value;
225 self
226 }
227 
228 /// Set initial RTO in milliseconds.
229 pub fn with_rto_initial_ms(mut self, value: u64) -> Self {
230 self.rto_initial_ms = value;
231 self
232 }
233 
234 /// Set minimum RTO in milliseconds.
235 pub fn with_rto_min_ms(mut self, value: u64) -> Self {
236 self.rto_min_ms = value;
237 self
238 }
239 
240 /// Set maximum RTO in milliseconds.
241 pub fn with_rto_max_ms(mut self, value: u64) -> Self {
242 self.rto_max_ms = value;
243 self
244 }
245 
246 pub(crate) fn max_init_retransmits(&self) -> Option<usize> {
247 self.max_init_retransmits
248 }
249 
250 pub(crate) fn max_data_retransmits(&self) -> Option<usize> {
251 self.max_data_retransmits
252 }
253 
254 pub(crate) fn rto_initial_ms(&self) -> u64 {
255 self.rto_initial_ms
256 }
257 
258 pub(crate) fn rto_min_ms(&self) -> u64 {
259 self.rto_min_ms
260 }
261 
262 pub(crate) fn rto_max_ms(&self) -> u64 {
263 self.rto_max_ms
264 }
265}
266 
267/// Global configuration for the endpoint, affecting all associations
268///
269/// Default values should be suitable for most internet applications.
270#[derive(Clone)]
271pub struct EndpointConfig {
272 pub(crate) max_payload_size: u32,
273 
274 /// AID generator factory
275 ///
276 /// Create a aid generator for local aid in Endpoint struct
277 pub(crate) aid_generator_factory:
278 Arc<dyn Fn() -> Box<dyn AssociationIdGenerator> + Send + Sync>,
279}
280 
281impl Default for EndpointConfig {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286 
287impl EndpointConfig {
288 /// Create a default config
289 pub fn new() -> Self {
290 let aid_factory: fn() -> Box<dyn AssociationIdGenerator> =
291 || Box::<RandomAssociationIdGenerator>::default();
292 Self {
293 max_payload_size: INITIAL_MTU - (COMMON_HEADER_SIZE + DATA_CHUNK_HEADER_SIZE),
294 aid_generator_factory: Arc::new(aid_factory),
295 }
296 }
297 
298 /// Supply a custom Association ID generator factory
299 ///
300 /// Called once by each `Endpoint` constructed from this configuration to obtain the AID
301 /// generator which will be used to generate the AIDs used for incoming packets on all
302 /// associations involving that `Endpoint`. A custom AID generator allows applications to embed
303 /// information in local association IDs, e.g. to support stateless packet-level load balancers.
304 ///
305 /// `EndpointConfig::new()` applies a default random AID generator factory. This functions
306 /// accepts any customized AID generator to reset AID generator factory that implements
307 /// the `AssociationIdGenerator` trait.
308 pub fn aid_generator<F: Fn() -> Box<dyn AssociationIdGenerator> + Send + Sync + 'static>(
309 &mut self,
310 factory: F,
311 ) -> &mut Self {
312 self.aid_generator_factory = Arc::new(factory);
313 self
314 }
315 
316 /// Maximum payload size accepted from peers.
317 ///
318 /// The default is suitable for typical internet applications. Applications which expect to run
319 /// on networks supporting Ethernet jumbo frames or similar should set this appropriately.
320 pub fn max_payload_size(&mut self, value: u32) -> &mut Self {
321 self.max_payload_size = value;
322 self
323 }
324 
325 /// Get the current value of `max_payload_size`
326 ///
327 /// While most parameters don't need to be readable, this must be exposed to allow higher-level
328 /// layers to determine how large a receive buffer to allocate to
329 /// support an externally-defined `EndpointConfig`.
330 ///
331 /// While `get_` accessors are typically unidiomatic in Rust, we favor concision for setters,
332 /// which will be used far more heavily.
333 #[doc(hidden)]
334 pub fn get_max_payload_size(&self) -> u32 {
335 self.max_payload_size
336 }
337}
338 
339impl fmt::Debug for EndpointConfig {
340 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
341 fmt.debug_struct("EndpointConfig")
342 .field("max_payload_size", &self.max_payload_size)
343 .field("aid_generator_factory", &"[ elided ]")
344 .finish()
345 }
346}
347 
348/// Parameters governing incoming associations
349///
350/// Default values should be suitable for most internet applications.
351#[derive(Debug, Clone)]
352pub struct ServerConfig {
353 /// Transport configuration to use for incoming associations
354 pub transport: Arc<TransportConfig>,
355 
356 /// Maximum number of concurrent associations
357 pub(crate) concurrent_associations: u32,
358}
359 
360impl Default for ServerConfig {
361 fn default() -> Self {
362 ServerConfig {
363 transport: Arc::new(TransportConfig::default()),
364 concurrent_associations: 100_000,
365 }
366 }
367}
368 
369impl ServerConfig {
370 /// Create a default config with a particular handshake token key
371 pub fn new() -> Self {
372 ServerConfig::default()
373 }
374}
375 
376/// Default SCTP source/destination port (conventional for WebRTC data channels).
377pub const DEFAULT_SCTP_PORT: u16 = 5000;
378 
379/// Maximum allowed size (in bytes) of a serialized SNAP token (INIT chunk)
380/// accepted via out-of-band negotiation. A typical token is well under
381/// 100 bytes; this limit prevents accidentally feeding megabytes of
382/// untrusted signaling data into the parser.
383pub const MAX_SNAP_INIT_BYTES: usize = 2048;
384 
385/// Configuration for outgoing associations.
386///
387/// Default values should be suitable for most internet applications.
388#[derive(Debug, Clone)]
389pub struct ClientConfig {
390 /// Transport configuration to use
391 pub transport: Arc<TransportConfig>,
392 /// Local SNAP token (INIT chunk) bytes.
393 ///
394 /// Generated via [`generate_snap_token`]. When both `local_sctp_init` and
395 /// `remote_sctp_init` are set, the association skips the SCTP 4-way
396 /// handshake (RFC 4960 Section 5.1) and immediately transitions to the
397 /// ESTABLISHED state.
398 ///
399 /// If only one side is set (e.g. the peer does not support SNAP), the
400 /// association falls back to the normal SCTP handshake.
401 ///
402 /// See [draft-hancke-tsvwg-snap-01](https://datatracker.ietf.org/doc/draft-hancke-tsvwg-snap/).
403 pub(crate) local_sctp_init: Option<Bytes>,
404 /// Remote SNAP token (INIT chunk) bytes.
405 ///
406 /// Received from the peer via a signaling channel (e.g., SDP `a=sctp-init`
407 /// attribute). Must be provided together with `local_sctp_init` to enable
408 /// SNAP.
409 ///
410 /// See [draft-hancke-tsvwg-snap-01](https://datatracker.ietf.org/doc/draft-hancke-tsvwg-snap/).
411 pub(crate) remote_sctp_init: Option<Bytes>,
412}
413 
414impl Default for ClientConfig {
415 fn default() -> Self {
416 ClientConfig {
417 transport: Arc::new(TransportConfig::default()),
418 local_sctp_init: None,
419 remote_sctp_init: None,
420 }
421 }
422}
423 
424impl ClientConfig {
425 /// Create a default config with a particular cryptographic config
426 pub fn new() -> Self {
427 ClientConfig::default()
428 }
429 
430 /// Enable SNAP (SCTP Negotiation Acceleration Protocol).
431 ///
432 /// Both a local and remote SNAP token (INIT chunk) must be provided.
433 /// The local token should be generated via [`generate_snap_token`] and
434 /// exchanged with the remote peer through a signaling channel (e.g.,
435 /// SDP `a=sctp-init` attribute). The remote token is the peer's
436 /// corresponding bytes received via signaling.
437 ///
438 /// When both are set, the association skips the SCTP 4-way handshake
439 /// (RFC 4960 Section 5.1) and immediately transitions to the ESTABLISHED
440 /// state.
441 ///
442 /// **Note:** When using SNAP, **both** peers must call
443 /// [`Endpoint::connect`](crate::Endpoint::connect) — there is no
444 /// server-side SNAP via [`Endpoint::handle`](crate::Endpoint::handle).
445 ///
446 /// See [draft-hancke-tsvwg-snap-01](https://datatracker.ietf.org/doc/draft-hancke-tsvwg-snap/).
447 pub fn with_snap(mut self, local_sctp_init: Bytes, remote_sctp_init: Bytes) -> Self {
448 self.local_sctp_init = Some(local_sctp_init);
449 self.remote_sctp_init = Some(remote_sctp_init);
450 self
451 }
452}
453 
454/// Generate a SNAP token (INIT chunk) for out-of-band negotiation.
455///
456/// Creates a serialized SCTP INIT **chunk** (not a full SCTP packet — no
457/// common header or IP/UDP framing) with random `initiate_tag` and
458/// `initial_tsn` values, using the receiver window from the provided
459/// [`TransportConfig`]. Stream counts are set to `u16::MAX` so that the
460/// actual limit is determined by the peer's offer during negotiation.
461///
462/// The returned bytes are suitable for exchange via a signaling channel
463/// (e.g., SDP `a=sctp-init`) as described in
464/// [draft-hancke-tsvwg-snap-01](https://datatracker.ietf.org/doc/draft-hancke-tsvwg-snap/).
465///
466/// Each call generates fresh random values. The caller must hold onto the
467/// returned bytes and pass them to [`ClientConfig::with_snap`] alongside
468/// the remote peer's token.
469pub fn generate_snap_token(config: &TransportConfig) -> Result<Bytes, crate::error::Error> {
470 use crate::chunk::{Chunk, chunk_init::ChunkInit};
471 use core::num::NonZeroU32;
472 use rand::random;
473 
474 let mut init = ChunkInit {
475 initiate_tag: random::<NonZeroU32>().get(),
476 initial_tsn: random::<NonZeroU32>().get(),
477 num_outbound_streams: u16::MAX,
478 num_inbound_streams: u16::MAX,
479 advertised_receiver_window_credit: config.max_receive_buffer_size(),
480 ..Default::default()
481 };
482 init.set_supported_extensions();
483 init.check()?;
484 init.marshal()
485}