blob: 8a1b0a1042a4fb292e9ed6ceceee9244a6d26033 [file] [log] [blame]
// 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::utils::skip,
bitfield::bitfield,
byteorder::{BigEndian, ByteOrder, LittleEndian},
num::{One, Unsigned},
zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned},
};
type MacAddr = [u8; 6];
pub const BCAST_ADDR: MacAddr = [0xFF; 6];
// IEEE Std 802.11-2016, 9.2.4.1.3
// Frame types:
pub const FRAME_TYPE_MGMT: u16 = 0;
pub const FRAME_TYPE_DATA: u16 = 2;
// Management subtypes:
pub const MGMT_SUBTYPE_ASSOC_RESP: u16 = 0x01;
pub const MGMT_SUBTYPE_BEACON: u16 = 0x08;
pub const MGMT_SUBTYPE_AUTH: u16 = 0x0B;
// Data subtypes:
pub const DATA_SUBTYPE_DATA: u16 = 0x00;
pub const DATA_SUBTYPE_NULL_DATA: u16 = 0x04;
pub const DATA_SUBTYPE_QOS_DATA: u16 = 0x08;
// IEEE Std 802.11-2016, 9.2.4.1.3, Table 9-1
const BITMASK_NULL: u16 = 1 << 2;
const BITMASK_QOS: u16 = 1 << 3;
// IEEE Std 802.11-2016, 9.2.4.1.1
bitfield! {
#[derive(PartialEq)]
pub struct FrameControl(u16);
impl Debug;
pub protocol_version, set_protocol_version: 1, 0;
pub frame_type, set_frame_type: 3, 2;
pub frame_subtype, set_frame_subtype: 7, 4;
pub to_ds, set_to_ds: 8;
pub from_ds, set_from_ds: 9;
pub more_frag, set_more_frag: 19;
pub retry, set_retry: 11;
pub pwr_mgmt, set_pwr_mgmt: 12;
pub more_data, set_more_data: 13;
pub protected, set_protected: 14;
pub htc_order, set_htc_order: 15;
pub value, _: 15,0;
}
impl FrameControl {
pub fn from_bytes(bytes: &[u8]) -> Option<FrameControl> {
if bytes.len() < 2 {
None
} else {
Some(FrameControl(LittleEndian::read_u16(bytes)))
}
}
}
// IEEE Std 802.11-2016, 9.2.4.4
bitfield! {
pub struct SequenceControl(u16);
impl Debug;
pub frag_num, set_frag_num: 3, 0;
pub seq_num, set_seq_num: 15, 4;
pub value, _: 15,0;
}
impl SequenceControl {
pub fn from_bytes(bytes: &[u8]) -> Option<SequenceControl> {
if bytes.len() < 2 {
None
} else {
Some(SequenceControl(LittleEndian::read_u16(bytes)))
}
}
}
// IEEE Std 802.11-2016, 9.2.4.6
bitfield! {
#[derive(PartialEq)]
pub struct HtControl(u32);
impl Debug;
pub vht, set_vht: 0;
// Structure of this middle section is defined in 9.2.4.6.2 for HT,
// and 9.2.4.6.3 for VHT.
pub middle, set_middle: 29, 1;
pub ac_constraint, set_ac_constraint: 30;
pub rdg_more_ppdu, setrdg_more_ppdu: 31;
pub value, _: 31,0;
}
impl HtControl {
pub fn from_bytes(bytes: &[u8]) -> Option<HtControl> {
if bytes.len() < 4 {
None
} else {
Some(HtControl(LittleEndian::read_u32(bytes)))
}
}
}
// IEEE Std 802.11-2016, 9.4.1.4
bitfield! {
pub struct CapabilityInfo(u16);
impl Debug;
pub ess, set_ess: 0;
pub ibss, set_ibss: 1;
pub cf_pollable, set_cf_pollable: 2;
pub cf_poll_req, set_cf_poll_req: 3;
pub privacy, set_privacy: 4;
pub short_preamble, set_short_preamble: 5;
// bit 6-7 reserved
pub spectrum_mgmt, set_spectrum_mgmt: 8;
pub qos, set_qos: 9;
pub short_slot_time, set_short_slot_time: 10;
pub apsd, set_apsd: 11;
pub radio_msmt, set_radio_msmt: 12;
// bit 13 reserved
pub delayed_block_ack, set_delayed_block_ack: 14;
pub immediate_block_ack, set_immediate_block_ack: 15;
pub value, _: 15, 0;
}
impl CapabilityInfo {
pub fn from_bytes(bytes: &[u8]) -> Option<CapabilityInfo> {
if bytes.len() < 4 {
None
} else {
Some(CapabilityInfo(LittleEndian::read_u16(bytes)))
}
}
}
// IEEE Std 802.11-2016, 9.3.3.2
#[repr(C, packed)]
pub struct MgmtHdr {
pub frame_ctrl: [u8; 2],
pub duration: [u8; 2],
pub addr1: MacAddr,
pub addr2: MacAddr,
pub addr3: MacAddr,
pub seq_ctrl: [u8; 2],
}
// Safe: see macro explanation.
unsafe_impl_zerocopy_traits!(MgmtHdr);
impl MgmtHdr {
pub fn frame_ctrl(&self) -> u16 {
LittleEndian::read_u16(&self.frame_ctrl)
}
pub fn duration(&self) -> u16 {
LittleEndian::read_u16(&self.duration)
}
pub fn seq_ctrl(&self) -> u16 {
LittleEndian::read_u16(&self.seq_ctrl)
}
pub fn set_frame_ctrl(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.frame_ctrl, val)
}
pub fn set_duration(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.duration, val)
}
pub fn set_seq_ctrl(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.seq_ctrl, val)
}
/// Returns the length in bytes of a mgmt header including all its fixed and optional
/// fields (if they are present).
pub fn len(has_ht_ctrl: bool) -> usize {
let mut bytes = std::mem::size_of::<DataHdr>();
bytes += if has_ht_ctrl { std::mem::size_of::<RawHtControl>() } else { 0 };
bytes
}
}
// IEEE Std 802.11-2016, 9.3.2.1
#[repr(C, packed)]
pub struct DataHdr {
pub frame_ctrl: [u8; 2],
pub duration: [u8; 2],
pub addr1: MacAddr,
pub addr2: MacAddr,
pub addr3: MacAddr,
pub seq_ctrl: [u8; 2],
}
// Safe: see macro explanation.
unsafe_impl_zerocopy_traits!(DataHdr);
impl DataHdr {
pub fn frame_ctrl(&self) -> u16 {
LittleEndian::read_u16(&self.frame_ctrl)
}
pub fn duration(&self) -> u16 {
LittleEndian::read_u16(&self.duration)
}
pub fn seq_ctrl(&self) -> u16 {
LittleEndian::read_u16(&self.seq_ctrl)
}
pub fn set_frame_ctrl(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.frame_ctrl, val)
}
pub fn set_duration(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.duration, val)
}
pub fn set_seq_ctrl(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.seq_ctrl, val)
}
/// Returns the length in bytes of a data header including all its fixed and optional
/// fields (if they are present).
pub fn len(has_addr4: bool, has_qos_ctrl: bool, has_ht_ctrl: bool) -> usize {
let mut bytes = std::mem::size_of::<DataHdr>();
bytes += if has_addr4 { std::mem::size_of::<MacAddr>() } else { 0 };
bytes += if has_qos_ctrl { std::mem::size_of::<RawQosControl>() } else { 0 };
bytes += if has_ht_ctrl { std::mem::size_of::<RawHtControl>() } else { 0 };
bytes
}
}
type RawHtControl = [u8; 4];
type RawQosControl = [u8; 2];
pub enum MacFrame<B> {
Mgmt {
// Management Header: fixed fields
mgmt_hdr: LayoutVerified<B, MgmtHdr>,
// Management Header: optional fields
ht_ctrl: Option<LayoutVerified<B, RawHtControl>>,
// Body
body: B,
},
Data {
// Data Header: fixed fields
data_hdr: LayoutVerified<B, DataHdr>,
// Data Header: optional fields
addr4: Option<LayoutVerified<B, MacAddr>>,
qos_ctrl: Option<LayoutVerified<B, RawQosControl>>,
ht_ctrl: Option<LayoutVerified<B, RawHtControl>>,
// Body
body: B,
},
Unsupported {
type_: u16,
},
}
impl<B: ByteSlice> MacFrame<B> {
/// If `body_aligned` is |true| the frame's body is expected to be 4 byte aligned.
pub fn parse(bytes: B, body_aligned: bool) -> Option<MacFrame<B>> {
let fc = FrameControl::from_bytes(&bytes[..])?;
match fc.frame_type() {
FRAME_TYPE_MGMT => {
// Parse fixed header fields:
let (mgmt_hdr, body) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
// Parse optional header fields:
let (ht_ctrl, body) = parse_ht_ctrl_if_present(&fc, body)?;
// Skip optional padding if body alignment is used.
let body = if body_aligned {
let full_hdr_len = MgmtHdr::len(ht_ctrl.is_some());
skip_body_alignment_padding(full_hdr_len, body)?
} else {
body
};
Some(MacFrame::Mgmt {
mgmt_hdr,
ht_ctrl,
body,
})
}
FRAME_TYPE_DATA => {
// Parse fixed header fields:
let (data_hdr, body) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
// Parse optional header fields:
let (addr4, body) = parse_addr4_if_present(&fc, body)?;
let (qos_ctrl, body) = parse_qos_if_present(&fc, body)?;
let (ht_ctrl, body) = parse_ht_ctrl_if_present(&fc, body)?;
// Skip optional padding if body alignment is used.
let body = if body_aligned {
let full_hdr_len = DataHdr::len(addr4.is_some(), qos_ctrl.is_some(),
ht_ctrl.is_some());
skip_body_alignment_padding(full_hdr_len, body)?
} else {
body
};
Some(MacFrame::Data {
data_hdr,
addr4,
qos_ctrl,
ht_ctrl,
body,
})
}
type_ => Some(MacFrame::Unsupported { type_ }),
}
}
}
/// Returns |None| if parsing fails. Otherwise returns |Some(tuple)| with `tuple` holding a
/// `MacAddr` if it is present and the remaining bytes.
fn parse_addr4_if_present<B: ByteSlice>(fc: &FrameControl, bytes: B)
-> Option<(Option<LayoutVerified<B, MacAddr>>, B)>
{
if fc.to_ds() && fc.from_ds() {
let (addr4, bytes) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
Some((Some(addr4), bytes))
} else {
Some((None, bytes))
}
}
/// Returns |None| if parsing fails. Otherwise returns |Some(tuple)| with `tuple` holding the
/// `QosControl` if it is present and the remaining bytes.
fn parse_qos_if_present<B: ByteSlice>(fc: &FrameControl, bytes: B)
-> Option<(Option<LayoutVerified<B, RawQosControl>>, B)>
{
if fc.frame_subtype() & BITMASK_QOS != 0 {
let (qos_ctrl, bytes) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
Some((Some(qos_ctrl), bytes))
} else {
Some((None, bytes))
}
}
/// Returns |None| if parsing fails. Otherwise returns |Some(tuple)| with `tuple` holding the
/// `HtControl` if it is present and the remaining bytes.
fn parse_ht_ctrl_if_present<B: ByteSlice>(fc: &FrameControl, bytes: B)
-> Option<(Option<LayoutVerified<B, RawHtControl>>, B)>
{
if fc.htc_order() {
let (ht_ctrl, bytes) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
Some((Some(ht_ctrl), bytes))
} else {
Some((None, bytes))
}
}
/// Skips optional padding required for body alignment.
fn skip_body_alignment_padding<B: ByteSlice>(hdr_len: usize, bytes: B) -> Option<B> {
const OPTIONAL_BODY_ALIGNMENT_BYTES: usize = 4;
let padded_len = round_up(hdr_len, OPTIONAL_BODY_ALIGNMENT_BYTES);
let padding = padded_len - hdr_len;
skip(bytes, padding)
}
// IEEE Std 802.11-2016, 9.3.3.3
#[repr(C, packed)]
pub struct BeaconHdr {
pub timestamp: [u8; 8],
pub beacon_interval: [u8; 2],
// IEEE Std 802.11-2016, 9.4.1.4
pub capabilities: [u8; 2],
}
// Safe: see macro explanation.
unsafe_impl_zerocopy_traits!(BeaconHdr);
impl BeaconHdr {
pub fn timestamp(&self) -> u64 {
LittleEndian::read_u64(&self.timestamp)
}
pub fn beacon_interval(&self) -> u16 {
LittleEndian::read_u16(&self.beacon_interval)
}
pub fn capabilities(&self) -> u16 {
LittleEndian::read_u16(&self.capabilities)
}
}
// IEEE Std 802.11-2016, 9.4.1.1
#[repr(u16)]
pub enum AuthAlgorithm {
Open = 0,
_SharedKey = 1,
_FastBssTransition = 2,
Sae = 3,
// 4-65534 Reserved
_VendorSpecific = 65535,
}
// IEEE Std 802.11-2016, 9.3.3.12
#[repr(C, packed)]
#[derive(Default)]
pub struct AuthHdr {
pub auth_alg_num: [u8; 2],
pub auth_txn_seq_num: [u8; 2],
pub status_code: [u8; 2],
}
// Safe: see macro explanation.
unsafe_impl_zerocopy_traits!(AuthHdr);
impl AuthHdr {
pub fn auth_alg_num(&self) -> u16 {
LittleEndian::read_u16(&self.auth_alg_num)
}
pub fn set_auth_alg_num(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.auth_alg_num, val)
}
pub fn auth_txn_seq_num(&self) -> u16 {
LittleEndian::read_u16(&self.auth_txn_seq_num)
}
pub fn set_auth_txn_seq_num(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.auth_txn_seq_num, val)
}
pub fn status_code(&self) -> u16 {
LittleEndian::read_u16(&self.status_code)
}
pub fn set_status_code(&mut self, val: u16) {
LittleEndian::write_u16(&mut self.status_code, val)
}
}
// IEEE Std 802.11-2016, 9.3.3.6
#[repr(C, packed)]
pub struct AssocRespHdr {
// IEEE Std 802.11-2016, 9.4.1.4
pub capabilities: [u8; 2],
pub status_code: [u8; 2],
pub aid: [u8; 2],
}
// Safe: see macro explanation.
unsafe_impl_zerocopy_traits!(AssocRespHdr);
impl AssocRespHdr {
pub fn capabilities(&self) -> u16 {
LittleEndian::read_u16(&self.capabilities)
}
pub fn status_code(&self) -> u16 {
LittleEndian::read_u16(&self.status_code)
}
pub fn aid(&self) -> u16 {
LittleEndian::read_u16(&self.aid)
}
}
pub enum MgmtSubtype<B> {
Beacon {
bcn_hdr: LayoutVerified<B, BeaconHdr>,
elements: B,
},
Authentication {
auth_hdr: LayoutVerified<B, AuthHdr>,
elements: B,
},
AssociationResp {
assoc_resp_hdr: LayoutVerified<B, AssocRespHdr>,
elements: B,
},
Unsupported {
subtype: u16,
},
}
impl<B: ByteSlice> MgmtSubtype<B> {
pub fn parse(subtype: u16, bytes: B) -> Option<MgmtSubtype<B>> {
match subtype {
MGMT_SUBTYPE_BEACON => {
let (bcn_hdr, elements) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
Some(MgmtSubtype::Beacon { bcn_hdr, elements })
}
MGMT_SUBTYPE_AUTH => {
let (auth_hdr, elements) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
Some(MgmtSubtype::Authentication { auth_hdr, elements })
}
MGMT_SUBTYPE_ASSOC_RESP => {
let (assoc_resp_hdr, elements) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
Some(MgmtSubtype::AssociationResp { assoc_resp_hdr, elements })
}
subtype => Some(MgmtSubtype::Unsupported { subtype }),
}
}
}
// IEEE Std 802.2-1998, 3.2
// IETF RFC 1042
#[repr(C, packed)]
pub struct LlcHdr {
pub dsap: u8,
pub ssap: u8,
pub control: u8,
pub oui: [u8; 3],
pub protocol_id: [u8; 2],
}
// Safe: see macro explanation.
unsafe_impl_zerocopy_traits!(LlcHdr);
impl LlcHdr {
pub fn protocol_id(&self) -> u16 {
LittleEndian::read_u16(&self.protocol_id)
}
}
// IEEE Std 802.11-2016, 9.3.2.2.2
#[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_be: [u8; 2], // In network byte order (big endian).
}
// Safe: see macro explanation.
unsafe_impl_zerocopy_traits!(AmsduSubframeHdr);
impl AmsduSubframeHdr {
pub fn msdu_len(&self) -> u16 {
BigEndian::read_u16(&self.msdu_len_be)
}
}
/// Iterates through an A-MSDU frame and provides access to
/// individual MSDUs.
/// The reader expects the byte stream to start with an
/// `AmsduSubframeHdr`.
pub struct AmsduReader<'a>(&'a [u8]);
impl<'a> AmsduReader<'a> {
pub fn has_remaining(&self) -> bool {
!self.0.is_empty()
}
pub fn remaining(&self) -> &'a [u8] {
self.0
}
}
impl<'a> Iterator for AmsduReader<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
let (amsdu_subframe_hdr, msdu) =
LayoutVerified::<_, AmsduSubframeHdr>::new_unaligned_from_prefix(&self.0[..])?;
let msdu_len = amsdu_subframe_hdr.msdu_len() as usize;
if msdu.len() < msdu_len {
// A-MSDU subframe header is valid, but MSDU doesn't fit into the buffer.
None
} else {
let (msdu, next_padded) = msdu.split_at(msdu_len);
// Padding following the last MSDU is optional.
if next_padded.is_empty() {
self.0 = next_padded;
Some(msdu)
} else {
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 next_padded.len() < padding_len {
// Corrupted: buffer to short to hold padding.
None
} else {
let (_padding, next) = next_padded.split_at(padding_len);
self.0 = next;
Some(msdu)
}
}
}
}
}
pub enum DataSubtype<B> {
// QoS or regular data type.
Data {
is_qos: bool,
hdr: LayoutVerified<B, LlcHdr>,
payload: B,
},
Unsupported {
subtype: u16,
},
}
impl<B: ByteSlice> DataSubtype<B> {
pub fn parse(subtype: u16, bytes: B) -> Option<DataSubtype<B>> {
match subtype {
DATA_SUBTYPE_DATA | DATA_SUBTYPE_QOS_DATA => {
let (hdr, payload) = LayoutVerified::new_unaligned_from_prefix(bytes)?;
let is_qos = subtype == DATA_SUBTYPE_QOS_DATA;
Some(DataSubtype::Data {
hdr,
is_qos,
payload,
})
}
subtype => Some(DataSubtype::Unsupported { subtype }),
}
}
}
// IEEE Std 802.11-2016, 9.4.1.9, Table 9-46
#[repr(u16)]
pub enum StatusCode {
Success = 0,
// ... defined remaining status codes.
}
fn round_up<T: Unsigned + Copy>(value: T, multiple: T) -> T {
let overshoot = value + multiple - T::one();
overshoot - overshoot % multiple
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::transmute;
fn make_mgmt_frame(ht_ctrl: bool) -> Vec<u8> {
#[cfg_attr(rustfmt, rustfmt_skip)]
let mut bytes = vec![
1, if ht_ctrl { 128 } else { 1 }, // fc
2, 2, // duration
3, 3, 3, 3, 3, 3, // addr1
4, 4, 4, 4, 4, 4, // addr2
5, 5, 5, 5, 5, 5, // addr3
6, 6, // sequence control
];
if ht_ctrl {
bytes.extend_from_slice(&[8, 8, 8, 8]);
}
bytes.extend_from_slice(&[9, 9, 9]);
bytes
}
fn make_data_frame(addr4: Option<[u8; 6]>, ht_ctrl: Option<[u8; 4]>) -> Vec<u8> {
let mut fc = FrameControl(0);
fc.set_frame_type(FRAME_TYPE_DATA);
fc.set_frame_subtype(DATA_SUBTYPE_QOS_DATA);
fc.set_from_ds(addr4.is_some());
fc.set_to_ds(addr4.is_some());
fc.set_htc_order(ht_ctrl.is_some());
let fc: [u8; 2] = unsafe { transmute(fc.value().to_le()) };
#[cfg_attr(rustfmt, rustfmt_skip)]
let mut bytes = vec![
// Data Header
fc[0], fc[1], // fc
2, 2, // duration
3, 3, 3, 3, 3, 3, // addr1
4, 4, 4, 4, 4, 4, // addr2
5, 5, 5, 5, 5, 5, // addr3
6, 6, // sequence control
];
if let Some(addr4) = addr4 {
bytes.extend_from_slice(&addr4);
}
// QoS Control
bytes.extend_from_slice(&[1, 1]);
if let Some(ht_ctrl) = ht_ctrl {
bytes.extend_from_slice(&ht_ctrl);
}
bytes.extend_from_slice(&[
// LLC Header
7, 7, 7, // DSAP, SSAP & control
8, 8, 8, // OUI
9, 9, // eth type
// Trailing bytes
10, 10, 10,
]);
bytes
}
fn make_data_frame_with_padding() -> Vec<u8> {
let mut fc = FrameControl(0);
fc.set_frame_type(FRAME_TYPE_DATA);
fc.set_frame_subtype(DATA_SUBTYPE_QOS_DATA);
let fc: [u8; 2] = unsafe { transmute(fc.value().to_le()) };
#[cfg_attr(rustfmt, rustfmt_skip)]
let bytes = vec![
// Data Header
fc[0], fc[1], // fc
2, 2, // duration
3, 3, 3, 3, 3, 3, // addr1
4, 4, 4, 4, 4, 4, // addr2
5, 5, 5, 5, 5, 5, // addr3
6, 6, // sequence control
// QoS Control
1, 1,
// Padding
2, 2,
// Body
7, 7, 7,
];
bytes
}
#[test]
fn mgmt_hdr_len() {
assert_eq!(MgmtHdr::len(false), 24);
assert_eq!(MgmtHdr::len(true), 28);
}
#[test]
fn data_hdr_len() {
assert_eq!(DataHdr::len(false, false, false), 24);
assert_eq!(DataHdr::len(true, false, false), 30);
assert_eq!(DataHdr::len(false, true, false), 26);
assert_eq!(DataHdr::len(false, false, true), 28);
assert_eq!(DataHdr::len(true, true, false), 32);
assert_eq!(DataHdr::len(false, true, true), 30);
assert_eq!(DataHdr::len(true, false, true), 34);
assert_eq!(DataHdr::len(true, true, true), 36);
}
#[test]
fn parse_mgmt_frame() {
let bytes = make_mgmt_frame(false);
match MacFrame::parse(&bytes[..], false) {
Some(MacFrame::Mgmt {
mgmt_hdr,
ht_ctrl,
body,
}) => {
assert_eq!([1, 1], mgmt_hdr.frame_ctrl);
assert_eq!([2, 2], mgmt_hdr.duration);
assert_eq!([3, 3, 3, 3, 3, 3], mgmt_hdr.addr1);
assert_eq!([4, 4, 4, 4, 4, 4], mgmt_hdr.addr2);
assert_eq!([5, 5, 5, 5, 5, 5], mgmt_hdr.addr3);
assert_eq!([6, 6], mgmt_hdr.seq_ctrl);
assert!(ht_ctrl.is_none());
assert_eq!(&body[..], &[9, 9, 9]);
}
_ => panic!("failed parsing mgmt frame"),
};
}
#[test]
fn parse_mgmt_frame_too_short_unsupported() {
// Valid MGMT header must have a minium length of 24 bytes.
assert!(MacFrame::parse(&[0; 22][..], false).is_none());
// Unsupported frame type.
match MacFrame::parse(&[0xFF; 24][..], false) {
Some(MacFrame::Unsupported { type_ }) => assert_eq!(3, type_),
_ => panic!("didn't detect unsupported frame"),
};
}
#[test]
fn parse_beacon_frame() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let bytes = vec![
1,1,1,1,1,1,1,1, // timestamp
2,2, // beacon_interval
3,3, // capabilities
0,5,1,2,3,4,5 // SSID IE: "12345"
];
match MgmtSubtype::parse(MGMT_SUBTYPE_BEACON, &bytes[..]) {
Some(MgmtSubtype::Beacon { bcn_hdr, elements }) => {
assert_eq!(0x0101010101010101, bcn_hdr.timestamp());
assert_eq!(0x0202, bcn_hdr.beacon_interval());
assert_eq!(0x0303, bcn_hdr.capabilities());
assert_eq!(&[0, 5, 1, 2, 3, 4, 5], &elements[..]);
}
_ => panic!("failed parsing beacon frame"),
};
}
#[test]
fn parse_data_frame() {
let bytes = make_data_frame(None, None);
match MacFrame::parse(&bytes[..], false) {
Some(MacFrame::Data {
data_hdr,
addr4,
qos_ctrl,
ht_ctrl,
body,
}) => {
assert_eq!([0b10001000, 0], data_hdr.frame_ctrl);
assert_eq!([2, 2], data_hdr.duration);
assert_eq!([3, 3, 3, 3, 3, 3], data_hdr.addr1);
assert_eq!([4, 4, 4, 4, 4, 4], data_hdr.addr2);
assert_eq!([5, 5, 5, 5, 5, 5], data_hdr.addr3);
assert_eq!([6, 6], data_hdr.seq_ctrl);
assert!(addr4.is_none());
match qos_ctrl {
None => panic!("qos_ctrl expected to be present"),
Some(qos_ctrl) => {
assert_eq!(&[1, 1][..], &qos_ctrl[..]);
}
};
assert!(ht_ctrl.is_none());
assert_eq!(&body[..], &[7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10]);
}
_ => panic!("failed parsing data frame"),
};
}
#[test]
fn parse_data_frame_with_padding() {
let bytes = make_data_frame_with_padding();
match MacFrame::parse(&bytes[..], true) {
Some(MacFrame::Data { qos_ctrl, body, .. }) => {
assert_eq!([1, 1], *qos_ctrl.expect("qos_ctrl not present"));
assert_eq!(&[7, 7, 7], &body[..]);
}
_ => panic!("failed parsing data frame"),
};
}
#[test]
fn parse_llc_with_addr4_ht_ctrl() {
let bytes = make_data_frame(Some([1, 2, 3, 4, 5, 6]), Some([4, 3, 2, 1]));
match MacFrame::parse(&bytes[..], false) {
Some(MacFrame::Data { data_hdr, body, .. }) => {
let fc = FrameControl(data_hdr.frame_ctrl());
match DataSubtype::parse(fc.frame_subtype(), &body[..]) {
Some(DataSubtype::Data { hdr, payload, is_qos }) => {
assert!(is_qos);
assert_eq!(7, hdr.dsap);
assert_eq!(7, hdr.ssap);
assert_eq!(7, hdr.control);
assert_eq!([8, 8, 8], hdr.oui);
assert_eq!([9, 9], hdr.protocol_id);
assert_eq!(&[10, 10, 10], payload);
},
_ => panic!("failed parsing LLC"),
}
}
_ => panic!("failed parsing data frame"),
};
}
#[test]
fn parse_amsdu() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let first_msdu = vec![
// LLC header
0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,
// Payload
0x33, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04,
];
#[cfg_attr(rustfmt, rustfmt_skip)]
let second_msdu = vec![
// LLC header
0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,
// Payload
0x99, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
];
#[cfg_attr(rustfmt, rustfmt_skip)]
let mut amsdu_frame = vec![
// A-MSDU Subframe #1
0x78, 0x8a, 0x20, 0x0d, 0x67, 0x03, 0xb4, 0xf7,
0xa1, 0xbe, 0xb9, 0xab,
0x00, 0x74, // MSDU length
];
amsdu_frame.extend(&first_msdu[..]);
amsdu_frame.extend(vec![
// Padding
0x00, 0x00, // A-MSDU Subframe #2
0x78, 0x8a, 0x20, 0x0d, 0x67, 0x03, 0xb4, 0xf7, 0xa1, 0xbe, 0xb9, 0xab, 0x00,
0x66, // MSDU length
]);
amsdu_frame.extend(&second_msdu[..]);
let mut found_msdus = (false, false);
let mut rdr = AmsduReader(&amsdu_frame[..]);
for msdu in &mut rdr {
match found_msdus {
(false, false) => {
assert_eq!(msdu, &first_msdu[..]);
found_msdus = (true, false);
}
(true, false) => {
assert_eq!(msdu, &second_msdu[..]);
found_msdus = (true, true);
}
_ => panic!("unexpected MSDU: {:x?}", msdu),
}
}
assert_eq!((true, true), found_msdus);
}
}