1use crate::buffer_reader::{BufferReader, IntoBufferReader};
6use crate::{ie, UnalignedView};
7use fidl_fuchsia_wlan_common as fidl_common;
8use ieee80211::MacAddr;
9use num::Unsigned;
10use zerocopy::{Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice};
11
12mod ctrl;
13mod data;
14mod eth;
15mod fields;
16mod frame_class;
17mod mgmt;
18
19pub use ctrl::*;
20pub use data::*;
21pub use eth::*;
22pub use fields::*;
23pub use frame_class::*;
24pub use mgmt::*;
25
26#[macro_export]
27macro_rules! frame_len {
28 () => { 0 };
29 ($only:ty) => { std::mem::size_of::<$only>() };
30 ($first:ty, $($tail:ty),*) => {
31 std::mem::size_of::<$first>() + frame_len!($($tail),*)
32 };
33}
34
35pub type Aid = u16;
37
38pub const MAX_AID: u16 = 2007;
41
42pub trait IntoBytesExt: IntoBytes + KnownLayout + Immutable + Sized {
43 fn as_bytes_ref(&self) -> Ref<&'_ [u8], Self> {
48 Ref::from_bytes(self.as_bytes())
49 .expect("Unaligned or missized byte slice from `IntoBytes` implementation.")
50 }
51}
52
53impl<T> IntoBytesExt for T where T: IntoBytes + Immutable + KnownLayout + Sized {}
54
55#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
56pub enum MacRole {
57 Client,
58 Ap,
59 Mesh,
60}
61
62impl From<MacRole> for fidl_common::WlanMacRole {
63 fn from(role: MacRole) -> Self {
64 match role {
65 MacRole::Client => fidl_common::WlanMacRole::Client,
66 MacRole::Ap => fidl_common::WlanMacRole::Ap,
67 MacRole::Mesh => fidl_common::WlanMacRole::Mesh,
68 }
69 }
70}
71
72impl TryFrom<fidl_common::WlanMacRole> for MacRole {
73 type Error = fidl_common::WlanMacRole;
74
75 fn try_from(role: fidl_common::WlanMacRole) -> Result<Self, Self::Error> {
76 match role {
77 fidl_common::WlanMacRole::Client => Ok(MacRole::Client),
78 fidl_common::WlanMacRole::Ap => Ok(MacRole::Ap),
79 fidl_common::WlanMacRole::Mesh => Ok(MacRole::Mesh),
80 role => Err(role),
81 }
82 }
83}
84
85pub struct CtrlFrame<B> {
86 pub frame_ctrl: FrameControl,
88 pub body: B,
90}
91
92impl<B> CtrlFrame<B>
93where
94 B: SplitByteSlice,
95{
96 pub fn parse(reader: impl IntoBufferReader<B>) -> Option<Self> {
97 let reader = reader.into_buffer_reader();
98 let fc = FrameControl(reader.peek_value()?);
99 matches!(fc.frame_type(), FrameType::CTRL)
100 .then(|| CtrlFrame::parse_frame_type_unchecked(reader))
101 .flatten()
102 }
103
104 fn parse_frame_type_unchecked(reader: impl IntoBufferReader<B>) -> Option<Self> {
105 let mut reader = reader.into_buffer_reader();
106 let fc = reader.read_value()?;
107
108 Some(CtrlFrame { frame_ctrl: fc, body: reader.into_remaining() })
109 }
110
111 pub fn try_into_ctrl_body(self) -> Option<CtrlBody<B>> {
112 CtrlBody::parse(self.ctrl_subtype(), self.body)
113 }
114
115 pub fn ctrl_body(&self) -> Option<CtrlBody<&'_ B::Target>> {
116 CtrlBody::parse(self.ctrl_subtype(), self.body.deref())
117 }
118
119 pub fn frame_ctrl(&self) -> FrameControl {
120 self.frame_ctrl
121 }
122
123 pub fn ctrl_subtype(&self) -> CtrlSubtype {
124 self.frame_ctrl().ctrl_subtype()
125 }
126}
127
128pub struct DataFrame<B> {
129 pub fixed_fields: Ref<B, FixedDataHdrFields>,
131 pub addr4: Option<Ref<B, Addr4>>,
133 pub qos_ctrl: Option<UnalignedView<B, QosControl>>,
134 pub ht_ctrl: Option<UnalignedView<B, HtControl>>,
135 pub body: B,
137}
138
139impl<B> std::fmt::Debug for DataFrame<B> {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 f.debug_struct("DataFrame").finish()
142 }
143}
144
145impl<B> DataFrame<B>
146where
147 B: SplitByteSlice,
148{
149 pub fn parse(reader: impl IntoBufferReader<B>, is_body_aligned: bool) -> Option<Self> {
150 let reader = reader.into_buffer_reader();
151 let fc = FrameControl(reader.peek_value()?);
152 matches!(fc.frame_type(), FrameType::DATA)
153 .then(|| DataFrame::parse_frame_type_unchecked(reader, is_body_aligned))
154 .flatten()
155 }
156
157 fn parse_frame_type_unchecked(
158 reader: impl IntoBufferReader<B>,
159 is_body_aligned: bool,
160 ) -> Option<Self> {
161 let mut reader = reader.into_buffer_reader();
162 let fc = FrameControl(reader.peek_value()?);
163
164 let fixed_fields = reader.read()?;
166
167 let addr4 = if fc.to_ds() && fc.from_ds() { Some(reader.read()?) } else { None };
169 let qos_ctrl = if fc.data_subtype().qos() { Some(reader.read_unaligned()?) } else { None };
170 let ht_ctrl = if fc.htc_order() { Some(reader.read_unaligned()?) } else { None };
171
172 if is_body_aligned {
174 let full_hdr_len = FixedDataHdrFields::len(
175 Presence::<Addr4>::from_bool(addr4.is_some()),
176 Presence::<QosControl>::from_bool(qos_ctrl.is_some()),
177 Presence::<HtControl>::from_bool(ht_ctrl.is_some()),
178 );
179 skip_body_alignment_padding(full_hdr_len, &mut reader)?
180 };
181 Some(DataFrame { fixed_fields, addr4, qos_ctrl, ht_ctrl, body: reader.into_remaining() })
182 }
183
184 pub fn frame_ctrl(&self) -> FrameControl {
185 self.fixed_fields.frame_ctrl
186 }
187
188 pub fn data_subtype(&self) -> DataSubtype {
189 self.frame_ctrl().data_subtype()
190 }
191}
192
193impl<B> IntoIterator for DataFrame<B>
194where
195 B: SplitByteSlice,
196{
197 type IntoIter = IntoMsduIter<B>;
198 type Item = Msdu<B>;
199
200 fn into_iter(self) -> Self::IntoIter {
201 self.into()
202 }
203}
204
205pub struct MgmtFrame<B> {
206 pub mgmt_hdr: Ref<B, MgmtHdr>,
208 pub ht_ctrl: Option<UnalignedView<B, HtControl>>,
210 pub body: B,
212}
213
214impl<B> MgmtFrame<B>
215where
216 B: SplitByteSlice,
217{
218 pub fn parse(reader: impl IntoBufferReader<B>, is_body_aligned: bool) -> Option<Self> {
219 let reader = reader.into_buffer_reader();
220 let fc = FrameControl(reader.peek_value()?);
221 matches!(fc.frame_type(), FrameType::MGMT)
222 .then(|| MgmtFrame::parse_frame_type_unchecked(reader, is_body_aligned))
223 .flatten()
224 }
225
226 fn parse_frame_type_unchecked(
227 reader: impl IntoBufferReader<B>,
228 is_body_aligned: bool,
229 ) -> Option<Self> {
230 let mut reader = reader.into_buffer_reader();
231 let fc = FrameControl(reader.peek_value()?);
232 let mgmt_hdr = reader.read()?;
234
235 let ht_ctrl = if fc.htc_order() { Some(reader.read_unaligned()?) } else { None };
237 if is_body_aligned {
239 let full_hdr_len = MgmtHdr::len(Presence::<HtControl>::from_bool(ht_ctrl.is_some()));
240 skip_body_alignment_padding(full_hdr_len, &mut reader)?
241 }
242 Some(MgmtFrame { mgmt_hdr, ht_ctrl, body: reader.into_remaining() })
243 }
244
245 pub fn try_into_mgmt_body(self) -> (Ref<B, MgmtHdr>, Option<MgmtBody<B>>) {
246 let MgmtFrame { mgmt_hdr, body, .. } = self;
247 let mgmt_subtype = { mgmt_hdr.frame_ctrl }.mgmt_subtype();
248 (mgmt_hdr, MgmtBody::parse(mgmt_subtype, body))
249 }
250
251 pub fn into_ies(self) -> (Ref<B, MgmtHdr>, impl Iterator<Item = (ie::Id, B)>) {
252 let MgmtFrame { mgmt_hdr, body, .. } = self;
253 (mgmt_hdr, ie::Reader::new(body))
254 }
255
256 pub fn ies(&self) -> impl '_ + Iterator<Item = (ie::Id, &'_ B::Target)> {
257 ie::Reader::new(self.body.deref())
258 }
259
260 pub fn frame_ctrl(&self) -> FrameControl {
261 self.mgmt_hdr.frame_ctrl
262 }
263
264 pub fn mgmt_subtype(&self) -> MgmtSubtype {
265 self.frame_ctrl().mgmt_subtype()
266 }
267}
268
269pub enum MacFrame<B> {
270 Mgmt(MgmtFrame<B>),
271 Data(DataFrame<B>),
272 Ctrl(CtrlFrame<B>),
273 Unsupported { frame_ctrl: FrameControl },
274}
275
276impl<B: SplitByteSlice> MacFrame<B> {
277 pub fn parse(bytes: B, is_body_aligned: bool) -> Option<MacFrame<B>> {
281 let reader = BufferReader::new(bytes);
282 let frame_ctrl = FrameControl(reader.peek_value()?);
283 match frame_ctrl.frame_type() {
284 FrameType::MGMT => {
285 MgmtFrame::parse_frame_type_unchecked(reader, is_body_aligned).map(From::from)
286 }
287 FrameType::DATA => {
288 DataFrame::parse_frame_type_unchecked(reader, is_body_aligned).map(From::from)
289 }
290 FrameType::CTRL => CtrlFrame::parse_frame_type_unchecked(reader).map(From::from),
291 _frame_type => Some(MacFrame::Unsupported { frame_ctrl }),
292 }
293 }
294
295 pub fn frame_ctrl(&self) -> FrameControl {
296 match self {
297 MacFrame::Ctrl(ctrl_frame) => ctrl_frame.frame_ctrl(),
298 MacFrame::Data(data_frame) => data_frame.frame_ctrl(),
299 MacFrame::Mgmt(mgmt_frame) => mgmt_frame.frame_ctrl(),
300 MacFrame::Unsupported { frame_ctrl } => *frame_ctrl,
301 }
302 }
303}
304
305impl<B: SplitByteSlice> std::fmt::Debug for MacFrame<B> {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 match self {
308 MacFrame::Mgmt(_) => f.write_str("MacFrame::Mgmt"),
309 MacFrame::Data(_) => f.write_str("MacFrame::Data"),
310 MacFrame::Ctrl(_) => f.write_str("MacFrame::Ctrl"),
311 MacFrame::Unsupported { .. } => f.write_str("MacFrame::Unsupported"),
312 }
313 }
314}
315
316impl<B> From<CtrlFrame<B>> for MacFrame<B> {
317 fn from(ctrl: CtrlFrame<B>) -> Self {
318 MacFrame::Ctrl(ctrl)
319 }
320}
321
322impl<B> From<DataFrame<B>> for MacFrame<B> {
323 fn from(data: DataFrame<B>) -> Self {
324 MacFrame::Data(data)
325 }
326}
327
328impl<B> From<MgmtFrame<B>> for MacFrame<B> {
329 fn from(mgmt: MgmtFrame<B>) -> Self {
330 MacFrame::Mgmt(mgmt)
331 }
332}
333
334fn skip_body_alignment_padding<B: SplitByteSlice>(
336 hdr_len: usize,
337 reader: &mut BufferReader<B>,
338) -> Option<()> {
339 const OPTIONAL_BODY_ALIGNMENT_BYTES: usize = 4;
340
341 let padded_len = round_up(hdr_len, OPTIONAL_BODY_ALIGNMENT_BYTES);
342 let padding = padded_len - hdr_len;
343 reader.read_bytes(padding).map(|_| ())
344}
345
346fn round_up<T: Unsigned + Copy>(value: T, multiple: T) -> T {
347 let overshoot = value + multiple - T::one();
348 overshoot - overshoot % multiple
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354 use crate::test_utils::fake_frames::*;
355 use assert_matches::assert_matches;
356
357 #[test]
358 fn parse_mgmt_frame() {
359 let bytes = make_mgmt_frame(false);
360 assert_matches!(
361 MacFrame::parse(&bytes[..], false),
362 Some(MacFrame::Mgmt(MgmtFrame { mgmt_hdr, ht_ctrl, body })) => {
363 assert_eq!(0x0101, { mgmt_hdr.frame_ctrl.0 });
364 assert_eq!(0x0202, { mgmt_hdr.duration });
365 assert_eq!(MacAddr::from([3, 3, 3, 3, 3, 3]), mgmt_hdr.addr1);
366 assert_eq!(MacAddr::from([4, 4, 4, 4, 4, 4]), mgmt_hdr.addr2);
367 assert_eq!(MacAddr::from([5, 5, 5, 5, 5, 5]), mgmt_hdr.addr3);
368 assert_eq!(0x0606, { mgmt_hdr.seq_ctrl.0 });
369 assert!(ht_ctrl.is_none());
370 assert_eq!(&body[..], &[9, 9, 9]);
371 },
372 "expected management frame"
373 );
374 }
375
376 #[test]
377 fn parse_mgmt_frame_too_short_unsupported() {
378 assert!(MacFrame::parse(&[0; 22][..], false).is_none());
380
381 assert_matches!(
383 MacFrame::parse(&[0xFF; 24][..], false),
384 Some(MacFrame::Unsupported { frame_ctrl }) => {
385 assert_eq!(frame_ctrl, FrameControl(0xFFFF))
386 },
387 "expected unsupported frame type"
388 );
389 }
390
391 #[test]
392 fn parse_data_frame() {
393 let bytes = make_data_frame_single_llc(None, None);
394 assert_matches!(
395 MacFrame::parse(&bytes[..], false),
396 Some(MacFrame::Data(DataFrame { fixed_fields, addr4, qos_ctrl, ht_ctrl, body })) => {
397 assert_eq!(0b00000000_10001000, { fixed_fields.frame_ctrl.0 });
398 assert_eq!(0x0202, { fixed_fields.duration });
399 assert_eq!(MacAddr::from([3, 3, 3, 3, 3, 3]), fixed_fields.addr1);
400 assert_eq!(MacAddr::from([4, 4, 4, 4, 4, 4]), fixed_fields.addr2);
401 assert_eq!(MacAddr::from([5, 5, 5, 5, 5, 5]), fixed_fields.addr3);
402 assert_eq!(0x0606, { fixed_fields.seq_ctrl.0 });
403 assert!(addr4.is_none());
404 assert_eq!(0x0101, qos_ctrl.expect("qos_ctrl not present").get().0);
405 assert!(ht_ctrl.is_none());
406 assert_eq!(&body[..], &[7, 7, 7, 8, 8, 8, 9, 10, 11, 11, 11]);
407 },
408 "expected management frame"
409 );
410 }
411
412 #[test]
413 fn parse_ctrl_frame() {
414 assert_matches!(
415 MacFrame::parse(&[
416 0b10100100, 0b00000000, 0b00000001, 0b11000000, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, ][..], false),
421 Some(MacFrame::Ctrl(CtrlFrame { frame_ctrl, body })) => {
422 assert_eq!(0b00000000_10100100, frame_ctrl.0);
423 assert_eq!(&body[..], &[
424 0b00000001, 0b11000000, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, ]);
428 },
429 "expected control frame"
430 );
431 }
432
433 #[test]
434 fn round_up_to_4() {
435 assert_eq!(0, round_up(0u32, 4));
436 assert_eq!(4, round_up(1u32, 4));
437 assert_eq!(4, round_up(2u32, 4));
438 assert_eq!(4, round_up(3u32, 4));
439 assert_eq!(4, round_up(4u32, 4));
440 assert_eq!(8, round_up(5u32, 4));
441 }
442}