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.
45use crate::big_endian::BigEndianU16;
6use crate::buffer_reader::BufferReader;
7use crate::mac::{round_up, MacAddr};
8use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned};
910// 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.
15pub da: MacAddr,
16pub sa: MacAddr,
17pub msdu_len: BigEndianU16,
18}
1920pub struct AmsduSubframe<B> {
21pub hdr: Ref<B, AmsduSubframeHdr>,
22pub body: B,
23}
2425/// 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> {
30pub fn parse(buffer_reader: &mut BufferReader<B>) -> Option<Self> {
31let hdr = buffer_reader.read::<AmsduSubframeHdr>()?;
32let msdu_len = hdr.msdu_len.to_native() as usize;
33if buffer_reader.bytes_remaining() < msdu_len {
34None
35} else {
36let body = buffer_reader.read_bytes(msdu_len)?;
37let base_len = std::mem::size_of::<AmsduSubframeHdr>() + msdu_len;
38let padded_len = round_up(base_len, 4);
39let padding_len = padded_len - base_len;
40if buffer_reader.bytes_remaining() == 0 {
41Some(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.`
47None
48} else {
49 buffer_reader.read_bytes(padding_len)?;
50Some(Self { hdr, body })
51 }
52 }
53 }
54}
5556#[cfg(test)]
57mod tests {
58use crate::mac::{self, data};
59use crate::test_utils::fake_frames::*;
60use zerocopy::Ref;
6162#[test]
63fn msdu_iter_aggregated() {
64let 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 }
8081#[test]
82fn msdu_iter_aggregated_with_padding_too_short() {
83let 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}