Skip to content
File

Blob: firmware/vendor/str0m/tests/mtu-compliance.rs

rust111 lines
1use std::net::Ipv4Addr;
2use std::time::{Duration, Instant};
3 
4use str0m::format::Codec;
5use str0m::media::{Direction, MediaKind};
6use str0m::{DATAGRAM_MTU_TARGET_MAX, DATAGRAM_MTU_TARGET_MIN, Event, Rtc, RtcError};
7 
8mod common;
9use common::{Peer, TestRtc, init_crypto_default, init_log, progress_strict_mtu};
10 
11/// End-to-end MTU compliance: walk a full ICE/DTLS/SCTP/SRTP session and
12/// assert every outgoing datagram on either side has `len() <= mtu`. The
13/// check fires inside the test pump via [`progress_strict_mtu`], so any
14/// oversized datagram fails the test at the source.
15fn run_mtu_compliance(mtu: usize) -> Result<(), RtcError> {
16 init_log();
17 init_crypto_default();
18 
19 let now = Instant::now();
20 let l_rtc = Rtc::builder().set_mtu(mtu..=mtu).build(now);
21 let r_rtc = Rtc::builder().set_mtu(mtu..=mtu).build(now);
22 
23 let mut l = TestRtc::new_with_rtc(Peer::Left.span(), l_rtc);
24 let mut r = TestRtc::new_with_rtc(Peer::Right.span(), r_rtc);
25 
26 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
27 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
28 
29 // Add an audio m-line and a data channel in the same offer.
30 let mut change = l.sdp_api();
31 let audio_mid = change.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None);
32 let cid = change.add_channel("mtu-test".into());
33 let (offer, pending) = change.apply().unwrap();
34 let answer = r.rtc.sdp_api().accept_offer(offer)?;
35 l.rtc.sdp_api().accept_answer(pending, answer)?;
36 
37 // ICE + DTLS handshake under strict MTU.
38 loop {
39 if l.is_connected() && r.is_connected() {
40 break;
41 }
42 progress_strict_mtu(&mut l, &mut r, mtu)?;
43 }
44 
45 let max = l.last.max(r.last);
46 l.last = max;
47 r.last = max;
48 
49 // Wait for the data channel to open on L.
50 while l.channel(cid).is_none() {
51 progress_strict_mtu(&mut l, &mut r, mtu)?;
52 }
53 
54 // Small data-channel send.
55 l.channel(cid)
56 .unwrap()
57 .write(false, b"hello mtu world")
58 .expect("small write");
59 
60 // Large data-channel send to exercise SCTP fragmentation.
61 let big = vec![0xCDu8; 32 * 1024];
62 l.channel(cid)
63 .unwrap()
64 .write(true, &big)
65 .expect("large write");
66 
67 // Audio writes (Opus) at typical 20 ms cadence.
68 let params = l.params_opus();
69 assert_eq!(params.spec().codec, Codec::Opus);
70 let pt = params.pt();
71 let audio_payload = vec![0xAAu8; 160];
72 
73 let deadline = Duration::from_secs(2);
74 while l.duration() < deadline {
75 let wallclock = l.start + l.duration();
76 let time = l.duration().into();
77 if let Some(w) = l.writer(audio_mid) {
78 w.write(pt, wallclock, time, audio_payload.clone())?;
79 }
80 progress_strict_mtu(&mut l, &mut r, mtu)?;
81 }
82 
83 // Sanity: r received the small + part of the large data-channel send.
84 let chan_events = r
85 .events
86 .iter()
87 .filter(|(_, e)| matches!(e, Event::ChannelData(_)))
88 .count();
89 assert!(
90 chan_events >= 1,
91 "expected at least one ChannelData event at r, got {chan_events}"
92 );
93 
94 Ok(())
95}
96 
97#[test]
98fn mtu_compliance_min() -> Result<(), RtcError> {
99 run_mtu_compliance(DATAGRAM_MTU_TARGET_MIN)
100}
101 
102#[test]
103fn mtu_compliance_mid() -> Result<(), RtcError> {
104 run_mtu_compliance((DATAGRAM_MTU_TARGET_MIN + DATAGRAM_MTU_TARGET_MAX) / 2)
105}
106 
107#[test]
108fn mtu_compliance_max() -> Result<(), RtcError> {
109 run_mtu_compliance(DATAGRAM_MTU_TARGET_MAX)
110}