stackforge_core/layer/dot11/
data.rs1use crate::layer::field::FieldError;
8
9pub const QOS_CTRL_LEN: usize = 2;
11
12pub const LLC_SNAP_LEN: usize = 8;
14
15pub const LLC_SNAP_PREFIX: [u8; 6] = [0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00];
17
18#[derive(Debug, Clone)]
32pub struct Dot11QoS {
33 pub offset: usize,
34}
35
36impl Dot11QoS {
37 #[must_use]
38 pub fn new(offset: usize) -> Self {
39 Self { offset }
40 }
41
42 pub fn validate(buf: &[u8], offset: usize) -> Result<(), FieldError> {
44 if buf.len() < offset + QOS_CTRL_LEN {
45 return Err(FieldError::BufferTooShort {
46 offset,
47 need: QOS_CTRL_LEN,
48 have: buf.len().saturating_sub(offset),
49 });
50 }
51 Ok(())
52 }
53
54 pub fn raw(&self, buf: &[u8]) -> Result<u16, FieldError> {
56 let off = self.offset;
57 if buf.len() < off + 2 {
58 return Err(FieldError::BufferTooShort {
59 offset: off,
60 need: 2,
61 have: buf.len(),
62 });
63 }
64 Ok(u16::from_le_bytes([buf[off], buf[off + 1]]))
65 }
66
67 pub fn tid(&self, buf: &[u8]) -> Result<u8, FieldError> {
69 let off = self.offset;
70 if buf.len() <= off {
71 return Err(FieldError::BufferTooShort {
72 offset: off,
73 need: 1,
74 have: buf.len(),
75 });
76 }
77 Ok(buf[off] & 0x0F)
78 }
79
80 pub fn eosp(&self, buf: &[u8]) -> Result<bool, FieldError> {
82 let off = self.offset;
83 if buf.len() <= off {
84 return Err(FieldError::BufferTooShort {
85 offset: off,
86 need: 1,
87 have: buf.len(),
88 });
89 }
90 Ok(buf[off] & 0x10 != 0)
91 }
92
93 pub fn ack_policy(&self, buf: &[u8]) -> Result<u8, FieldError> {
95 let off = self.offset;
96 if buf.len() <= off {
97 return Err(FieldError::BufferTooShort {
98 offset: off,
99 need: 1,
100 have: buf.len(),
101 });
102 }
103 Ok((buf[off] >> 5) & 0x03)
104 }
105
106 pub fn a_msdu_present(&self, buf: &[u8]) -> Result<bool, FieldError> {
108 let off = self.offset;
109 if buf.len() <= off {
110 return Err(FieldError::BufferTooShort {
111 offset: off,
112 need: 1,
113 have: buf.len(),
114 });
115 }
116 Ok(buf[off] & 0x80 != 0)
117 }
118
119 pub fn txop(&self, buf: &[u8]) -> Result<u8, FieldError> {
121 let off = self.offset + 1;
122 if buf.len() <= off {
123 return Err(FieldError::BufferTooShort {
124 offset: off,
125 need: 1,
126 have: buf.len(),
127 });
128 }
129 Ok(buf[off])
130 }
131
132 #[must_use]
134 pub fn header_len(&self) -> usize {
135 QOS_CTRL_LEN
136 }
137
138 #[must_use]
140 pub fn build(tid: u8, eosp: bool, ack_policy: u8, a_msdu: bool, txop: u8) -> Vec<u8> {
141 let byte0 = (tid & 0x0F)
142 | (if eosp { 0x10 } else { 0 })
143 | ((ack_policy & 0x03) << 5)
144 | (if a_msdu { 0x80 } else { 0 });
145 vec![byte0, txop]
146 }
147}
148
149#[must_use]
155pub fn is_llc_snap(buf: &[u8], offset: usize) -> bool {
156 if buf.len() < offset + LLC_SNAP_LEN {
157 return false;
158 }
159 buf[offset..offset + 6] == LLC_SNAP_PREFIX
160}
161
162pub fn llc_snap_ethertype(buf: &[u8], offset: usize) -> Result<u16, FieldError> {
164 if buf.len() < offset + LLC_SNAP_LEN {
165 return Err(FieldError::BufferTooShort {
166 offset: offset + 6,
167 need: 2,
168 have: buf.len().saturating_sub(offset + 6),
169 });
170 }
171 Ok(u16::from_be_bytes([buf[offset + 6], buf[offset + 7]]))
172}
173
174#[derive(Debug, Clone)]
187pub struct AMsduSubframe {
188 pub da: [u8; 6],
190 pub sa: [u8; 6],
192 pub data: Vec<u8>,
194}
195
196pub const AMSDU_SUBFRAME_HEADER_LEN: usize = 14;
198
199impl AMsduSubframe {
200 pub fn parse_all(buf: &[u8], offset: usize) -> Result<Vec<AMsduSubframe>, FieldError> {
202 let mut subframes = Vec::new();
203 let mut pos = offset;
204
205 while pos + AMSDU_SUBFRAME_HEADER_LEN <= buf.len() {
206 let mut da = [0u8; 6];
207 let mut sa = [0u8; 6];
208 da.copy_from_slice(&buf[pos..pos + 6]);
209 sa.copy_from_slice(&buf[pos + 6..pos + 12]);
210 let length = u16::from_be_bytes([buf[pos + 12], buf[pos + 13]]) as usize;
211
212 let data_start = pos + AMSDU_SUBFRAME_HEADER_LEN;
213 let data_end = data_start + length;
214 if data_end > buf.len() {
215 break;
216 }
217
218 subframes.push(AMsduSubframe {
219 da,
220 sa,
221 data: buf[data_start..data_end].to_vec(),
222 });
223
224 pos = data_end;
226 let padding = (4 - (pos % 4)) % 4;
227 pos += padding;
228 }
229
230 Ok(subframes)
231 }
232
233 #[must_use]
235 pub fn build(&self) -> Vec<u8> {
236 let length = self.data.len() as u16;
237 let mut out = Vec::with_capacity(AMSDU_SUBFRAME_HEADER_LEN + self.data.len() + 3);
238 out.extend_from_slice(&self.da);
239 out.extend_from_slice(&self.sa);
240 out.extend_from_slice(&length.to_be_bytes());
241 out.extend_from_slice(&self.data);
242 let padding = (4 - (out.len() % 4)) % 4;
244 out.extend(std::iter::repeat_n(0u8, padding));
245 out
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn test_qos_parse() {
255 let buf = vec![0x03, 0x00];
256 let qos = Dot11QoS::new(0);
257
258 assert_eq!(qos.tid(&buf).unwrap(), 3);
259 assert!(!qos.eosp(&buf).unwrap());
260 assert_eq!(qos.ack_policy(&buf).unwrap(), 0);
261 assert!(!qos.a_msdu_present(&buf).unwrap());
262 assert_eq!(qos.txop(&buf).unwrap(), 0);
263 }
264
265 #[test]
266 fn test_qos_with_eosp_and_amsdu() {
267 let buf = vec![0xD5, 0x10];
270 let qos = Dot11QoS::new(0);
271
272 assert_eq!(qos.tid(&buf).unwrap(), 5);
273 assert!(qos.eosp(&buf).unwrap());
274 assert_eq!(qos.ack_policy(&buf).unwrap(), 2);
275 assert!(qos.a_msdu_present(&buf).unwrap());
276 assert_eq!(qos.txop(&buf).unwrap(), 0x10);
277 }
278
279 #[test]
280 fn test_qos_build_roundtrip() {
281 let data = Dot11QoS::build(5, true, 2, true, 0x10);
282 assert_eq!(data.len(), QOS_CTRL_LEN);
283
284 let qos = Dot11QoS::new(0);
285 assert_eq!(qos.tid(&data).unwrap(), 5);
286 assert!(qos.eosp(&data).unwrap());
287 assert_eq!(qos.ack_policy(&data).unwrap(), 2);
288 assert!(qos.a_msdu_present(&data).unwrap());
289 assert_eq!(qos.txop(&data).unwrap(), 0x10);
290 }
291
292 #[test]
293 fn test_llc_snap_detection() {
294 let mut buf = vec![0u8; 8];
295 buf[0..6].copy_from_slice(&LLC_SNAP_PREFIX);
296 buf[6] = 0x08;
297 buf[7] = 0x00;
298
299 assert!(is_llc_snap(&buf, 0));
300 assert_eq!(llc_snap_ethertype(&buf, 0).unwrap(), 0x0800);
301 }
302
303 #[test]
304 fn test_llc_snap_not_present() {
305 let buf = vec![0x00; 8];
306 assert!(!is_llc_snap(&buf, 0));
307 }
308
309 #[test]
310 fn test_amsdu_parse() {
311 let subframe1 = AMsduSubframe {
312 da: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
313 sa: [0x11, 0x12, 0x13, 0x14, 0x15, 0x16],
314 data: vec![0xAA, 0xBB, 0xCC],
315 };
316 let built = subframe1.build();
317 assert_eq!(built.len(), 20);
318
319 let parsed = AMsduSubframe::parse_all(&built, 0).unwrap();
320 assert_eq!(parsed.len(), 1);
321 assert_eq!(parsed[0].da, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
322 assert_eq!(parsed[0].sa, [0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
323 assert_eq!(parsed[0].data, vec![0xAA, 0xBB, 0xCC]);
324 }
325
326 #[test]
327 fn test_amsdu_multiple_subframes() {
328 let sf1 = AMsduSubframe {
329 da: [0x01; 6],
330 sa: [0x02; 6],
331 data: vec![0x10, 0x20],
332 };
333 let sf2 = AMsduSubframe {
334 da: [0x03; 6],
335 sa: [0x04; 6],
336 data: vec![0x30, 0x40, 0x50, 0x60],
337 };
338
339 let mut buf = sf1.build();
340 buf.extend(sf2.build());
341
342 let parsed = AMsduSubframe::parse_all(&buf, 0).unwrap();
343 assert_eq!(parsed.len(), 2);
344 assert_eq!(parsed[0].data, vec![0x10, 0x20]);
345 assert_eq!(parsed[1].data, vec![0x30, 0x40, 0x50, 0x60]);
346 }
347
348 #[test]
349 fn test_qos_header_len() {
350 let qos = Dot11QoS::new(0);
351 assert_eq!(qos.header_len(), 2);
352 }
353
354 #[test]
355 fn test_qos_raw() {
356 let buf = vec![0xD5, 0x10];
357 let qos = Dot11QoS::new(0);
358 assert_eq!(qos.raw(&buf).unwrap(), 0x10D5);
359 }
360}