wlan_common/mac/
mod.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
35// IEEE Std 802.11-2016, 9.4.1.8
36pub type Aid = u16;
37
38// IEEE Std 802.11-2016, 9.4.1.8: A non-DMG STA assigns the value of the AID in the range of 1 to
39// 2007.
40pub const MAX_AID: u16 = 2007;
41
42pub trait IntoBytesExt: IntoBytes + KnownLayout + Immutable + Sized {
43    /// Gets a byte slice reference from a reference to `Self`.
44    ///
45    /// This is essentially the reverse of `Ref` constructors and can be used to construct `Ref`
46    /// fields in `zerocopy` types from a reference to `Self` instead of bytes.
47    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    // Control Header: frame control
87    pub frame_ctrl: FrameControl,
88    // Body
89    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    // Data Header: fixed fields
130    pub fixed_fields: Ref<B, FixedDataHdrFields>,
131    // Data Header: optional fields
132    pub addr4: Option<Ref<B, Addr4>>,
133    pub qos_ctrl: Option<UnalignedView<B, QosControl>>,
134    pub ht_ctrl: Option<UnalignedView<B, HtControl>>,
135    // Body
136    pub body: B,
137}
138
139impl<B> DataFrame<B>
140where
141    B: SplitByteSlice,
142{
143    pub fn parse(reader: impl IntoBufferReader<B>, is_body_aligned: bool) -> Option<Self> {
144        let reader = reader.into_buffer_reader();
145        let fc = FrameControl(reader.peek_value()?);
146        matches!(fc.frame_type(), FrameType::DATA)
147            .then(|| DataFrame::parse_frame_type_unchecked(reader, is_body_aligned))
148            .flatten()
149    }
150
151    fn parse_frame_type_unchecked(
152        reader: impl IntoBufferReader<B>,
153        is_body_aligned: bool,
154    ) -> Option<Self> {
155        let mut reader = reader.into_buffer_reader();
156        let fc = FrameControl(reader.peek_value()?);
157
158        // Parse fixed header fields
159        let fixed_fields = reader.read()?;
160
161        // Parse optional header fields
162        let addr4 = if fc.to_ds() && fc.from_ds() { Some(reader.read()?) } else { None };
163        let qos_ctrl = if fc.data_subtype().qos() { Some(reader.read_unaligned()?) } else { None };
164        let ht_ctrl = if fc.htc_order() { Some(reader.read_unaligned()?) } else { None };
165
166        // Skip optional padding if body alignment is used.
167        if is_body_aligned {
168            let full_hdr_len = FixedDataHdrFields::len(
169                Presence::<Addr4>::from_bool(addr4.is_some()),
170                Presence::<QosControl>::from_bool(qos_ctrl.is_some()),
171                Presence::<HtControl>::from_bool(ht_ctrl.is_some()),
172            );
173            skip_body_alignment_padding(full_hdr_len, &mut reader)?
174        };
175        Some(DataFrame { fixed_fields, addr4, qos_ctrl, ht_ctrl, body: reader.into_remaining() })
176    }
177
178    pub fn frame_ctrl(&self) -> FrameControl {
179        self.fixed_fields.frame_ctrl
180    }
181
182    pub fn data_subtype(&self) -> DataSubtype {
183        self.frame_ctrl().data_subtype()
184    }
185}
186
187impl<B> IntoIterator for DataFrame<B>
188where
189    B: SplitByteSlice,
190{
191    type IntoIter = IntoMsduIter<B>;
192    type Item = Msdu<B>;
193
194    fn into_iter(self) -> Self::IntoIter {
195        self.into()
196    }
197}
198
199pub struct MgmtFrame<B> {
200    // Management Header: fixed fields
201    pub mgmt_hdr: Ref<B, MgmtHdr>,
202    // Management Header: optional fields
203    pub ht_ctrl: Option<UnalignedView<B, HtControl>>,
204    // Body
205    pub body: B,
206}
207
208impl<B> MgmtFrame<B>
209where
210    B: SplitByteSlice,
211{
212    pub fn parse(reader: impl IntoBufferReader<B>, is_body_aligned: bool) -> Option<Self> {
213        let reader = reader.into_buffer_reader();
214        let fc = FrameControl(reader.peek_value()?);
215        matches!(fc.frame_type(), FrameType::MGMT)
216            .then(|| MgmtFrame::parse_frame_type_unchecked(reader, is_body_aligned))
217            .flatten()
218    }
219
220    fn parse_frame_type_unchecked(
221        reader: impl IntoBufferReader<B>,
222        is_body_aligned: bool,
223    ) -> Option<Self> {
224        let mut reader = reader.into_buffer_reader();
225        let fc = FrameControl(reader.peek_value()?);
226        // Parse fixed header fields
227        let mgmt_hdr = reader.read()?;
228
229        // Parse optional header fields
230        let ht_ctrl = if fc.htc_order() { Some(reader.read_unaligned()?) } else { None };
231        // Skip optional padding if body alignment is used.
232        if is_body_aligned {
233            let full_hdr_len = MgmtHdr::len(Presence::<HtControl>::from_bool(ht_ctrl.is_some()));
234            skip_body_alignment_padding(full_hdr_len, &mut reader)?
235        }
236        Some(MgmtFrame { mgmt_hdr, ht_ctrl, body: reader.into_remaining() })
237    }
238
239    pub fn try_into_mgmt_body(self) -> (Ref<B, MgmtHdr>, Option<MgmtBody<B>>) {
240        let MgmtFrame { mgmt_hdr, body, .. } = self;
241        let mgmt_subtype = { mgmt_hdr.frame_ctrl }.mgmt_subtype();
242        (mgmt_hdr, MgmtBody::parse(mgmt_subtype, body))
243    }
244
245    pub fn into_ies(self) -> (Ref<B, MgmtHdr>, impl Iterator<Item = (ie::Id, B)>) {
246        let MgmtFrame { mgmt_hdr, body, .. } = self;
247        (mgmt_hdr, ie::Reader::new(body))
248    }
249
250    pub fn ies(&self) -> impl '_ + Iterator<Item = (ie::Id, &'_ B::Target)> {
251        ie::Reader::new(self.body.deref())
252    }
253
254    pub fn frame_ctrl(&self) -> FrameControl {
255        self.mgmt_hdr.frame_ctrl
256    }
257
258    pub fn mgmt_subtype(&self) -> MgmtSubtype {
259        self.frame_ctrl().mgmt_subtype()
260    }
261}
262
263pub enum MacFrame<B> {
264    Mgmt(MgmtFrame<B>),
265    Data(DataFrame<B>),
266    Ctrl(CtrlFrame<B>),
267    Unsupported { frame_ctrl: FrameControl },
268}
269
270impl<B: SplitByteSlice> MacFrame<B> {
271    /// Parses a MAC frame from bytes.
272    ///
273    /// If `is_body_aligned` is `true`, then the frame body **must** be aligned to four bytes.
274    pub fn parse(bytes: B, is_body_aligned: bool) -> Option<MacFrame<B>> {
275        let reader = BufferReader::new(bytes);
276        let frame_ctrl = FrameControl(reader.peek_value()?);
277        match frame_ctrl.frame_type() {
278            FrameType::MGMT => {
279                MgmtFrame::parse_frame_type_unchecked(reader, is_body_aligned).map(From::from)
280            }
281            FrameType::DATA => {
282                DataFrame::parse_frame_type_unchecked(reader, is_body_aligned).map(From::from)
283            }
284            FrameType::CTRL => CtrlFrame::parse_frame_type_unchecked(reader).map(From::from),
285            _frame_type => Some(MacFrame::Unsupported { frame_ctrl }),
286        }
287    }
288
289    pub fn frame_ctrl(&self) -> FrameControl {
290        match self {
291            MacFrame::Ctrl(ctrl_frame) => ctrl_frame.frame_ctrl(),
292            MacFrame::Data(data_frame) => data_frame.frame_ctrl(),
293            MacFrame::Mgmt(mgmt_frame) => mgmt_frame.frame_ctrl(),
294            MacFrame::Unsupported { frame_ctrl } => *frame_ctrl,
295        }
296    }
297}
298
299impl<B> From<CtrlFrame<B>> for MacFrame<B> {
300    fn from(ctrl: CtrlFrame<B>) -> Self {
301        MacFrame::Ctrl(ctrl)
302    }
303}
304
305impl<B> From<DataFrame<B>> for MacFrame<B> {
306    fn from(data: DataFrame<B>) -> Self {
307        MacFrame::Data(data)
308    }
309}
310
311impl<B> From<MgmtFrame<B>> for MacFrame<B> {
312    fn from(mgmt: MgmtFrame<B>) -> Self {
313        MacFrame::Mgmt(mgmt)
314    }
315}
316
317/// Skips optional padding required for body alignment.
318fn skip_body_alignment_padding<B: SplitByteSlice>(
319    hdr_len: usize,
320    reader: &mut BufferReader<B>,
321) -> Option<()> {
322    const OPTIONAL_BODY_ALIGNMENT_BYTES: usize = 4;
323
324    let padded_len = round_up(hdr_len, OPTIONAL_BODY_ALIGNMENT_BYTES);
325    let padding = padded_len - hdr_len;
326    reader.read_bytes(padding).map(|_| ())
327}
328
329fn round_up<T: Unsigned + Copy>(value: T, multiple: T) -> T {
330    let overshoot = value + multiple - T::one();
331    overshoot - overshoot % multiple
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337    use crate::assert_variant;
338    use crate::test_utils::fake_frames::*;
339
340    #[test]
341    fn parse_mgmt_frame() {
342        let bytes = make_mgmt_frame(false);
343        assert_variant!(
344            MacFrame::parse(&bytes[..], false),
345            Some(MacFrame::Mgmt(MgmtFrame { mgmt_hdr, ht_ctrl, body })) => {
346                assert_eq!(0x0101, { mgmt_hdr.frame_ctrl.0 });
347                assert_eq!(0x0202, { mgmt_hdr.duration });
348                assert_eq!(MacAddr::from([3, 3, 3, 3, 3, 3]), mgmt_hdr.addr1);
349                assert_eq!(MacAddr::from([4, 4, 4, 4, 4, 4]), mgmt_hdr.addr2);
350                assert_eq!(MacAddr::from([5, 5, 5, 5, 5, 5]), mgmt_hdr.addr3);
351                assert_eq!(0x0606, { mgmt_hdr.seq_ctrl.0 });
352                assert!(ht_ctrl.is_none());
353                assert_eq!(&body[..], &[9, 9, 9]);
354            },
355            "expected management frame"
356        );
357    }
358
359    #[test]
360    fn parse_mgmt_frame_too_short_unsupported() {
361        // Valid MGMT header must have a minium length of 24 bytes.
362        assert!(MacFrame::parse(&[0; 22][..], false).is_none());
363
364        // Unsupported frame type.
365        assert_variant!(
366            MacFrame::parse(&[0xFF; 24][..], false),
367            Some(MacFrame::Unsupported { frame_ctrl }) => {
368                assert_eq!(frame_ctrl, FrameControl(0xFFFF))
369            },
370            "expected unsupported frame type"
371        );
372    }
373
374    #[test]
375    fn parse_data_frame() {
376        let bytes = make_data_frame_single_llc(None, None);
377        assert_variant!(
378            MacFrame::parse(&bytes[..], false),
379            Some(MacFrame::Data(DataFrame { fixed_fields, addr4, qos_ctrl, ht_ctrl, body })) => {
380                assert_eq!(0b00000000_10001000, { fixed_fields.frame_ctrl.0 });
381                assert_eq!(0x0202, { fixed_fields.duration });
382                assert_eq!(MacAddr::from([3, 3, 3, 3, 3, 3]), fixed_fields.addr1);
383                assert_eq!(MacAddr::from([4, 4, 4, 4, 4, 4]), fixed_fields.addr2);
384                assert_eq!(MacAddr::from([5, 5, 5, 5, 5, 5]), fixed_fields.addr3);
385                assert_eq!(0x0606, { fixed_fields.seq_ctrl.0 });
386                assert!(addr4.is_none());
387                assert_eq!(0x0101, qos_ctrl.expect("qos_ctrl not present").get().0);
388                assert!(ht_ctrl.is_none());
389                assert_eq!(&body[..], &[7, 7, 7, 8, 8, 8, 9, 10, 11, 11, 11]);
390            },
391            "expected management frame"
392        );
393    }
394
395    #[test]
396    fn parse_ctrl_frame() {
397        assert_variant!(
398            MacFrame::parse(&[
399                0b10100100, 0b00000000, // Frame Control
400                0b00000001, 0b11000000, // Masked AID
401                2, 2, 2, 2, 2, 2, // addr1
402                4, 4, 4, 4, 4, 4, // addr2
403            ][..], false),
404            Some(MacFrame::Ctrl(CtrlFrame { frame_ctrl, body })) => {
405                assert_eq!(0b00000000_10100100, frame_ctrl.0);
406                assert_eq!(&body[..], &[
407                    0b00000001, 0b11000000, // Masked AID
408                    2, 2, 2, 2, 2, 2, // addr1
409                    4, 4, 4, 4, 4, 4, // addr2
410                ]);
411            },
412            "expected control frame"
413        );
414    }
415
416    #[test]
417    fn round_up_to_4() {
418        assert_eq!(0, round_up(0u32, 4));
419        assert_eq!(4, round_up(1u32, 4));
420        assert_eq!(4, round_up(2u32, 4));
421        assert_eq!(4, round_up(3u32, 4));
422        assert_eq!(4, round_up(4u32, 4));
423        assert_eq!(8, round_up(5u32, 4));
424    }
425}