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