Skip to content

Commit cd3dde2

Browse files
authored
Make MTU size configurable
1 parent 9d99f9f commit cd3dde2

30 files changed

Lines changed: 653 additions & 70 deletions

File tree

crates/is/src/agent.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::{HashSet, VecDeque};
22
use std::fmt;
33
use std::net::SocketAddr;
4+
use std::ops::RangeInclusive;
45
use std::panic::{RefUnwindSafe, UnwindSafe};
56
use std::sync::Arc;
67
use std::time::{Duration, Instant};
@@ -11,7 +12,7 @@ use crate::Sha1HmacProvider;
1112
use crate::preference::default_local_preference;
1213
use crate::stun::{Class as StunClass, Method as StunMethod, StunTiming};
1314
use crate::stun::{StunMessage, StunPacket, TransId};
14-
use str0m_proto::{DATAGRAM_MTU, DATAGRAM_MTU_WARN, Id, Transmit};
15+
use str0m_proto::{DATAGRAM_MTU_TARGET, DATAGRAM_MTU_WARN, Id, Transmit};
1516
use str0m_proto::{NonCryptographicRng, Pii, Protocol};
1617

1718
use crate::candidate::{Candidate, CandidateKind};
@@ -106,6 +107,10 @@ pub struct IceAgent {
106107

107108
/// Pluggable calculation of local preference.
108109
local_preference: LocalPreferenceHolder,
110+
111+
/// Target MTU (start) and warn threshold (end). Buffer sizing uses the
112+
/// target; oversized outgoing datagrams above the warn threshold log a warning.
113+
mtu: RangeInclusive<usize>,
109114
}
110115

111116
/// IceAgent contains only static references to thread-safe traits,
@@ -341,9 +346,25 @@ impl IceAgent {
341346
timing_config: StunTiming::default(),
342347
local_preference: LocalPreferenceHolder(Arc::new(default_local_preference)),
343348
sha1_hmac_provider,
349+
mtu: DATAGRAM_MTU_TARGET..=DATAGRAM_MTU_WARN,
344350
}
345351
}
346352

