1use 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#[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 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
44pub type ActionAckFrame<B> = NoAck<false, ActionBody<B>>;
46
47pub type ActionNoAckFrame<B> = NoAck<true, ActionBody<B>>;
50
51#[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#[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#[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, 2,2, 3,3, 0,5,1,2,3,4,5 ];
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}