1use std::{collections::HashMap, sync::Arc};
2
3use crate::channel::ChannelId;
4use crate::ieee80211::{FrameLayout, WifiFrame};
5use crate::pipeline::{
6 MockPayloadPipeline, PayloadPipeline, PayloadPipelineEvent, RecoveredPayload,
7};
8use crate::wfb::{FecCounters, WfbError, WfbKeypair};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct PayloadRouteId(u64);
13
14impl PayloadRouteId {
15 pub const fn new(raw: u64) -> Self {
17 Self(raw)
18 }
19
20 pub const fn raw(self) -> u64 {
22 self.0
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub struct PayloadRuntimeKey {
32 channel_id: ChannelId,
33 key_slot: u64,
34}
35
36impl PayloadRuntimeKey {
37 pub const fn new(channel_id: ChannelId, key_slot: u64) -> Self {
39 Self {
40 channel_id,
41 key_slot,
42 }
43 }
44
45 pub const fn channel_id(self) -> ChannelId {
47 self.channel_id
48 }
49
50 pub const fn key_slot(self) -> u64 {
52 self.key_slot
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
58pub enum PayloadRouteEvent {
59 IgnoredFrame,
61 SessionEstablished {
63 runtime: PayloadRuntimeKey,
65 route_ids: Arc<[PayloadRouteId]>,
67 epoch: u64,
69 fec_k: usize,
71 fec_n: usize,
73 },
74 Payload {
76 runtime: PayloadRuntimeKey,
78 route_ids: Arc<[PayloadRouteId]>,
80 payload: RecoveredPayload,
82 },
83}
84
85#[derive(Debug, PartialEq, Eq)]
87pub enum PayloadRouteError {
88 UnknownRuntime(PayloadRuntimeKey),
90 Wfb(WfbError),
92}
93
94impl std::fmt::Display for PayloadRouteError {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 Self::UnknownRuntime(key) => write!(
98 f,
99 "unknown payload runtime for channel 0x{:08x} key slot {}",
100 key.channel_id().raw(),
101 key.key_slot()
102 ),
103 Self::Wfb(err) => std::fmt::Display::fmt(err, f),
104 }
105 }
106}
107
108impl std::error::Error for PayloadRouteError {}
109
110impl From<WfbError> for PayloadRouteError {
111 fn from(err: WfbError) -> Self {
112 Self::Wfb(err)
113 }
114}
115
116#[derive(Debug, Clone)]
117enum PayloadChannelPipeline {
118 Real(Box<PayloadPipeline>),
119 Mock(MockPayloadPipeline),
120}
121
122impl PayloadChannelPipeline {
123 fn channel_id(&self) -> ChannelId {
124 match self {
125 Self::Real(pipeline) => pipeline.channel_id(),
126 Self::Mock(pipeline) => pipeline.channel_id(),
127 }
128 }
129
130 fn fec_counters(&self) -> FecCounters {
131 match self {
132 Self::Real(pipeline) => pipeline.fec_counters(),
133 Self::Mock(pipeline) => pipeline.fec_counters(),
134 }
135 }
136
137 fn accepts_80211_frame(&self, frame: &[u8]) -> bool {
138 match self {
139 Self::Real(pipeline) => pipeline.accepts_80211_frame(frame),
140 Self::Mock(_) => false,
141 }
142 }
143
144 fn push_matched_payload_into(
145 &mut self,
146 payload: &[u8],
147 events: &mut Vec<PayloadPipelineEvent>,
148 ) -> Result<(), WfbError> {
149 match self {
150 Self::Real(pipeline) => pipeline.push_matched_payload_into(payload, events),
151 Self::Mock(_) => {
152 events.clear();
153 events.push(PayloadPipelineEvent::IgnoredFrame);
154 Ok(())
155 }
156 }
157 }
158
159 fn push_decrypted_80211_frame(
160 &mut self,
161 frame: &[u8],
162 decrypted_fragment: &[u8],
163 ) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
164 match self {
165 Self::Real(pipeline) => pipeline.push_decrypted_80211_frame(frame, decrypted_fragment),
166 Self::Mock(_) => Ok(vec![PayloadPipelineEvent::IgnoredFrame]),
167 }
168 }
169
170 fn push_decrypted_fragment(
171 &mut self,
172 data_nonce: u64,
173 decrypted_fragment: &[u8],
174 ) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
175 match self {
176 Self::Real(pipeline) => {
177 pipeline.push_decrypted_fragment(data_nonce, decrypted_fragment)
178 }
179 Self::Mock(pipeline) => Ok(pipeline.push_payload(data_nonce, decrypted_fragment)),
180 }
181 }
182
183 fn push_mock_payload(&mut self, packet_seq: u64, data: &[u8]) -> Vec<PayloadPipelineEvent> {
184 match self {
185 Self::Real(_) => vec![PayloadPipelineEvent::IgnoredFrame],
186 Self::Mock(pipeline) => pipeline.push_payload(packet_seq, data),
187 }
188 }
189}
190
191#[derive(Debug, Clone)]
197pub struct PayloadChannelRuntime {
198 pipeline: PayloadChannelPipeline,
199 route_ids: Arc<[PayloadRouteId]>,
200 pipeline_events: Vec<PayloadPipelineEvent>,
201}
202
203impl PayloadChannelRuntime {
204 fn real(pipeline: PayloadPipeline, route_id: PayloadRouteId) -> Self {
205 Self {
206 pipeline: PayloadChannelPipeline::Real(Box::new(pipeline)),
207 route_ids: Arc::from([route_id]),
208 pipeline_events: Vec::with_capacity(1),
209 }
210 }
211
212 fn mock(channel_id: ChannelId, route_id: PayloadRouteId) -> Self {
213 Self {
214 pipeline: PayloadChannelPipeline::Mock(MockPayloadPipeline::new(channel_id)),
215 route_ids: Arc::from([route_id]),
216 pipeline_events: Vec::with_capacity(1),
217 }
218 }
219
220 pub fn channel_id(&self) -> ChannelId {
222 self.pipeline.channel_id()
223 }
224
225 pub fn route_ids(&self) -> &[PayloadRouteId] {
227 &self.route_ids
228 }
229
230 fn push_route_id(&mut self, route_id: PayloadRouteId) {
231 if self.route_ids.contains(&route_id) {
232 return;
233 }
234 let mut route_ids = self.route_ids.to_vec();
235 route_ids.push(route_id);
236 self.route_ids = route_ids.into();
237 }
238}
239
240#[derive(Debug, Clone)]
246pub struct PayloadRouteManager {
247 frame_layout: FrameLayout,
248 runtimes: HashMap<PayloadRuntimeKey, PayloadChannelRuntime>,
249}
250
251impl PayloadRouteManager {
252 pub fn new(frame_layout: FrameLayout) -> Self {
254 Self {
255 frame_layout,
256 runtimes: HashMap::new(),
257 }
258 }
259
260 pub const fn frame_layout(&self) -> FrameLayout {
262 self.frame_layout
263 }
264
265 pub fn runtime_count(&self) -> usize {
267 self.runtimes.len()
268 }
269
270 pub fn add_plain_route(
274 &mut self,
275 route_id: PayloadRouteId,
276 channel_id: ChannelId,
277 key_slot: u64,
278 fec_k: usize,
279 fec_n: usize,
280 ) -> Result<PayloadRuntimeKey, PayloadRouteError> {
281 let key = PayloadRuntimeKey::new(channel_id, key_slot);
282 if let Some(runtime) = self.runtimes.get_mut(&key) {
283 runtime.push_route_id(route_id);
284 return Ok(key);
285 }
286
287 let pipeline = PayloadPipeline::new(channel_id, self.frame_layout, fec_k, fec_n)?;
288 self.runtimes
289 .insert(key, PayloadChannelRuntime::real(pipeline, route_id));
290 Ok(key)
291 }
292
293 pub fn add_keyed_route(
297 &mut self,
298 route_id: PayloadRouteId,
299 channel_id: ChannelId,
300 key_slot: u64,
301 keypair: WfbKeypair,
302 minimum_epoch: u64,
303 ) -> Result<PayloadRuntimeKey, PayloadRouteError> {
304 let key = PayloadRuntimeKey::new(channel_id, key_slot);
305 if let Some(runtime) = self.runtimes.get_mut(&key) {
306 runtime.push_route_id(route_id);
307 return Ok(key);
308 }
309
310 let pipeline =
311 PayloadPipeline::with_keypair(channel_id, self.frame_layout, keypair, minimum_epoch)?;
312 self.runtimes
313 .insert(key, PayloadChannelRuntime::real(pipeline, route_id));
314 Ok(key)
315 }
316
317 pub fn add_direct_route(
324 &mut self,
325 route_id: PayloadRouteId,
326 channel_id: ChannelId,
327 key_slot: u64,
328 ) -> PayloadRuntimeKey {
329 let key = PayloadRuntimeKey::new(channel_id, key_slot);
330 if let Some(runtime) = self.runtimes.get_mut(&key) {
331 runtime.push_route_id(route_id);
332 return key;
333 }
334
335 self.runtimes
336 .insert(key, PayloadChannelRuntime::mock(channel_id, route_id));
337 key
338 }
339
340 pub fn add_mock_route(
344 &mut self,
345 route_id: PayloadRouteId,
346 channel_id: ChannelId,
347 key_slot: u64,
348 ) -> PayloadRuntimeKey {
349 self.add_direct_route(route_id, channel_id, key_slot)
350 }
351
352 pub fn route_ids(&self, key: PayloadRuntimeKey) -> Option<&[PayloadRouteId]> {
354 self.runtimes
355 .get(&key)
356 .map(PayloadChannelRuntime::route_ids)
357 }
358
359 pub(crate) fn route_membership(
361 &self,
362 key: PayloadRuntimeKey,
363 ) -> Option<(Arc<[PayloadRouteId]>, bool)> {
364 self.runtimes.get(&key).map(|runtime| {
365 (
366 Arc::clone(&runtime.route_ids),
367 matches!(runtime.pipeline, PayloadChannelPipeline::Mock(_)),
368 )
369 })
370 }
371
372 pub fn fec_counters(&self, key: PayloadRuntimeKey) -> Option<FecCounters> {
374 self.runtimes
375 .get(&key)
376 .map(|runtime| runtime.pipeline.fec_counters())
377 }
378
379 pub fn accepts_80211_frame(&self, key: PayloadRuntimeKey, frame: &[u8]) -> bool {
381 self.runtimes
382 .get(&key)
383 .map(|runtime| runtime.pipeline.accepts_80211_frame(frame))
384 .unwrap_or(false)
385 }
386
387 pub fn push_80211_frame(
389 &mut self,
390 frame: &[u8],
391 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
392 let Ok(frame_view) = WifiFrame::parse(frame, self.frame_layout) else {
393 return Ok(vec![PayloadRouteEvent::IgnoredFrame]);
394 };
395 self.push_wifi_frame(frame_view)
396 }
397
398 pub fn push_wifi_frame(
400 &mut self,
401 frame_view: WifiFrame<'_>,
402 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
403 let mut route_events = Vec::new();
404 self.push_wifi_frame_into(frame_view, &mut route_events)?;
405 Ok(route_events)
406 }
407
408 pub fn push_wifi_frame_into(
410 &mut self,
411 frame_view: WifiFrame<'_>,
412 route_events: &mut Vec<PayloadRouteEvent>,
413 ) -> Result<(), PayloadRouteError> {
414 route_events.clear();
415 let Some(channel_id) = frame_view.channel_id() else {
416 route_events.push(PayloadRouteEvent::IgnoredFrame);
417 return Ok(());
418 };
419
420 let mut matched = false;
421 let mut first_error = None;
422
423 for (key, runtime) in self
424 .runtimes
425 .iter_mut()
426 .filter(|(key, _)| key.channel_id() == channel_id)
427 {
428 matched = true;
429 match runtime
430 .pipeline
431 .push_matched_payload_into(frame_view.payload(), &mut runtime.pipeline_events)
432 {
433 Ok(()) => {
434 map_pipeline_events_into(
435 *key,
436 Arc::clone(&runtime.route_ids),
437 runtime.pipeline_events.drain(..),
438 route_events,
439 );
440 }
441 Err(err) => {
442 if first_error.is_none() {
443 first_error = Some(err);
444 }
445 }
446 }
447 }
448
449 if !matched {
450 route_events.push(PayloadRouteEvent::IgnoredFrame);
451 return Ok(());
452 }
453 if route_events.is_empty() {
454 if let Some(err) = first_error {
455 return Err(err.into());
456 }
457 }
458 Ok(())
459 }
460
461 pub fn push_decrypted_80211_frame(
463 &mut self,
464 key: PayloadRuntimeKey,
465 frame: &[u8],
466 decrypted_fragment: &[u8],
467 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
468 let runtime = self
469 .runtimes
470 .get_mut(&key)
471 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
472 let events = runtime
473 .pipeline
474 .push_decrypted_80211_frame(frame, decrypted_fragment)?;
475 Ok(map_pipeline_events(key, runtime.route_ids(), events))
476 }
477
478 pub fn push_decrypted_fragment(
480 &mut self,
481 key: PayloadRuntimeKey,
482 data_nonce: u64,
483 decrypted_fragment: &[u8],
484 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
485 let runtime = self
486 .runtimes
487 .get_mut(&key)
488 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
489 let events = runtime
490 .pipeline
491 .push_decrypted_fragment(data_nonce, decrypted_fragment)?;
492 Ok(map_pipeline_events(key, runtime.route_ids(), events))
493 }
494
495 pub fn push_direct_payload(
497 &mut self,
498 key: PayloadRuntimeKey,
499 packet_seq: u64,
500 data: &[u8],
501 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
502 let runtime = self
503 .runtimes
504 .get_mut(&key)
505 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
506 let events = runtime.pipeline.push_mock_payload(packet_seq, data);
507 Ok(map_pipeline_events(key, runtime.route_ids(), events))
508 }
509
510 pub fn push_mock_payload(
514 &mut self,
515 key: PayloadRuntimeKey,
516 packet_seq: u64,
517 data: &[u8],
518 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
519 self.push_direct_payload(key, packet_seq, data)
520 }
521}
522
523fn map_pipeline_events(
524 runtime: PayloadRuntimeKey,
525 route_ids: &[PayloadRouteId],
526 events: Vec<PayloadPipelineEvent>,
527) -> Vec<PayloadRouteEvent> {
528 let mut mapped = Vec::with_capacity(events.len());
529 map_pipeline_events_into(runtime, Arc::from(route_ids), events, &mut mapped);
530 mapped
531}
532
533fn map_pipeline_events_into(
534 runtime: PayloadRuntimeKey,
535 route_ids: Arc<[PayloadRouteId]>,
536 events: impl IntoIterator<Item = PayloadPipelineEvent>,
537 mapped: &mut Vec<PayloadRouteEvent>,
538) {
539 mapped.extend(events.into_iter().map(|event| match event {
540 PayloadPipelineEvent::IgnoredFrame => PayloadRouteEvent::IgnoredFrame,
541 PayloadPipelineEvent::SessionEstablished {
542 epoch,
543 fec_k,
544 fec_n,
545 } => PayloadRouteEvent::SessionEstablished {
546 runtime,
547 route_ids: Arc::clone(&route_ids),
548 epoch,
549 fec_k,
550 fec_n,
551 },
552 PayloadPipelineEvent::Payload(payload) => PayloadRouteEvent::Payload {
553 runtime,
554 route_ids: Arc::clone(&route_ids),
555 payload,
556 },
557 }));
558}
559
560#[cfg(test)]
561mod tests {
562 use super::*;
563
564 fn plain(payload: &[u8]) -> Vec<u8> {
565 let mut out = Vec::new();
566 out.push(0);
567 out.extend_from_slice(&(payload.len() as u16).to_be_bytes());
568 out.extend_from_slice(payload);
569 out
570 }
571
572 #[test]
573 fn routes_share_one_runtime_per_channel_and_key_slot() {
574 let mut manager = PayloadRouteManager::new(FrameLayout::WithFcs);
575 let channel = ChannelId::default_video();
576 let runtime = manager
577 .add_plain_route(PayloadRouteId::new(1), channel, 0, 1, 1)
578 .unwrap();
579 let same_runtime = manager
580 .add_plain_route(PayloadRouteId::new(2), channel, 0, 1, 1)
581 .unwrap();
582
583 assert_eq!(runtime, same_runtime);
584 assert_eq!(manager.runtime_count(), 1);
585
586 let events = manager
587 .push_decrypted_fragment(runtime, 0, &plain(b"rtp bytes"))
588 .unwrap();
589 assert_eq!(
590 events,
591 vec![PayloadRouteEvent::Payload {
592 runtime,
593 route_ids: vec![PayloadRouteId::new(1), PayloadRouteId::new(2)].into(),
594 payload: RecoveredPayload {
595 channel_id: channel,
596 packet_seq: 0,
597 data: b"rtp bytes".to_vec(),
598 },
599 }]
600 );
601 }
602
603 #[test]
604 fn different_channels_get_different_runtimes() {
605 let mut manager = PayloadRouteManager::new(FrameLayout::WithFcs);
606 let video = ChannelId::default_video();
607 let telemetry = ChannelId::from_link_port(
608 crate::channel::DEFAULT_LINK_ID,
609 crate::RadioPort::TelemetryRx,
610 );
611
612 let video_runtime = manager
613 .add_plain_route(PayloadRouteId::new(1), video, 0, 1, 1)
614 .unwrap();
615 let telemetry_runtime = manager
616 .add_plain_route(PayloadRouteId::new(2), telemetry, 0, 1, 1)
617 .unwrap();
618
619 assert_ne!(video_runtime, telemetry_runtime);
620 assert_eq!(manager.runtime_count(), 2);
621 }
622}