353+
/// Set the UDP datagram MTU range used for sizing internal buffers (target..=warn).
354+
pub fn set_mtu(&mut self, mtu: RangeInclusive<usize>) {
355+
self.mtu = mtu;
356+
}
357+
358+
/// Target MTU used for sizing outgoing STUN buffers.
359+
pub fn mtu(&self) -> usize {
360+
*self.mtu.start()
361+
}
362+
363+
/// Threshold above which an outgoing datagram triggers an MTU warning.
364+
pub fn mtu_warn(&self) -> usize {
365+
*self.mtu.end()
366+
}
367+
347368
/// Sets the control tie breaker of this agent.
348369
///
349370
/// By default, this is randomly generated and per the ICE spec,
@@ -1265,8 +1286,8 @@ impl IceAgent {
12651286
pub fn poll_transmit(&mut self) -> Option<Transmit> {
12661287
let x = self.transmit.pop_front();
12671288
if let Some(x) = &x {
1268-
if x.contents.len() > DATAGRAM_MTU_WARN {
1269-
warn!("ICE above MTU {}: {}", DATAGRAM_MTU_WARN, x.contents.len());
1289+
if x.contents.len() > self.mtu_warn() {
1290+
warn!("ICE above MTU {}: {}", self.mtu_warn(), x.contents.len());
12701291
}
12711292
trace!("Poll transmit: {:?}", x);
12721293
}
@@ -1601,7 +1622,7 @@ impl IceAgent {
16011622
local_addr, remote_addr, reply
16021623
);
16031624

1604-
let mut buf = vec![0_u8; DATAGRAM_MTU];
1625+
let mut buf = vec![0_u8; self.mtu()];
16051626

16061627
let sha1_hmac =
16071628
|key: &[u8], payloads: &[&[u8]]| self.sha1_hmac_provider.sha1_hmac(key, payloads);
@@ -1632,7 +1653,7 @@ impl IceAgent {
16321653
req.source,
16331654
);
16341655

1635-
let mut buf = vec![0_u8; DATAGRAM_MTU];
1656+
let mut buf = vec![0_u8; self.mtu()];
16361657

16371658
let sha1_hmac =
16381659
|key: &[u8], payloads: &[&[u8]]| self.sha1_hmac_provider.sha1_hmac(key, payloads);
@@ -1707,7 +1728,7 @@ impl IceAgent {
17071728
binding
17081729
);
17091730

1710-
let mut buf = vec![0_u8; DATAGRAM_MTU];
1731+
let mut buf = vec![0_u8; self.mtu()];
17111732

17121733
let sha1_hmac =
17131734
|key: &[u8], payloads: &[&[u8]]| self.sha1_hmac_provider.sha1_hmac(key, payloads);
@@ -2672,7 +2693,7 @@ mod test {
26722693
/// Serializing will calculate a message integrity for it. You can then re-parse to get a message
26732694
/// that contains that correct integrity value.
26742695
fn serialize_stun_msg(msg: StunMessage<'_>, password: &str) -> Vec<u8> {
2675-
let mut buf = vec![0_u8; DATAGRAM_MTU];
2696+
let mut buf = vec![0_u8; DATAGRAM_MTU_TARGET];
26762697

26772698
let sha1_hmac =
26782699
|key: &[u8], payloads: &[&[u8]]| DefaultSha1HmacProvider.sha1_hmac(key, payloads);
@@ -2683,4 +2704,15 @@ mod test {
26832704

26842705
buf
26852706
}
2707+
2708+
#[test]
2709+
fn set_mtu_updates_mtu() {
2710+
let mut agent = IceAgent::new(IceCreds::new());
2711+
assert_eq!(agent.mtu(), DATAGRAM_MTU_TARGET);
2712+
assert_eq!(agent.mtu_warn(), DATAGRAM_MTU_WARN);
2713+
2714+
agent.set_mtu(900..=1280);
2715+
assert_eq!(agent.mtu(), 900);
2716+
assert_eq!(agent.mtu_warn(), 1280);
2717+
}
26862718
}

crates/proto/src/crypto/provider.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,15 @@ pub trait DtlsProvider: CryptoSafe {
147147
fn generate_certificate(&self) -> Option<DtlsCert>;
148148

149149
/// Create a new DTLS instance with the given certificate.
150+
///
151+
/// mtu: Backends that can size their record layer according to the provided MTU should do so;
152+
/// backends that cannot ignore it. `None` means use the backend's default.
150153
fn new_dtls(
151154
&self,
152155
cert: &DtlsCert,
153156
now: Instant,
154157
dtls_version: DtlsVersion,
158+
mtu: Option<usize>,
155159
) -> Result<Box<dyn DtlsInstance>, CryptoError>;
156160

157161
/// Whether the provider is used in a test context.

crates/proto/src/lib.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
//! Shared protocol types and traits for str0m.
22
3-
/// Targeted MTU
4-
pub const DATAGRAM_MTU: usize = 1150;
3+
/// Default target MTU when no MTU is explicitly configured via [`RtcConfig::set_mtu`].
4+
pub const DATAGRAM_MTU_TARGET: usize = 1150;
55

6-
/// Warn if any packet we are about to send is above this size.
6+
/// Lower bound for the target MTU.
7+
pub const DATAGRAM_MTU_TARGET_MIN: usize = 576;
8+
9+
/// Upper bound for the target MTU.
10+
pub const DATAGRAM_MTU_TARGET_MAX: usize = 1500;
11+
12+
/// Default warning threshold for MTU when no warning threshold is configured via [`RtcConfig::set_mtu`].
713
pub const DATAGRAM_MTU_WARN: usize = 1280;
814

915
mod bandwidth;

crypto/apple-crypto/src/dtls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ impl DtlsProvider for AppleCryptoDtlsProvider {
8989
cert: &DtlsCert,
9090
now: Instant,
9191
dtls_version: DtlsVersion,
92+
mtu: Option<usize>,
9293
) -> Result<Box<dyn DtlsInstance>, CryptoError> {
9394
let dimpl_cert = DtlsCertificate {
9495
certificate: cert.certificate.clone(),
@@ -98,6 +99,9 @@ impl DtlsProvider for AppleCryptoDtlsProvider {
9899
// Create a dimpl Config with Apple CommonCrypto crypto provider
99100
// ICE verifies return routability before DTLS, making server cookies redundant.
100101
let mut builder = Config::builder().use_server_cookie(false);
102+
if let Some(mtu) = mtu {
103+
builder = builder.mtu(mtu);
104+
}
101105
if self.is_test() {
102106
// We need the DTLS impl to be deterministic for the BWE tests.
103107
builder = builder.dangerously_set_rng_seed(42);

crypto/aws-lc-rs/src/dtls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ impl DtlsProvider for AwsLcRsDtlsProvider {
3030
cert: &DtlsCert,
3131
now: Instant,
3232
dtls_version: DtlsVersion,
33+
mtu: Option<usize>,
3334
) -> Result<Box<dyn DtlsInstance>, CryptoError> {
3435
let dimpl_cert = dimpl::DtlsCertificate {
3536
certificate: cert.certificate.clone(),
@@ -39,6 +40,9 @@ impl DtlsProvider for AwsLcRsDtlsProvider {
3940
// Create a default dimpl Config with AWS-LC-RS crypto provider
4041
// ICE verifies return routability before DTLS, making server cookies redundant.
4142
let mut builder = dimpl::Config::builder().use_server_cookie(false);
43+
if let Some(mtu) = mtu {
44+
builder = builder.mtu(mtu);
45+
}
4246
if self.is_test() {
4347
// We need the DTLS impl to be deterministic for the BWE tests.
4448
builder = builder.dangerously_set_rng_seed(42);

crypto/openssl/src/dtls_dimpl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl DtlsProvider for OsslDtlsProvider {
2929
cert: &DtlsCert,
3030
now: Instant,
3131
dtls_version: DtlsVersion,
32+
mtu: Option<usize>,
3233
) -> Result<Box<dyn DtlsInstance>, CryptoError> {
3334
let dimpl_cert = dimpl::DtlsCertificate {
3435
certificate: cert.certificate.clone(),
@@ -37,6 +38,9 @@ impl DtlsProvider for OsslDtlsProvider {
3738

3839
// ICE verifies return routability before DTLS, making server cookies redundant.
3940
let mut builder = dimpl::Config::builder().use_server_cookie(false);
41+
if let Some(mtu) = mtu {
42+
builder = builder.mtu(mtu);
43+
}
4044
if self.is_test() {
4145
// We need the DTLS impl to be deterministic for the BWE tests.
4246
builder = builder.dangerously_set_rng_seed(42);

crypto/openssl/src/dtls_ossl.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ use openssl::ssl::{SslContext, SslContextBuilder, SslMethod};
1212
use openssl::ssl::{SslOptions, SslStream, SslVerifyMode};
1313
use openssl::x509::X509;
1414

15+
use str0m_proto::DATAGRAM_MTU_TARGET;
1516
use str0m_proto::crypto::dtls::{DtlsCert, KeyingMaterial, SrtpProfile};
1617
use str0m_proto::crypto::dtls::{DtlsImplError, DtlsInstance, DtlsOutput, DtlsProvider};
1718
use str0m_proto::crypto::{CryptoError, DtlsVersion};
18-
use str0m_proto::{DATAGRAM_MTU, DATAGRAM_MTU_WARN};
1919

2020
// ============================================================================
2121
// IO Buffer
@@ -290,9 +290,9 @@ struct OsslDtlsImpl {
290290
}
291291

292292
impl OsslDtlsImpl {
293-
fn new(cert: &DtlsCert) -> Result<Self, CryptoError> {
293+
fn new(cert: &DtlsCert, mtu: usize) -> Result<Self, CryptoError> {
294294
let context = dtls_create_ctx(cert)?;
295-
let ssl = dtls_ssl_create(&context)?;
295+
let ssl = dtls_ssl_create(&context, mtu)?;
296296
Ok(OsslDtlsImpl {
297297
tls: TlsStream::new(ssl, IoBuffer::default()),
298298
})
@@ -347,9 +347,6 @@ impl OsslDtlsImpl {
347347
fn poll_datagram(&mut self) -> Option<Vec<u8>> {
348348
let x = self.tls.inner_mut().pop_outgoing();
349349
if let Some(x) = &x {
350-
if x.len() > DATAGRAM_MTU_WARN {
351-
warn!("DTLS above MTU {}: {}", DATAGRAM_MTU_WARN, x.len());
352-
}
353350
trace!("Poll datagram: {}", x.len());
354351
}
355352
x
@@ -407,9 +404,9 @@ fn dtls_create_ctx(cert: &DtlsCert) -> Result<SslContext, CryptoError> {
407404
Ok(ctx.build())
408405
}
409406

410-
fn dtls_ssl_create(ctx: &SslContext) -> Result<Ssl, CryptoError> {
407+
fn dtls_ssl_create(ctx: &SslContext, mtu: usize) -> Result<Ssl, CryptoError> {
411408
let mut ssl = Ssl::new(ctx)?;
412-
ssl.set_mtu(DATAGRAM_MTU as u32)?;
409+
ssl.set_mtu(mtu as u32)?;
413410
Ok(ssl)
414411
}
415412

@@ -446,8 +443,8 @@ impl std::fmt::Debug for OsslDtlsInstance {
446443
}
447444

448445
impl OsslDtlsInstance {
449-
pub(super) fn new(cert: &DtlsCert) -> Result<Self, CryptoError> {
450-
let inner = OsslDtlsImpl::new(cert)?;
446+
pub(super) fn new(cert: &DtlsCert, mtu: usize) -> Result<Self, CryptoError> {
447+
let inner = OsslDtlsImpl::new(cert, mtu)?;
451448
Ok(Self {
452449
inner,
453450
pending_packets: VecDeque::new(),
@@ -638,9 +635,11 @@ impl DtlsProvider for OsslDtlsProvider {
638635
cert: &DtlsCert,
639636
_now: Instant,
640637
dtls_version: DtlsVersion,
638+
mtu: Option<usize>,
641639
) -> Result<Box<dyn DtlsInstance>, CryptoError> {
640+
let mtu = mtu.unwrap_or(DATAGRAM_MTU_TARGET);
642641
match dtls_version {
643-
DtlsVersion::Dtls12 => Ok(Box::new(OsslDtlsInstance::new(cert)?)),
642+
DtlsVersion::Dtls12 => Ok(Box::new(OsslDtlsInstance::new(cert, mtu)?)),
644643
_ => Err(CryptoError::Other(
645644
"OpenSSL DTLS provider only supports DTLS 1.2 without dimpl. \
646645
Enable the openssl-dimpl feature for DTLS 1.3/Auto."

crypto/rust-crypto/src/dtls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ impl DtlsProvider for RustCryptoDtlsProvider {
3030
cert: &DtlsCert,
3131
now: Instant,
3232
dtls_version: DtlsVersion,
33+
mtu: Option<usize>,
3334
) -> Result<Box<dyn DtlsInstance>, CryptoError> {
3435
let dimpl_cert = dimpl::DtlsCertificate {
3536
certificate: cert.certificate.clone(),
@@ -39,6 +40,9 @@ impl DtlsProvider for RustCryptoDtlsProvider {
3940
// Create a default dimpl Config with RustCrypto crypto provider
4041
// ICE verifies return routability before DTLS, making server cookies redundant.
4142
let mut builder = dimpl::Config::builder().use_server_cookie(false);
43+
if let Some(mtu) = mtu {
44+
builder = builder.mtu(mtu);
45+
}
4246
if self.is_test() {
4347
// We need the DTLS impl to be deterministic for the BWE tests.
4448
builder = builder.dangerously_set_rng_seed(42);

crypto/wincrypto/src/dtls_dimpl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ impl DtlsProvider for WinCryptoDtlsProvider {
2626
cert: &DtlsCert,
2727
now: Instant,
2828
dtls_version: DtlsVersion,
29+
mtu: Option<usize>,
2930
) -> Result<Box<dyn DtlsInstance>, CryptoError> {
3031
let dimpl_cert = DtlsCertificate {
3132
certificate: cert.certificate.clone(),
@@ -34,6 +35,9 @@ impl DtlsProvider for WinCryptoDtlsProvider {
3435

3536
// ICE verifies return routability before DTLS, making server cookies redundant.
3637
let mut builder = Config::builder().use_server_cookie(false);
38+
if let Some(mtu) = mtu {
39+
builder = builder.mtu(mtu);
40+
}
3741
if self.is_test() {
3842
builder = builder.dangerously_set_rng_seed(42);
3943
}

crypto/wincrypto/src/dtls_schannel.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::collections::{HashMap, VecDeque};
44
use std::sync::{Arc, LazyLock, Mutex};
55
use std::time::{Duration, Instant};
6+
use str0m_proto::DATAGRAM_MTU_TARGET;
67
use str0m_proto::crypto::dtls::{DtlsCert, DtlsImplError, DtlsInstance, DtlsOutput, DtlsProvider};
78
use str0m_proto::crypto::dtls::{KeyingMaterial, SrtpProfile};
89
use str0m_proto::crypto::{CryptoError, DtlsVersion};
@@ -43,6 +44,7 @@ impl DtlsProvider for WinCryptoDtlsProvider {
4344
cert: &DtlsCert,
4445
_now: Instant,
4546
dtls_version: DtlsVersion,
47+
mtu: Option<usize>,
4648
) -> Result<Box<dyn DtlsInstance>, CryptoError> {
4749
if !matches!(dtls_version, DtlsVersion::Dtls12 | DtlsVersion::Auto) {
4850
return Err(CryptoError::Other(
@@ -64,8 +66,12 @@ impl DtlsProvider for WinCryptoDtlsProvider {
6466
)
6567
})?;
6668

67-
let dtls =
68-
Dtls::new(win_cert).map_err(|e| CryptoError::Other(format!("DTLS creation: {}", e)))?;
69+
let mtu = mtu.unwrap_or(DATAGRAM_MTU_TARGET);
70+
let mtu_u16 = u16::try_from(mtu).map_err(|_| {
71+
CryptoError::Other(format!("MTU {} does not fit in u16 for SChannel", mtu))
72+
})?;
73+
let dtls = Dtls::new(win_cert, mtu_u16)
74+
.map_err(|e| CryptoError::Other(format!("DTLS creation: {}", e)))?;
6975

7076
Ok(Box::new(WinCryptoDtlsInstance {
7177
dtls,

0 commit comments

Comments
 (0)