openipc_core/
ieee80211.rs1use crate::channel::ChannelId;
2
3pub const IEEE80211_HEADER_LEN: usize = 24;
5pub const IEEE80211_FCS_LEN: usize = 4;
7pub const WFB_PREFIX: [u8; 2] = [0x57, 0x42];
9pub const QOS_DATA_FROM_STA_TO_DS: [u8; 2] = [0x08, 0x01];
11pub const SRC_MAC_CHANNEL_OFFSET: usize = 12;
13pub const DST_MAC_CHANNEL_OFFSET: usize = 18;
15pub const FRAME_SEQUENCE_OFFSET: usize = 22;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub enum FrameLayout {
21 WithFcs,
23 WithoutFcs,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum FrameError {
30 TooShort,
32 NotDataFrame,
34 InvalidWfbAddressMirror,
36 MissingPayload,
38}
39
40#[derive(Debug, Clone, Copy)]
42pub struct WifiFrame<'a> {
43 data: &'a [u8],
44 layout: FrameLayout,
45}
46
47impl<'a> WifiFrame<'a> {
48 pub fn parse(data: &'a [u8], layout: FrameLayout) -> Result<Self, FrameError> {
50 let min_len = match layout {
51 FrameLayout::WithFcs => IEEE80211_HEADER_LEN + IEEE80211_FCS_LEN + 1,
52 FrameLayout::WithoutFcs => IEEE80211_HEADER_LEN + 1,
53 };
54 if data.len() < min_len {
55 return Err(FrameError::TooShort);
56 }
57
58 let frame = Self { data, layout };
59 if !frame.is_qos_data_from_sta_to_ds() {
60 return Err(FrameError::NotDataFrame);
61 }
62 if !frame.has_mirrored_air_id_and_radio_port() {
63 return Err(FrameError::InvalidWfbAddressMirror);
64 }
65 if frame.payload().is_empty() {
66 return Err(FrameError::MissingPayload);
67 }
68 Ok(frame)
69 }
70
71 pub const fn raw(&self) -> &'a [u8] {
73 self.data
74 }
75
76 pub const fn layout(&self) -> FrameLayout {
78 self.layout
79 }
80
81 pub fn payload(&self) -> &'a [u8] {
83 match self.layout {
84 FrameLayout::WithFcs => {
85 &self.data[IEEE80211_HEADER_LEN..self.data.len() - IEEE80211_FCS_LEN]
86 }
87 FrameLayout::WithoutFcs => &self.data[IEEE80211_HEADER_LEN..],
88 }
89 }
90
91 pub fn nonce(&self) -> [u8; 8] {
93 let mut nonce = [0; 8];
94 nonce[0..4].copy_from_slice(&self.data[11..15]);
95 nonce[4..8].copy_from_slice(&self.data[17..21]);
96 nonce
97 }
98
99 pub fn matches_channel_id(&self, channel_id: ChannelId) -> bool {
101 let id = channel_id.to_be_bytes();
102 self.data[10..12] == WFB_PREFIX
103 && self.data[12..16] == id
104 && self.data[16..18] == WFB_PREFIX
105 && self.data[18..22] == id
106 }
107
108 pub fn channel_id(&self) -> Option<ChannelId> {
110 if self.data[10..12] != WFB_PREFIX || self.data[16..18] != WFB_PREFIX {
111 return None;
112 }
113 if self.data[12..16] != self.data[18..22] {
114 return None;
115 }
116 let mut bytes = [0; 4];
117 bytes.copy_from_slice(&self.data[12..16]);
118 Some(ChannelId::new(u32::from_be_bytes(bytes)))
119 }
120
121 fn is_qos_data_from_sta_to_ds(&self) -> bool {
122 self.data[0..2] == QOS_DATA_FROM_STA_TO_DS
123 }
124
125 fn has_mirrored_air_id_and_radio_port(&self) -> bool {
126 self.data[10] == self.data[16] && self.data[15] == self.data[21]
127 }
128}
129
130pub fn build_wfb_header(
132 channel_id: ChannelId,
133 sequence_control: [u8; 2],
134) -> [u8; IEEE80211_HEADER_LEN] {
135 build_wfb_header_with_frame_type(channel_id, sequence_control, QOS_DATA_FROM_STA_TO_DS[0])
136}
137
138pub fn build_wfb_header_with_frame_type(
140 channel_id: ChannelId,
141 sequence_control: [u8; 2],
142 frame_type: u8,
143) -> [u8; IEEE80211_HEADER_LEN] {
144 let id = channel_id.to_be_bytes();
145 [
146 frame_type,
147 0x01,
148 0x00,
149 0x00,
150 0xff,
151 0xff,
152 0xff,
153 0xff,
154 0xff,
155 0xff,
156 0x57,
157 0x42,
158 id[0],
159 id[1],
160 id[2],
161 id[3],
162 0x57,
163 0x42,
164 id[0],
165 id[1],
166 id[2],
167 id[3],
168 sequence_control[0],
169 sequence_control[1],
170 ]
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn parses_reference_wfb_frame_with_fcs() {
179 let channel = ChannelId::default_video();
180 let mut bytes = Vec::from(build_wfb_header(channel, [0x10, 0x00]));
181 bytes.extend_from_slice(&[1, 2, 3, 4]);
182 bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
183
184 let frame = WifiFrame::parse(&bytes, FrameLayout::WithFcs).unwrap();
185 assert!(frame.matches_channel_id(channel));
186 assert_eq!(frame.channel_id(), Some(channel));
187 assert_eq!(frame.payload(), &[1, 2, 3, 4]);
188 assert_eq!(
189 frame.nonce(),
190 [
191 0x42,
192 channel.to_be_bytes()[0],
193 channel.to_be_bytes()[1],
194 channel.to_be_bytes()[2],
195 0x42,
196 channel.to_be_bytes()[0],
197 channel.to_be_bytes()[1],
198 channel.to_be_bytes()[2]
199 ]
200 );
201 }
202
203 #[test]
204 fn rejects_short_frames() {
205 assert_eq!(
206 WifiFrame::parse(&[0x08, 0x01], FrameLayout::WithFcs).unwrap_err(),
207 FrameError::TooShort
208 );
209 }
210}