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