1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    crate::{
        big_endian::BigEndianU16,
        buffer_reader::BufferReader,
        mac::{round_up, MacAddr},
    },
    zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeros, NoCell, Ref, Unaligned},
};

// IEEE Std 802.11-2016, 9.3.2.2.2
#[derive(FromZeros, FromBytes, AsBytes, NoCell, Unaligned)]
#[repr(C, packed)]
pub struct AmsduSubframeHdr {
    // Note this is the same as the IEEE 802.3 frame format.
    pub da: MacAddr,
    pub sa: MacAddr,
    pub msdu_len: BigEndianU16,
}

pub struct AmsduSubframe<B> {
    pub hdr: Ref<B, AmsduSubframeHdr>,
    pub body: B,
}

/// Parse an A-MSDU subframe from the byte stream and advance the cursor in the `BufferReader` if
/// successful. Parsing is only successful if the byte stream starts with a valid subframe.
/// TODO(https://fxbug.dev/42104386): The received AMSDU should not be greater than `max_amsdu_len`, specified in
/// HtCapabilities IE of Association. Warn or discard if violated.
impl<B: ByteSlice> AmsduSubframe<B> {
    pub fn parse(buffer_reader: &mut BufferReader<B>) -> Option<Self> {
        let hdr = buffer_reader.read::<AmsduSubframeHdr>()?;
        let msdu_len = hdr.msdu_len.to_native() as usize;
        if buffer_reader.bytes_remaining() < msdu_len {
            None
        } else {
            let body = buffer_reader.read_bytes(msdu_len)?;
            let base_len = std::mem::size_of::<AmsduSubframeHdr>() + msdu_len;
            let padded_len = round_up(base_len, 4);
            let padding_len = padded_len - base_len;
            if buffer_reader.bytes_remaining() == 0 {
                Some(Self { hdr, body })
            } else if buffer_reader.bytes_remaining() <= padding_len {
                // The subframe is invalid if EITHER one of the following is true
                // a) there are not enough bytes in the buffer for padding
                // b) the remaining buffer only contains padding bytes
                // IEEE 802.11-2016 9.3.2.2.2 `The last A-MSDU subframe has no padding.`
                None
            } else {
                buffer_reader.read_bytes(padding_len)?;
                Some(Self { hdr, body })
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use {
        crate::mac::{self, data},
        crate::test_utils::fake_frames::*,
        zerocopy::Ref,
    };

    #[test]
    fn msdu_iter_aggregated() {
        let bytes = make_data_frame_amsdu();
        data::harness::assert_msdus_llc_frame_eq(
            mac::DataFrame::parse(bytes.as_slice(), false)
                .expect("failed to parse aggregated data frame"),
            [
                mac::LlcFrame { hdr: Ref::new(MSDU_1_LLC_HDR).unwrap(), body: MSDU_1_PAYLOAD },
                mac::LlcFrame { hdr: Ref::new(MSDU_2_LLC_HDR).unwrap(), body: MSDU_2_PAYLOAD },
            ],
        );
    }

    #[test]
    fn msdu_iter_aggregated_with_padding_too_short() {
        let bytes = make_data_frame_amsdu_padding_too_short();
        data::harness::assert_msdus_llc_frame_eq(
            mac::DataFrame::parse(bytes.as_slice(), false)
                .expect("failed to parse aggregated data frame"),
            [mac::LlcFrame { hdr: Ref::new(MSDU_1_LLC_HDR).unwrap(), body: MSDU_1_PAYLOAD }],
        );
    }
}