1use crate::channel::ChannelId;
2use crate::ieee80211::{FrameLayout, WifiFrame};
3use crate::wfb::{
4 parse_forwarder_packet, FecCounters, PlainAssembler, WfbError, WfbEvent, WfbKeypair, WfbPacket,
5 WfbReceiver,
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct RecoveredPayload {
11 pub channel_id: ChannelId,
13 pub packet_seq: u64,
15 pub data: Vec<u8>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum PayloadPipelineEvent {
22 IgnoredFrame,
24 SessionEstablished {
26 epoch: u64,
28 fec_k: usize,
30 fec_n: usize,
32 },
33 Payload(RecoveredPayload),
35}
36
37#[derive(Debug, Clone)]
42pub struct PayloadPipeline {
43 channel_id: ChannelId,
44 frame_layout: FrameLayout,
45 assembler: PlainAssembler,
46 wfb_receiver: Option<WfbReceiver>,
47}
48
49impl PayloadPipeline {
50 pub fn new(
52 channel_id: ChannelId,
53 frame_layout: FrameLayout,
54 fec_k: usize,
55 fec_n: usize,
56 ) -> Result<Self, WfbError> {
57 Ok(Self {
58 channel_id,
59 frame_layout,
60 assembler: PlainAssembler::new(fec_k, fec_n)?,
61 wfb_receiver: None,
62 })
63 }
64
65 pub fn with_keypair(
67 channel_id: ChannelId,
68 frame_layout: FrameLayout,
69 keypair: WfbKeypair,
70 minimum_epoch: u64,
71 ) -> Result<Self, WfbError> {
72 Ok(Self {
73 channel_id,
74 frame_layout,
75 assembler: PlainAssembler::new(1, 1)?,
76 wfb_receiver: Some(WfbReceiver::new(channel_id, keypair, minimum_epoch)),
77 })
78 }
79
80 pub const fn channel_id(&self) -> ChannelId {
82 self.channel_id
83 }
84
85 pub fn fec_counters(&self) -> FecCounters {
87 self.wfb_receiver
88 .as_ref()
89 .map(WfbReceiver::counters)
90 .unwrap_or_else(|| self.assembler.counters())
91 }
92
93 pub fn accepts_80211_frame(&self, frame: &[u8]) -> bool {
95 WifiFrame::parse(frame, self.frame_layout)
96 .map(|frame| frame.matches_channel_id(self.channel_id))
97 .unwrap_or(false)
98 }
99
100 pub fn push_80211_frame(
107 &mut self,
108 frame: &[u8],
109 ) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
110 let Ok(frame) = WifiFrame::parse(frame, self.frame_layout) else {
111 return Ok(vec![PayloadPipelineEvent::IgnoredFrame]);
112 };
113 if !frame.matches_channel_id(self.channel_id) {
114 return Ok(vec![PayloadPipelineEvent::IgnoredFrame]);
115 }
116
117 if let Some(receiver) = self.wfb_receiver.as_mut() {
118 let events = receiver.push_forwarder_packet(frame.payload())?;
119 return Ok(self.map_wfb_events(events));
120 }
121
122 match parse_forwarder_packet(frame.payload())? {
123 WfbPacket::Data {
124 data_nonce,
125 encrypted_payload,
126 ..
127 } => self.push_decrypted_fragment(data_nonce, encrypted_payload),
128 WfbPacket::SessionKey { .. } => Ok(Vec::new()),
129 }
130 }
131
132 pub fn push_decrypted_80211_frame(
134 &mut self,
135 frame: &[u8],
136 decrypted_fragment: &[u8],
137 ) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
138 let Ok(frame) = WifiFrame::parse(frame, self.frame_layout) else {
139 return Ok(vec![PayloadPipelineEvent::IgnoredFrame]);
140 };
141 if !frame.matches_channel_id(self.channel_id) {
142 return Ok(vec![PayloadPipelineEvent::IgnoredFrame]);
143 }
144
145 let packet = match parse_forwarder_packet(frame.payload())? {
146 WfbPacket::Data { data_nonce, .. } => data_nonce,
147 WfbPacket::SessionKey { .. } => return Ok(Vec::new()),
148 };
149 self.push_decrypted_fragment(packet, decrypted_fragment)
150 }
151
152 pub fn push_decrypted_fragment(
154 &mut self,
155 data_nonce: u64,
156 decrypted_fragment: &[u8],
157 ) -> Result<Vec<PayloadPipelineEvent>, WfbError> {
158 let payloads = self
159 .assembler
160 .push_decrypted_fragment(data_nonce, decrypted_fragment)?;
161 Ok(payloads
162 .into_iter()
163 .map(|payload| {
164 PayloadPipelineEvent::Payload(RecoveredPayload {
165 channel_id: self.channel_id,
166 packet_seq: payload.packet_seq,
167 data: payload.payload,
168 })
169 })
170 .collect())
171 }
172
173 fn map_wfb_events(&self, events: Vec<WfbEvent>) -> Vec<PayloadPipelineEvent> {
174 events
175 .into_iter()
176 .map(|event| match event {
177 WfbEvent::Session(session) => PayloadPipelineEvent::SessionEstablished {
178 epoch: session.epoch,
179 fec_k: session.fec_k,
180 fec_n: session.fec_n,
181 },
182 WfbEvent::Payload(payload) => PayloadPipelineEvent::Payload(RecoveredPayload {
183 channel_id: self.channel_id,
184 packet_seq: payload.packet_seq,
185 data: payload.payload,
186 }),
187 })
188 .collect()
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 fn plain(payload: &[u8]) -> Vec<u8> {
197 let mut out = Vec::new();
198 out.push(0);
199 out.extend_from_slice(&(payload.len() as u16).to_be_bytes());
200 out.extend_from_slice(payload);
201 out
202 }
203
204 #[test]
205 fn payload_pipeline_emits_raw_recovered_payloads() {
206 let mut pipeline =
207 PayloadPipeline::new(ChannelId::default_video(), FrameLayout::WithFcs, 1, 1).unwrap();
208 let events = pipeline
209 .push_decrypted_fragment(0, &plain(b"telemetry bytes"))
210 .unwrap();
211 assert_eq!(
212 events,
213 vec![PayloadPipelineEvent::Payload(RecoveredPayload {
214 channel_id: ChannelId::default_video(),
215 packet_seq: 0,
216 data: b"telemetry bytes".to_vec(),
217 })]
218 );
219 }
220
221 #[test]
222 fn recovered_payloads_carry_the_pipeline_channel_id() {
223 let channel_id = ChannelId::from_link_port(0x112233, crate::RadioPort::TunnelRx);
224 let mut pipeline = PayloadPipeline::new(channel_id, FrameLayout::WithFcs, 1, 1).unwrap();
225 let events = pipeline
226 .push_decrypted_fragment(0, &plain(b"data bytes"))
227 .unwrap();
228 let PayloadPipelineEvent::Payload(payload) = &events[0] else {
229 panic!("expected payload event");
230 };
231 assert_eq!(payload.channel_id, channel_id);
232 assert_eq!(payload.data, b"data bytes");
233 }
234}