Skip to main content

ieee80211/frames/mgmt_frame/
mod.rs

1//! This module implements support management frames.
2//!
3//! ## What are management frames?
4//! Within IEEE 802.11 managment frames are a type of MPDU, that are - as the name suggests - used for the management of the link between two STAs.
5//! This includes association, authentication, BSS presence announcement through beacons and much more.
6//! Unlike control and data frames, all management frames have the same fixed header format, which is followed by a body determined by the subtype.
7//! Almost all of these bodies contain some fixed values followed by a series of so called elements, which are Type-Length-Value encoded fields, allowing for backwards compatible extension.
8//!
9//! ## How does this implementation work?
10//! Implementing management frames cleanly is an architectural challenge, since the bodies have different lengths.
11//! An approach we used previously, was to have an enum of all possible frame types, which came with horrific looking code and having to add more and more generic parameters.
12//! The current (and hopefully final) approach is, that we have a [struct](ManagementFrame), which contains the header and is generic over the body.
13//! It implements checking and attaching the fcs along with some other handy features, like conversion to a [DynamicManagementFrame].
14//! Types like [BeaconFrame] are just type aliases, for a [ManagementFrame] with a specific body.
15//! For documentation on the specific bodies, see the docs in [body].
16//!
17//! ## Usage
18//! Like all other frames, management frames can be matched using [match_frames](crate::match_frames).
19//! Direct RW through [Pread] and [Pwrite] is also available.
20//! For the specific usage of elements, see the docs in [elements](crate::elements).
21
22use core::ops::{Deref, DerefMut};
23
24use body::{
25    action::RawActionBody, AssociationRequestBody, AssociationResponseBody, AuthenticationBody,
26    BeaconBody, DeauthenticationBody, DisassociationBody, ManagementFrameBody, ProbeRequestBody,
27    ProbeResponseBody,
28};
29use scroll::{
30    ctx::{MeasureWith, TryFromCtx, TryIntoCtx},
31    Endian, Pread, Pwrite,
32};
33
34use crate::{
35    common::{attach_fcs, strip_and_validate_fcs, FrameControlField, FrameType},
36    elements::{Element, ReadElements, WrappedIEEE80211Element},
37    IEEE80211Frame,
38};
39
40pub mod body;
41mod header;
42pub use header::ManagementFrameHeader;
43
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
46/// A generic management frame.
47pub struct ManagementFrame<Body> {
48    pub header: ManagementFrameHeader,
49    pub body: Body,
50}
51impl<Body: TryIntoCtx<Error = scroll::Error> + ManagementFrameBody> ManagementFrame<Body> {
52    /// Create a [DynamicManagementFrame] from a statically typed one.
53    pub fn into_dynamic(
54        self,
55        buffer: &mut [u8],
56    ) -> Result<DynamicManagementFrame<'_>, scroll::Error> {
57        DynamicManagementFrame::new(self, buffer)
58    }
59}
60impl<Body: ManagementFrameBody> IEEE80211Frame for ManagementFrame<Body> {
61    const TYPE: FrameType = FrameType::Management(Body::SUBTYPE);
62    fn read_action_body_matches(action_body: RawActionBody<'_>) -> bool {
63        Body::read_action_body_matches(action_body)
64    }
65}
66impl<Body: MeasureWith<()>> MeasureWith<bool> for ManagementFrame<Body> {
67    fn measure_with(&self, with_fcs: &bool) -> usize {
68        self.header.length_in_bytes() + self.body.measure_with(&()) + if *with_fcs { 4 } else { 0 }
69    }
70}
71impl<'a, Body: TryFromCtx<'a, (), Error = scroll::Error>> TryFromCtx<'a, bool>
72    for ManagementFrame<Body>
73{
74    type Error = scroll::Error;
75    fn try_from_ctx(from: &'a [u8], with_fcs: bool) -> Result<(Self, usize), Self::Error> {
76        // We don't care about the FCF, since the information is already encoded in the type.
77        let mut offset = 0;
78
79        let from = if with_fcs {
80            strip_and_validate_fcs(from)?
81        } else {
82            from
83        };
84        let fcf = FrameControlField::from_bits(from.gread_with(&mut offset, Endian::Little)?);
85        if !matches!(fcf.frame_type(), FrameType::Management(_)) {
86            return Err(scroll::Error::BadInput {
87                size: offset,
88                msg: "Frame type wasn't management.",
89            });
90        }
91        let header = from.gread_with(&mut offset, fcf.flags())?;
92        let body = from.gread(&mut offset)?;
93
94        Ok((Self { header, body }, offset))
95    }
96}
97impl<Body: TryIntoCtx<Error = scroll::Error> + ManagementFrameBody> TryIntoCtx<bool>
98    for ManagementFrame<Body>
99{
100    type Error = scroll::Error;
101    fn try_into_ctx(self, buf: &mut [u8], with_fcs: bool) -> Result<usize, Self::Error> {
102        let mut offset = 0;
103
104        buf.gwrite_with(
105            FrameControlField::new()
106                .with_frame_type(<Self as IEEE80211Frame>::TYPE)
107                .with_flags(self.header.fcf_flags)
108                .into_bits(),
109            &mut offset,
110            Endian::Little,
111        )?;
112        buf.gwrite(self.header, &mut offset)?;
113        buf.gwrite(self.body, &mut offset)?;
114        if with_fcs {
115            attach_fcs(buf, &mut offset)?;
116        }
117
118        Ok(offset)
119    }
120}
121impl<Body> Deref for ManagementFrame<Body> {
122    type Target = Body;
123    fn deref(&self) -> &Self::Target {
124        &self.body
125    }
126}
127impl<Body> DerefMut for ManagementFrame<Body> {
128    fn deref_mut(&mut self) -> &mut Self::Target {
129        &mut self.body
130    }
131}
132macro_rules! mgmt_frames {
133    (
134        $(
135            $(
136                #[$frame_meta:meta]
137            )*
138            $frame:ident => $frame_body:ident
139        ),*
140    ) => {
141        $(
142            $(
143                #[$frame_meta]
144            )*
145            pub type $frame<'a, ElementContainer = ReadElements<'a>> = ManagementFrame<$frame_body<'a, ElementContainer>>;
146        )*
147    };
148}
149mgmt_frames! {
150    AssociationRequestFrame => AssociationRequestBody,
151    AssociationResponseFrame => AssociationResponseBody,
152    ProbeRequestFrame => ProbeRequestBody,
153    ProbeResponseFrame => ProbeResponseBody,
154    BeaconFrame => BeaconBody,
155    DisassociationFrame => DisassociationBody,
156    AuthenticationFrame => AuthenticationBody,
157    DeauthenticationFrame => DeauthenticationBody
158}
159pub type RawActionFrame<'a> = ManagementFrame<RawActionBody<'a>>;
160
161#[derive(Debug, PartialEq, Eq, Hash)]
162/// A dynamic management frame.
163///
164/// This frame allows writing a frame, with a fixed header and set of elements, and dynamically adding [Elements](Element) to it.
165/// One potential use case for this is, generating a [BeaconFrame] and optionally for example a channel switch announcement element.
166/// For an example see `examples/dynamic_mgmt_frame.rs`.
167pub struct DynamicManagementFrame<'a> {
168    buffer: &'a mut [u8],
169    offset: usize,
170}
171impl<'a> DynamicManagementFrame<'a> {
172    /// Create a new dynamic frame.
173    ///
174    /// This writes the frame into the buffer.
175    pub fn new(
176        frame: impl TryIntoCtx<bool, Error = scroll::Error>,
177        buffer: &'a mut [u8],
178    ) -> Result<Self, scroll::Error> {
179        let offset = buffer.pwrite(frame, 0)?;
180        Ok(Self { buffer, offset })
181    }
182    /// Attach an element to the frame body.
183    ///
184    /// This will return an error, if writing the element failed.
185    pub fn add_element(&mut self, element: impl Element) -> Result<(), scroll::Error> {
186        self.buffer
187            .gwrite(WrappedIEEE80211Element(element), &mut self.offset)?;
188        Ok(())
189    }
190    /// Finish writing the dynamic frame.
191    ///
192    /// # Returns
193    /// If `with_fcs` is `true` and the remaining length of the buffer is less then four, an error will be returned.
194    /// Otherwise, this will always return [Ok].
195    pub fn finish(mut self, with_fcs: bool) -> Result<usize, scroll::Error> {
196        if with_fcs {
197            self.buffer.gwrite(
198                crc32fast::hash(&self.buffer[..self.offset]),
199                &mut self.offset,
200            )?;
201        }
202        Ok(self.offset)
203    }
204}