wlan_common/mac/mgmt/
mod.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 super::{MgmtFrame, MgmtSubtype};
6use crate::ie;
7use zerocopy::{Ref, SplitByteSlice};
8
9mod fields;
10mod reason;
11mod status;
12
13pub use fields::*;
14pub use reason::*;
15pub use status::*;
16
17/// Frame or frame body distinguished by acknowledgement or lack thereof, namely action frames.
18#[derive(Clone, Copy, Debug)]
19pub struct NoAck<const NO_ACK: bool, T>(pub T);
20
21impl<const NO_ACK: bool, B> NoAck<NO_ACK, ActionBody<B>>
22where
23    B: SplitByteSlice,
24{
25    // NOTE: The management frame subtype is not part of the body. This function assumes whether or
26    //       not acknowledgement is required.
27    pub fn parse(bytes: B) -> Option<Self> {
28        ActionBody::parse(bytes).map(NoAck)
29    }
30}
31
32impl<const NO_ACK: bool, T> NoAck<NO_ACK, T> {
33    pub fn into_body(self) -> T {
34        self.0
35    }
36}
37
38impl<const NO_ACK: bool, T> AsRef<T> for NoAck<NO_ACK, T> {
39    fn as_ref(&self) -> &T {
40        &self.0
41    }
42}
43
44/// Action frame that requires acknowledgement per the `ACTION` management frame subtype.
45pub type ActionAckFrame<B> = NoAck<false, ActionBody<B>>;
46
47/// Action frame that does **not** require acknowledgement per the `ACTION_NO_ACK` management frame
48/// subtype.
49pub type ActionNoAckFrame<B> = NoAck<true, ActionBody<B>>;
50
51/// Action frame that may or may not require acknowledgement.
52#[derive(Debug)]
53pub enum ActionFrame<B>
54where
55    B: SplitByteSlice,
56{
57    Ack(ActionAckFrame<B>),
58    NoAck(ActionNoAckFrame<B>),
59}
60
61impl<B> ActionFrame<B>
62where
63    B: SplitByteSlice,
64{
65    pub fn parse(subtype: MgmtSubtype, bytes: B) -> Option<Self> {
66        match subtype {
67            MgmtSubtype::ACTION => ActionAckFrame::parse(bytes).map(From::from),
68            MgmtSubtype::ACTION_NO_ACK => ActionNoAckFrame::parse(bytes).map(From::from),
69            _ => None,
70        }
71    }
72
73    pub fn into_body(self) -> ActionBody<B> {
74        match self {
75            ActionFrame::Ack(frame) => frame.into_body(),
76            ActionFrame::NoAck(frame) => frame.into_body(),
77        }
78    }
79
80    pub fn ack(self) -> Option<ActionAckFrame<B>> {
81        match self {
82            ActionFrame::Ack(frame) => Some(frame),
83            _ => None,
84        }
85    }
86
87    pub fn no_ack(self) -> Option<ActionNoAckFrame<B>> {
88        match self {
89            ActionFrame::NoAck(frame) => Some(frame),
90            _ => None,
91        }
92    }
93
94    pub fn is_ack(&self) -> bool {
95        matches!(self, ActionFrame::Ack(_))
96    }
97
98    pub fn is_no_ack(&self) -> bool {
99        matches!(self, ActionFrame::NoAck(_))
100    }
101}
102
103impl<B> AsRef<ActionBody<B>> for ActionFrame<B>
104where
105    B: SplitByteSlice,
106{
107    fn as_ref(&self) -> &ActionBody<B> {
108        match self {
109            ActionFrame::Ack(frame) => frame.as_ref(),
110            ActionFrame::NoAck(frame) => frame.as_ref(),
111        }
112    }
113}
114
115impl<B> From<ActionAckFrame<B>> for ActionFrame<B>
116where
117    B: SplitByteSlice,
118{
119    fn from(frame: ActionAckFrame<B>) -> Self {
120        ActionFrame::Ack(frame)
121    }
122}
123
124impl<B> From<ActionNoAckFrame<B>> for ActionFrame<B>
125where
126    B: SplitByteSlice,
127{
128    fn from(frame: ActionNoAckFrame<B>) -> Self {
129        ActionFrame::NoAck(frame)
130    }
131}
132
133/// Action frame body.
134///
135/// Contains the contents of an action frame: an action header and action elements.
136#[derive(Debug)]
137pub struct ActionBody<B>
138where
139    B: SplitByteSlice,
140{
141    pub action_hdr: Ref<B, ActionHdr>,
142    pub elements: B,
143}
144
145impl<B> ActionBody<B>
146where
147    B: SplitByteSlice,
148{
149    pub fn parse(bytes: B) -> Option<Self> {
150        Ref::from_prefix(bytes)
151            .ok()
152            .map(|(action_hdr, elements)| ActionBody { action_hdr, elements })
153    }
154}
155
156#[derive(Debug)]
157pub struct AssocReqFrame<B>
158where
159    B: SplitByteSlice,
160{
161    pub assoc_req_hdr: Ref<B, AssocReqHdr>,
162    pub elements: B,
163}
164
165impl<B> AssocReqFrame<B>
166where
167    B: SplitByteSlice,
168{
169    pub fn parse(bytes: B) -> Option<Self> {
170        Ref::from_prefix(bytes)
171            .ok()
172            .map(|(assoc_req_hdr, elements)| AssocReqFrame { assoc_req_hdr, elements })
173    }
174
175    pub fn ies(&self) -> impl '_ + Iterator<Item = (ie::Id, &'_ B::Target)> {
176        ie::Reader::new(self.elements.deref())
177    }
178}
179
180#[derive(Debug)]
181pub struct AssocRespFrame<B>
182where
183    B: SplitByteSlice,
184{
185    pub assoc_resp_hdr: Ref<B, AssocRespHdr>,
186    pub elements: B,
187}
188
189impl<B> AssocRespFrame<B>
190where
191    B: SplitByteSlice,
192{
193    pub fn parse(bytes: B) -> Option<Self> {
194        Ref::from_prefix(bytes)
195            .ok()
196            .map(|(assoc_resp_hdr, elements)| AssocRespFrame { assoc_resp_hdr, elements })
197    }
198
199    pub fn into_assoc_resp_body(self) -> (Ref<B, AssocRespHdr>, B) {
200        let AssocRespFrame { assoc_resp_hdr, elements } = self;
201        (assoc_resp_hdr, elements)
202    }
203
204    pub fn ies(&self) -> impl '_ + Iterator<Item = (ie::Id, &'_ B::Target)> {
205        ie::Reader::new(self.elements.deref())
206    }
207}
208
209#[derive(Debug)]
210pub struct AuthFrame<B>
211where
212    B: SplitByteSlice,
213{
214    pub auth_hdr: Ref<B, AuthHdr>,
215    pub elements: B,
216}
217
218impl<B> AuthFrame<B>
219where
220    B: SplitByteSlice,
221{
222    pub fn parse(bytes: B) -> Option<Self> {
223        Ref::from_prefix(bytes).ok().map(|(auth_hdr, elements)| AuthFrame { auth_hdr, elements })
224    }
225
226    pub fn into_auth_body(self) -> (Ref<B, AuthHdr>, B) {
227        let AuthFrame { auth_hdr, elements } = self;
228        (auth_hdr, elements)
229    }
230}
231
232#[derive(Debug)]
233pub struct ProbeReqFrame<B>
234where
235    B: SplitByteSlice,
236{
237    pub elements: B,
238}
239
240impl<B> ProbeReqFrame<B>
241where
242    B: SplitByteSlice,
243{
244    pub fn parse(bytes: B) -> Option<Self> {
245        Some(ProbeReqFrame { elements: bytes })
246    }
247}
248
249// TODO(https://fxbug.dev/42079361): Implement dedicated management body types for remaining
250//                                   variants with fields and change those variants to tuple
251//                                   variants instead.
252#[derive(Debug)]
253pub enum MgmtBody<B: SplitByteSlice> {
254    Beacon { bcn_hdr: Ref<B, BeaconHdr>, elements: B },
255    ProbeReq(ProbeReqFrame<B>),
256    ProbeResp { probe_resp_hdr: Ref<B, ProbeRespHdr>, elements: B },
257    Authentication(AuthFrame<B>),
258    AssociationReq(AssocReqFrame<B>),
259    AssociationResp(AssocRespFrame<B>),
260    Deauthentication { deauth_hdr: Ref<B, DeauthHdr>, elements: B },
261    Disassociation { disassoc_hdr: Ref<B, DisassocHdr>, elements: B },
262    Action(ActionFrame<B>),
263    Unsupported { subtype: MgmtSubtype },
264}
265
266impl<B: SplitByteSlice> MgmtBody<B> {
267    pub fn parse(subtype: MgmtSubtype, bytes: B) -> Option<Self> {
268        match subtype {
269            MgmtSubtype::BEACON => {
270                let (bcn_hdr, elements) = Ref::from_prefix(bytes).ok()?;
271                Some(MgmtBody::Beacon { bcn_hdr, elements })
272            }
273            MgmtSubtype::PROBE_REQ => ProbeReqFrame::parse(bytes).map(From::from),
274            MgmtSubtype::PROBE_RESP => {
275                let (probe_resp_hdr, elements) = Ref::from_prefix(bytes).ok()?;
276                Some(MgmtBody::ProbeResp { probe_resp_hdr, elements })
277            }
278            MgmtSubtype::AUTH => AuthFrame::parse(bytes).map(From::from),
279            MgmtSubtype::ASSOC_REQ => AssocReqFrame::parse(bytes).map(From::from),
280            MgmtSubtype::ASSOC_RESP => AssocRespFrame::parse(bytes).map(From::from),
281            MgmtSubtype::DEAUTH => {
282                let (deauth_hdr, elements) = Ref::from_prefix(bytes).ok()?;
283                Some(MgmtBody::Deauthentication { deauth_hdr, elements })
284            }
285            MgmtSubtype::DISASSOC => {
286                let (disassoc_hdr, elements) = Ref::from_prefix(bytes).ok()?;
287                Some(MgmtBody::Disassociation { disassoc_hdr, elements })
288            }
289            MgmtSubtype::ACTION => {
290                ActionAckFrame::parse(bytes).map(ActionFrame::from).map(From::from)
291            }
292            MgmtSubtype::ACTION_NO_ACK => {
293                ActionNoAckFrame::parse(bytes).map(ActionFrame::from).map(From::from)
294            }
295            subtype => Some(MgmtBody::Unsupported { subtype }),
296        }
297    }
298}
299
300impl<B> From<ProbeReqFrame<B>> for MgmtBody<B>
301where
302    B: SplitByteSlice,
303{
304    fn from(frame: ProbeReqFrame<B>) -> Self {
305        MgmtBody::ProbeReq(frame)
306    }
307}
308
309impl<B> From<AuthFrame<B>> for MgmtBody<B>
310where
311    B: SplitByteSlice,
312{
313    fn from(frame: AuthFrame<B>) -> Self {
314        MgmtBody::Authentication(frame)
315    }
316}
317
318impl<B> From<AssocReqFrame<B>> for MgmtBody<B>
319where
320    B: SplitByteSlice,
321{
322    fn from(frame: AssocReqFrame<B>) -> Self {
323        MgmtBody::AssociationReq(frame)
324    }
325}
326
327impl<B> From<AssocRespFrame<B>> for MgmtBody<B>
328where
329    B: SplitByteSlice,
330{
331    fn from(frame: AssocRespFrame<B>) -> Self {
332        MgmtBody::AssociationResp(frame)
333    }
334}
335
336impl<B> From<ActionFrame<B>> for MgmtBody<B>
337where
338    B: SplitByteSlice,
339{
340    fn from(frame: ActionFrame<B>) -> Self {
341        MgmtBody::Action(frame)
342    }
343}
344
345impl<B> TryFrom<MgmtFrame<B>> for MgmtBody<B>
346where
347    B: SplitByteSlice,
348{
349    type Error = ();
350
351    fn try_from(mgmt_frame: MgmtFrame<B>) -> Result<Self, Self::Error> {
352        mgmt_frame.try_into_mgmt_body().1.ok_or(())
353    }
354}
355
356#[cfg(test)]
357mod tests {
358    use super::*;
359    use crate::mac::*;
360    use crate::{assert_variant, TimeUnit};
361
362    #[test]
363    fn mgmt_hdr_len() {
364        assert_eq!(MgmtHdr::len(HtControl::ABSENT), 24);
365        assert_eq!(MgmtHdr::len(HtControl::PRESENT), 28);
366    }
367
368    #[test]
369    fn parse_beacon_frame() {
370        #[rustfmt::skip]
371            let bytes = vec![
372            1,1,1,1,1,1,1,1, // timestamp
373            2,2, // beacon_interval
374            3,3, // capabilities
375            0,5,1,2,3,4,5 // SSID IE: "12345"
376        ];
377        assert_variant!(
378            MgmtBody::parse(MgmtSubtype::BEACON, &bytes[..]),
379            Some(MgmtBody::Beacon { bcn_hdr, elements }) => {
380                assert_eq!(TimeUnit(0x0202), { bcn_hdr.beacon_interval });
381                assert_eq!(0x0303, { bcn_hdr.capabilities.0 });
382                assert_eq!(&[0, 5, 1, 2, 3, 4, 5], &elements[..]);
383            },
384            "expected beacon frame"
385        );
386    }
387}