1use std::collections::HashMap;
2
3use crate::channel::ChannelId;
4use crate::ieee80211::{FrameLayout, WifiFrame};
5use crate::pipeline::{PayloadPipeline, PayloadPipelineEvent, RecoveredPayload};
6use crate::wfb::{FecCounters, WfbError, WfbKeypair};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct PayloadRouteId(u64);
11
12impl PayloadRouteId {
13 pub const fn new(raw: u64) -> Self {
15 Self(raw)
16 }
17
18 pub const fn raw(self) -> u64 {
20 self.0
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub struct PayloadRuntimeKey {
30 channel_id: ChannelId,
31 key_slot: u64,
32}
33
34impl PayloadRuntimeKey {
35 pub const fn new(channel_id: ChannelId, key_slot: u64) -> Self {
37 Self {
38 channel_id,
39 key_slot,
40 }
41 }
42
43 pub const fn channel_id(self) -> ChannelId {
45 self.channel_id
46 }
47
48 pub const fn key_slot(self) -> u64 {
50 self.key_slot
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum PayloadRouteEvent {
57 IgnoredFrame,
59 SessionEstablished {
61 runtime: PayloadRuntimeKey,
63 route_ids: Vec<PayloadRouteId>,
65 epoch: u64,
67 fec_k: usize,
69 fec_n: usize,
71 },
72 Payload {
74 runtime: PayloadRuntimeKey,
76 route_ids: Vec<PayloadRouteId>,
78 payload: RecoveredPayload,
80 },
81}
82
83#[derive(Debug, PartialEq, Eq)]
85pub enum PayloadRouteError {
86 UnknownRuntime(PayloadRuntimeKey),
88 Wfb(WfbError),
90}
91
92impl std::fmt::Display for PayloadRouteError {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 match self {
95 Self::UnknownRuntime(key) => write!(
96 f,
97 "unknown payload runtime for channel 0x{:08x} key slot {}",
98 key.channel_id().raw(),
99 key.key_slot()
100 ),
101 Self::Wfb(err) => std::fmt::Display::fmt(err, f),
102 }
103 }
104}
105
106impl std::error::Error for PayloadRouteError {}
107
108impl From<WfbError> for PayloadRouteError {
109 fn from(err: WfbError) -> Self {
110 Self::Wfb(err)
111 }
112}
113
114#[derive(Debug, Clone)]
115struct PayloadChannelRuntime {
116 pipeline: PayloadPipeline,
117 route_ids: Vec<PayloadRouteId>,
118}
119
120#[derive(Debug, Clone)]
126pub struct PayloadRouteManager {
127 frame_layout: FrameLayout,
128 runtimes: HashMap<PayloadRuntimeKey, PayloadChannelRuntime>,
129}
130
131impl PayloadRouteManager {
132 pub fn new(frame_layout: FrameLayout) -> Self {
134 Self {
135 frame_layout,
136 runtimes: HashMap::new(),
137 }
138 }
139
140 pub const fn frame_layout(&self) -> FrameLayout {
142 self.frame_layout
143 }
144
145 pub fn runtime_count(&self) -> usize {
147 self.runtimes.len()
148 }
149
150 pub fn add_plain_route(
154 &mut self,
155 route_id: PayloadRouteId,
156 channel_id: ChannelId,
157 key_slot: u64,
158 fec_k: usize,
159 fec_n: usize,
160 ) -> Result<PayloadRuntimeKey, PayloadRouteError> {
161 let key = PayloadRuntimeKey::new(channel_id, key_slot);
162 if let Some(runtime) = self.runtimes.get_mut(&key) {
163 push_route_id(&mut runtime.route_ids, route_id);
164 return Ok(key);
165 }
166
167 let pipeline = PayloadPipeline::new(channel_id, self.frame_layout, fec_k, fec_n)?;
168 self.runtimes.insert(
169 key,
170 PayloadChannelRuntime {
171 pipeline,
172 route_ids: vec![route_id],
173 },
174 );
175 Ok(key)
176 }
177
178 pub fn add_keyed_route(
182 &mut self,
183 route_id: PayloadRouteId,
184 channel_id: ChannelId,
185 key_slot: u64,
186 keypair: WfbKeypair,
187 minimum_epoch: u64,
188 ) -> Result<PayloadRuntimeKey, PayloadRouteError> {
189 let key = PayloadRuntimeKey::new(channel_id, key_slot);
190 if let Some(runtime) = self.runtimes.get_mut(&key) {
191 push_route_id(&mut runtime.route_ids, route_id);
192 return Ok(key);
193 }
194
195 let pipeline =
196 PayloadPipeline::with_keypair(channel_id, self.frame_layout, keypair, minimum_epoch)?;
197 self.runtimes.insert(
198 key,
199 PayloadChannelRuntime {
200 pipeline,
201 route_ids: vec![route_id],
202 },
203 );
204 Ok(key)
205 }
206
207 pub fn route_ids(&self, key: PayloadRuntimeKey) -> Option<&[PayloadRouteId]> {
209 self.runtimes
210 .get(&key)
211 .map(|runtime| runtime.route_ids.as_slice())
212 }
213
214 pub fn fec_counters(&self, key: PayloadRuntimeKey) -> Option<FecCounters> {
216 self.runtimes
217 .get(&key)
218 .map(|runtime| runtime.pipeline.fec_counters())
219 }
220
221 pub fn accepts_80211_frame(&self, key: PayloadRuntimeKey, frame: &[u8]) -> bool {
223 self.runtimes
224 .get(&key)
225 .map(|runtime| runtime.pipeline.accepts_80211_frame(frame))
226 .unwrap_or(false)
227 }
228
229 pub fn push_80211_frame(
231 &mut self,
232 frame: &[u8],
233 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
234 let Ok(frame_view) = WifiFrame::parse(frame, self.frame_layout) else {
235 return Ok(vec![PayloadRouteEvent::IgnoredFrame]);
236 };
237 let Some(channel_id) = frame_view.channel_id() else {
238 return Ok(vec![PayloadRouteEvent::IgnoredFrame]);
239 };
240
241 let mut matched = false;
242 let mut route_events = Vec::new();
243 let mut first_error = None;
244
245 for (key, runtime) in self
246 .runtimes
247 .iter_mut()
248 .filter(|(key, _)| key.channel_id() == channel_id)
249 {
250 matched = true;
251 match runtime.pipeline.push_80211_frame(frame) {
252 Ok(events) => {
253 route_events.extend(map_pipeline_events(
254 *key,
255 runtime.route_ids.as_slice(),
256 events,
257 ));
258 }
259 Err(err) => {
260 if first_error.is_none() {
261 first_error = Some(err);
262 }
263 }
264 }
265 }
266
267 if !matched {
268 return Ok(vec![PayloadRouteEvent::IgnoredFrame]);
269 }
270 if route_events.is_empty() {
271 if let Some(err) = first_error {
272 return Err(err.into());
273 }
274 }
275 Ok(route_events)
276 }
277
278 pub fn push_decrypted_80211_frame(
280 &mut self,
281 key: PayloadRuntimeKey,
282 frame: &[u8],
283 decrypted_fragment: &[u8],
284 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
285 let runtime = self
286 .runtimes
287 .get_mut(&key)
288 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
289 let events = runtime
290 .pipeline
291 .push_decrypted_80211_frame(frame, decrypted_fragment)?;
292 Ok(map_pipeline_events(
293 key,
294 runtime.route_ids.as_slice(),
295 events,
296 ))
297 }
298
299 pub fn push_decrypted_fragment(
301 &mut self,
302 key: PayloadRuntimeKey,
303 data_nonce: u64,
304 decrypted_fragment: &[u8],
305 ) -> Result<Vec<PayloadRouteEvent>, PayloadRouteError> {
306 let runtime = self
307 .runtimes
308 .get_mut(&key)
309 .ok_or(PayloadRouteError::UnknownRuntime(key))?;
310 let events = runtime
311 .pipeline
312 .push_decrypted_fragment(data_nonce, decrypted_fragment)?;
313 Ok(map_pipeline_events(
314 key,
315 runtime.route_ids.as_slice(),
316 events,
317 ))
318 }
319}
320
321fn push_route_id(route_ids: &mut Vec<PayloadRouteId>, route_id: PayloadRouteId) {
322 if !route_ids.contains(&route_id) {
323 route_ids.push(route_id);
324 }
325}
326
327fn map_pipeline_events(
328 runtime: PayloadRuntimeKey,
329 route_ids: &[PayloadRouteId],
330 events: Vec<PayloadPipelineEvent>,
331) -> Vec<PayloadRouteEvent> {
332 events
333 .into_iter()
334 .map(|event| match event {
335 PayloadPipelineEvent::IgnoredFrame => PayloadRouteEvent::IgnoredFrame,
336 PayloadPipelineEvent::SessionEstablished {
337 epoch,
338 fec_k,
339 fec_n,
340 } => PayloadRouteEvent::SessionEstablished {
341 runtime,
342 route_ids: route_ids.to_vec(),
343 epoch,
344 fec_k,
345 fec_n,
346 },
347 PayloadPipelineEvent::Payload(payload) => PayloadRouteEvent::Payload {
348 runtime,
349 route_ids: route_ids.to_vec(),
350 payload,
351 },
352 })
353 .collect()
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 fn plain(payload: &[u8]) -> Vec<u8> {
361 let mut out = Vec::new();
362 out.push(0);
363 out.extend_from_slice(&(payload.len() as u16).to_be_bytes());
364 out.extend_from_slice(payload);
365 out
366 }
367
368 #[test]
369 fn routes_share_one_runtime_per_channel_and_key_slot() {
370 let mut manager = PayloadRouteManager::new(FrameLayout::WithFcs);
371 let channel = ChannelId::default_video();
372 let runtime = manager
373 .add_plain_route(PayloadRouteId::new(1), channel, 0, 1, 1)
374 .unwrap();
375 let same_runtime = manager
376 .add_plain_route(PayloadRouteId::new(2), channel, 0, 1, 1)
377 .unwrap();
378
379 assert_eq!(runtime, same_runtime);
380 assert_eq!(manager.runtime_count(), 1);
381
382 let events = manager
383 .push_decrypted_fragment(runtime, 0, &plain(b"rtp bytes"))
384 .unwrap();
385 assert_eq!(
386 events,
387 vec![PayloadRouteEvent::Payload {
388 runtime,
389 route_ids: vec![PayloadRouteId::new(1), PayloadRouteId::new(2)],
390 payload: RecoveredPayload {
391 channel_id: channel,
392 packet_seq: 0,
393 data: b"rtp bytes".to_vec(),
394 },
395 }]
396 );
397 }
398
399 #[test]
400 fn different_channels_get_different_runtimes() {
401 let mut manager = PayloadRouteManager::new(FrameLayout::WithFcs);
402 let video = ChannelId::default_video();
403 let telemetry = ChannelId::from_link_port(
404 crate::channel::DEFAULT_LINK_ID,
405 crate::RadioPort::TelemetryRx,
406 );
407
408 let video_runtime = manager
409 .add_plain_route(PayloadRouteId::new(1), video, 0, 1, 1)
410 .unwrap();
411 let telemetry_runtime = manager
412 .add_plain_route(PayloadRouteId::new(2), telemetry, 0, 1, 1)
413 .unwrap();
414
415 assert_ne!(video_runtime, telemetry_runtime);
416 assert_eq!(manager.runtime_count(), 2);
417 }
418}