1use std::collections::HashMap;
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: Vec<PayloadRouteId>,
67 epoch: u64,
69 fec_k: usize,
71 fec_n: usize,
73 },
74 Payload {
76 runtime: PayloadRuntimeKey,
78 route_ids: Vec<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_80211_frame(&mut self, frame: &[u8]) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
145 match self {
146 Self::Real(pipeline) => pipeline.push_80211_frame(frame),
147 Self::Mock(_) => Ok(vec![PayloadPipelineEvent::IgnoredFrame]),
148 }
149 }
150
151 fn push_decrypted_80211_frame(
152 &mut self,
153 frame: &[u8],
154 decrypted_fragment: &[u8],
155 ) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
156 match self {
157 Self::Real(pipeline) => pipeline.push_decrypted_80211_frame(frame, decrypted_fragment),
158 Self::Mock(_) => Ok(vec![PayloadPipelineEvent::IgnoredFrame]),
159 }
160 }
161
162 fn push_decrypted_fragment(
163 &mut self,
164 data_nonce: u64,
165 decrypted_fragment: &[u8],
166 ) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
167 match self {
168 Self::Real(pipeline) => {
169 pipeline.push_decrypted_fragment(data_nonce, decrypted_fragment)
170 }
171 Self::Mock(pipeline) => Ok(pipeline.push_payload(data_nonce, decrypted_fragment)),
172 }
173 }
174
175 fn push_mock_payload(&mut self, packet_seq: u64, data: &[u8]) -> Vec<PayloadPipelineEvent> {
176 match self {
177 Self::Real(_) => vec![PayloadPipelineEvent::IgnoredFrame],
178 Self::Mock(pipeline) => pipeline.push_payload(packet_seq, data),
179 }
180 }
181}
182
183#[derive(Debug, Clone)]
189pub struct PayloadChannelRuntime {
190 pipeline: PayloadChannelPipeline,
191 route_ids: Vec<PayloadRouteId>,
192}
193
194impl PayloadChannelRuntime {
195 fn real(pipeline: PayloadPipeline, route_id: PayloadRouteId) -> Self {
196 Self {
197 pipeline: PayloadChannelPipeline::Real(Box::new(pipeline)),
198 route_ids: vec![route_id],
199 }
200 }
201
202 fn mock(channel_id: ChannelId, route_id: PayloadRouteId) -> Self {
203 Self {
204 pipeline: PayloadChannelPipeline::Mock(MockPayloadPipeline::new(channel_id)),
205 route_ids: vec![route_id],
206 }
207 }
208
209 pub fn channel_id(&self) -> ChannelId {
211 self.pipeline.channel_id()
212 }
213
214 pub fn route_ids(&self) -> &[PayloadRouteId] {
216 self.route_ids.as_slice()
217 }
218
219 fn push_route_id(&mut self, route_id: PayloadRouteId) {
220 push_route_id(&mut self.route_ids, route_id);
221 }
222}
223
224#[derive(Debug, Clone)]
230pub struct PayloadRouteManager {
231 frame_layout: FrameLayout,
232 runtimes: HashMap<PayloadRuntimeKey, PayloadChannelRuntime>,
233}
234
235impl PayloadRouteManager {
236 pub fn new(frame_layout: FrameLayout) -> Self {
238 Self {
239 frame_layout,
240 runtimes: HashMap::new(),
241 }
242 }
243
244 pub const fn frame_layout(&self) -> FrameLayout {
246 self.frame_layout
247 }
248
249 pub fn runtime_count(&self) -> usize {
251 self.runtimes.len()
252 }
253
254 pub fn add_plain_route(
258 &mut self,
259 route_id: PayloadRouteId,
260 channel_id: ChannelId,
261 key_slot: u64,
262 fec_k: usize,
263 fec_n: usize,
264 ) -> Result<PayloadRuntimeKey, PayloadRouteError> {
265 let key = PayloadRuntimeKey::new(channel_id, key_slot);
266 if let Some(runtime) = self.runtimes.get_mut(&key) {
267 runtime.push_route_id(route_id);
268 return Ok(key);
269 }
270
271 let pipeline = PayloadPipeline::new(channel_id, self.frame_layout, fec_k, fec_n)?;
272 self.runtimes
273 .insert(key, PayloadChannelRuntime::real(pipeline, route_id));
274 Ok(key)
275 }
276
277 pub fn add_keyed_route(
281 &mut self,
282 route_id: PayloadRouteId,
283 channel_id: ChannelId,
284 key_slot: u64,
285 keypair: WfbKeypair,
286 minimum_epoch: u64,
287 ) -> Result<PayloadRuntimeKey, PayloadRouteError> {
288 let key = PayloadRuntimeKey::new(channel_id, key_slot);
289 if let Some(runtime) = self.runtimes.get_mut(&key) {
290 runtime.push_route_id(route_id);
291 return Ok(key);
292 }
293
294 let pipeline =
295 PayloadPipeline::with_keypair(channel_id, self.frame_layout, keypair, minimum_epoch)?;
296 self.runtimes
297 .insert(key, PayloadChannelRuntime::real(pipeline, route_id));
298 Ok(key)
299 }
300
301 pub fn add_mock_route(
307 &mut self,
308 route_id: PayloadRouteId,
309 channel_id: ChannelId,
310 key_slot: u64,
311 ) -> PayloadRuntimeKey {
312 let key = PayloadRuntimeKey::new(channel_id, key_slot);
313 if let Some(runtime) = self.runtimes.get_mut(&key) {
314 runtime.push_route_id(route_id);
315 return key;
316 }
317
318 self.runtimes
319 .insert(key, PayloadChannelRuntime::mock(channel_id, route_id));
320 key
321 }
322
323 pub fn route_ids(&self, key: PayloadRuntimeKey) -> Option<&[PayloadRouteId]> {
325 self.runtimes
326 .get(&key)
327 .map(PayloadChannelRuntime::route_ids)
328 }
329
330 pub fn fec_counters(&self, key: PayloadRuntimeKey) -> Option<FecCounters> {
332 self.runtimes
333 .get(&key)
334 .map(|runtime| runtime.pipeline.fec_counters())
335 }
336
337 pub fn accepts_80211_frame(&self, key: PayloadRuntimeKey, frame: &[u8]) -> bool {
339 self.runtimes
340 .get(&key)
341 .map(|runtime| runtime.pipeline.accepts_80211_frame(frame))
342 .unwrap_or(false)
343 }
344
345 pub fn push_80211_frame(
347 &mut self,
348 frame: &[u8],
349 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
350 let Ok(frame_view) = WifiFrame::parse(frame, self.frame_layout) else {
351 return Ok(vec![PayloadRouteEvent::IgnoredFrame]);
352 };
353 let Some(channel_id) = frame_view.channel_id() else {
354 return Ok(vec![PayloadRouteEvent::IgnoredFrame]);
355 };
356
357 let mut matched = false;
358 let mut route_events = Vec::new();
359 let mut first_error = None;
360
361 for (key, runtime) in self
362 .runtimes
363 .iter_mut()
364 .filter(|(key, _)| key.channel_id() == channel_id)
365 {
366 matched = true;
367 match runtime.pipeline.push_80211_frame(frame) {
368 Ok(events) => {
369 route_events.extend(map_pipeline_events(*key, runtime.route_ids(), events));
370 }
371 Err(err) => {
372 if first_error.is_none() {
373 first_error = Some(err);
374 }
375 }
376 }
377 }
378
379 if !matched {
380 return Ok(vec![PayloadRouteEvent::IgnoredFrame]);
381 }
382 if route_events.is_empty() {
383 if let Some(err) = first_error {
384 return Err(err.into());
385 }
386 }
387 Ok(route_events)
388 }
389
390 pub fn push_decrypted_80211_frame(
392 &mut self,
393 key: PayloadRuntimeKey,
394 frame: &[u8],
395 decrypted_fragment: &[u8],
396 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
397 let runtime = self
398 .runtimes
399 .get_mut(&key)
400 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
401 let events = runtime
402 .pipeline
403 .push_decrypted_80211_frame(frame, decrypted_fragment)?;
404 Ok(map_pipeline_events(key, runtime.route_ids(), events))
405 }
406
407 pub fn push_decrypted_fragment(
409 &mut self,
410 key: PayloadRuntimeKey,
411 data_nonce: u64,
412 decrypted_fragment: &[u8],
413 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
414 let runtime = self
415 .runtimes
416 .get_mut(&key)
417 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
418 let events = runtime
419 .pipeline
420 .push_decrypted_fragment(data_nonce, decrypted_fragment)?;
421 Ok(map_pipeline_events(key, runtime.route_ids(), events))
422 }
423
424 pub fn push_mock_payload(
426 &mut self,
427 key: PayloadRuntimeKey,
428 packet_seq: u64,
429 data: &[u8],
430 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
431 let runtime = self
432 .runtimes
433 .get_mut(&key)
434 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
435 let events = runtime.pipeline.push_mock_payload(packet_seq, data);
436 Ok(map_pipeline_events(key, runtime.route_ids(), events))
437 }
438}
439
440fn push_route_id(route_ids: &mut Vec<PayloadRouteId>, route_id: PayloadRouteId) {
441 if !route_ids.contains(&route_id) {
442 route_ids.push(route_id);
443 }
444}
445
446fn map_pipeline_events(
447 runtime: PayloadRuntimeKey,
448 route_ids: &[PayloadRouteId],
449 events: Vec<PayloadPipelineEvent>,
450) -> Vec<PayloadRouteEvent> {
451 events
452 .into_iter()
453 .map(|event| match event {
454 PayloadPipelineEvent::IgnoredFrame => PayloadRouteEvent::IgnoredFrame,
455 PayloadPipelineEvent::SessionEstablished {
456 epoch,
457 fec_k,
458 fec_n,
459 } => PayloadRouteEvent::SessionEstablished {
460 runtime,
461 route_ids: route_ids.to_vec(),
462 epoch,
463 fec_k,
464 fec_n,
465 },
466 PayloadPipelineEvent::Payload(payload) => PayloadRouteEvent::Payload {
467 runtime,
468 route_ids: route_ids.to_vec(),
469 payload,
470 },
471 })
472 .collect()
473}
474
475#[cfg(test)]
476mod tests {
477 use super::*;
478
479 fn plain(payload: &[u8]) -> Vec<u8> {
480 let mut out = Vec::new();
481 out.push(0);
482 out.extend_from_slice(&(payload.len() as u16).to_be_bytes());
483 out.extend_from_slice(payload);
484 out
485 }
486
487 #[test]
488 fn routes_share_one_runtime_per_channel_and_key_slot() {
489 let mut manager = PayloadRouteManager::new(FrameLayout::WithFcs);
490 let channel = ChannelId::default_video();
491 let runtime = manager
492 .add_plain_route(PayloadRouteId::new(1), channel, 0, 1, 1)
493 .unwrap();
494 let same_runtime = manager
495 .add_plain_route(PayloadRouteId::new(2), channel, 0, 1, 1)
496 .unwrap();
497
498 assert_eq!(runtime, same_runtime);
499 assert_eq!(manager.runtime_count(), 1);
500
501 let events = manager
502 .push_decrypted_fragment(runtime, 0, &plain(b"rtp bytes"))
503 .unwrap();
504 assert_eq!(
505 events,
506 vec![PayloadRouteEvent::Payload {
507 runtime,
508 route_ids: vec![PayloadRouteId::new(1), PayloadRouteId::new(2)],
509 payload: RecoveredPayload {
510 channel_id: channel,
511 packet_seq: 0,
512 data: b"rtp bytes".to_vec(),
513 },
514 }]
515 );
516 }
517
518 #[test]
519 fn different_channels_get_different_runtimes() {
520 let mut manager = PayloadRouteManager::new(FrameLayout::WithFcs);
521 let video = ChannelId::default_video();
522 let telemetry = ChannelId::from_link_port(
523 crate::channel::DEFAULT_LINK_ID,
524 crate::RadioPort::TelemetryRx,
525 );
526
527 let video_runtime = manager
528 .add_plain_route(PayloadRouteId::new(1), video, 0, 1, 1)
529 .unwrap();
530 let telemetry_runtime = manager
531 .add_plain_route(PayloadRouteId::new(2), telemetry, 0, 1, 1)
532 .unwrap();
533
534 assert_ne!(video_runtime, telemetry_runtime);
535 assert_eq!(manager.runtime_count(), 2);
536 }
537}