wlan_common/mac/data/
amsdu.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::big_endian::BigEndianU16;
6use crate::buffer_reader::BufferReader;
7use crate::mac::{round_up, MacAddr};
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned};
9
10// IEEE Std 802.11-2016, 9.3.2.2.2
11#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
12#[repr(C, packed)]
13pub struct AmsduSubframeHdr {
14    // Note this is the same as the IEEE 802.3 frame format.
15    pub da: MacAddr,
16    pub sa: MacAddr,
17    pub msdu_len: BigEndianU16,
18}
19
20pub struct AmsduSubframe<B> {
21    pub hdr: Ref<B, AmsduSubframeHdr>,
22    pub body: B,
23}
24
25/// Parse an A-MSDU subframe from the byte stream and advance the cursor in the `BufferReader` if
26/// successful. Parsing is only successful if the byte stream starts with a valid subframe.
27/// TODO(https://fxbug.dev/42104386): The received AMSDU should not be greater than `max_amsdu_len`, specified in
28/// HtCapabilities IE of Association. Warn or discard if violated.
29impl<B: SplitByteSlice> AmsduSubframe<B> {
30    pub fn parse(buffer_reader: &mut BufferReader<B>) -> Option<Self> {
31        let hdr = buffer_reader.read::<AmsduSubframeHdr>()?;
32        let msdu_len = hdr.msdu_len.to_native() as usize;
33        if buffer_reader.bytes_remaining() < msdu_len {
34            None
35        } else {
36            let body = buffer_reader.read_bytes(msdu_len)?;
37            let base_len = std::mem::size_of::<AmsduSubframeHdr>() + msdu_len;
38            let padded_len = round_up(base_len, 4);
39            let padding_len = padded_len - base_len;
40            if buffer_reader.bytes_remaining() == 0 {
41                Some(Self { hdr, body })
42            } else if buffer_reader.bytes_remaining() <= padding_len {
43                // The subframe is invalid if EITHER one of the following is true
44                // a) there are not enough bytes in the buffer for padding
45                // b) the remaining buffer only contains padding bytes
46                // IEEE 802.11-2016 9.3.2.2.2 `The last A-MSDU subframe has no padding.`
47                None
48            } else {
49                buffer_reader.read_bytes(padding_len)?;
50                Some(Self { hdr, body })
51            }
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use crate::mac::{self, data};
59    use crate::test_utils::fake_frames::*;
60    use zerocopy::Ref;
61
62    #[test]
63    fn msdu_iter_aggregated() {
64        let bytes = make_data_frame_amsdu();
65        data::harness::assert_msdus_llc_frame_eq(
66            mac::DataFrame::parse(bytes.as_slice(), false)
67                .expect("failed to parse aggregated data frame"),
68            [
69                mac::LlcFrame {
70                    hdr: Ref::from_bytes(MSDU_1_LLC_HDR).unwrap(),
71                    body: MSDU_1_PAYLOAD,
72                },
73                mac::LlcFrame {
74                    hdr: Ref::from_bytes(MSDU_2_LLC_HDR).unwrap(),
75                    body: MSDU_2_PAYLOAD,
76                },
77            ],
78        );
79    }
80
81    #[test]
82    fn msdu_iter_aggregated_with_padding_too_short() {
83        let bytes = make_data_frame_amsdu_padding_too_short();
84        data::harness::assert_msdus_llc_frame_eq(
85            mac::DataFrame::parse(bytes.as_slice(), false)
86                .expect("failed to parse aggregated data frame"),
87            [mac::LlcFrame { hdr: Ref::from_bytes(MSDU_1_LLC_HDR).unwrap(), body: MSDU_1_PAYLOAD }],
88        );
89    }
90}