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> 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        // Parse fixed header fields
165        let fixed_fields = reader.read()?;
166
167        // Parse optional header fields
168        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        // Skip optional padding if body alignment is used.
173        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    // Management Header: fixed fields
207    pub mgmt_hdr: Ref<B, MgmtHdr>,
208    // Management Header: optional fields
209    pub ht_ctrl: Option<UnalignedView<B, HtControl>>,
210    // Body
211    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        // Parse fixed header fields
233        let mgmt_hdr = reader.read()?;
234
235        // Parse optional header fields
236        let ht_ctrl = if fc.htc_order() { Some(reader.read_unaligned()?) } else { None };
237        // Skip optional padding if body alignment is used.
238        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    /// Parses a MAC frame from bytes.
278    ///
279    /// If `is_body_aligned` is `true`, then the frame body **must** be aligned to four bytes.
280    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
334/// Skips optional padding required for body alignment.
335fn 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        // Valid MGMT header must have a minium length of 24 bytes.
379        assert!(MacFrame::parse(&[0; 22][..], false).is_none());
380
381        // Unsupported frame type.
382        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, // Frame Control
417                0b00000001, 0b11000000, // Masked AID
418                2, 2, 2, 2, 2, 2, // addr1
419                4, 4, 4, 4, 4, 4, // addr2
420            ][..], false),
421            Some(MacFrame::Ctrl(CtrlFrame { frame_ctrl, body })) => {
422                assert_eq!(0b00000000_10100100, frame_ctrl.0);
423                assert_eq!(&body[..], &[
424                    0b00000001, 0b11000000, // Masked AID
425                    2, 2, 2, 2, 2, 2, // addr1
426                    4, 4, 4, 4, 4, 4, // addr2
427                ]);
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}