wlan_hw_sim/event/
buffered.rs

1// Copyright 2023 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
5//! Buffered (owned) frames and MAC data.
6//!
7//! This module provides types that copy buffers used by `zerocopy` types, in particular MAC frame
8//! types. The `Buffered` type must be used in extractors to accept MAC frame types, because these
9//! types cannot otherwise own the buffers from which they are parsed.
10//!
11//! For technical reasons, empty proxy types are used to name corresponding MAC frame types. For
12//! example, `buffered::Buffered<buffered::MgmtFrame>` buffers a `mac::MgmtFrame`, which is
13//! distinct from the empty `buffered::MgmtFrame` type. Generally, there is no need to explicitly
14//! refer to the corresponding `mac` types in event handlers.
15
16use fidl_fuchsia_wlan_tap as fidl_tap;
17use std::borrow::Borrow;
18use std::fmt::Debug;
19use std::marker::PhantomData;
20use wlan_common::mac::{
21    self, ActionBody, AssocReqFrame as ParsedAssocReqFrame, AssocRespFrame as ParsedAssocRespFrame,
22    AuthFrame as ParsedAuthFrame, DataFrame as ParsedDataFrame, MacFrame as ParsedMacFrame,
23    MgmtFrame as ParsedMgmtFrame, NoAck, ProbeReqFrame as ParsedProbeReqFrame,
24};
25use zerocopy::SplitByteSlice;
26
27use crate::event::extract::FromEvent;
28
29pub type ParsedActionFrame<const NO_ACK: bool, B> = NoAck<NO_ACK, ActionBody<B>>;
30
31mod sealed {
32    use super::*;
33
34    pub trait AsBuffer<T>
35    where
36        T: Parse,
37    {
38        fn as_buffer(&self) -> Option<&[u8]>;
39    }
40}
41use sealed::AsBuffer;
42
43pub trait Parse {
44    type Output<B>
45    where
46        B: SplitByteSlice;
47
48    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
49    where
50        B: SplitByteSlice;
51}
52
53pub trait TaggedField: Parse {
54    type Tag: Tag;
55
56    fn tag<B>(parsed: &Self::Output<B>) -> Self::Tag
57    where
58        B: SplitByteSlice;
59}
60
61pub trait TaggedVariant<T>
62where
63    T: TaggedField,
64{
65    const TAG: T::Tag;
66
67    fn has_tag(tag: impl Borrow<T::Tag>) -> bool {
68        Self::TAG.eq(tag.borrow())
69    }
70}
71
72pub trait Tag: Eq {
73    fn is_supported(&self) -> bool;
74}
75
76impl Tag for mac::FrameType {
77    fn is_supported(&self) -> bool {
78        mac::FrameType::is_supported(self)
79    }
80}
81
82impl Tag for mac::MgmtSubtype {
83    fn is_supported(&self) -> bool {
84        mac::MgmtSubtype::is_supported(self)
85    }
86}
87
88pub enum MacFrame {}
89
90impl AsBuffer<MacFrame> for fidl_tap::TxArgs {
91    fn as_buffer(&self) -> Option<&[u8]> {
92        Some(&self.packet.data)
93    }
94}
95
96impl Parse for MacFrame {
97    type Output<B>
98        = ParsedMacFrame<B>
99    where
100        B: SplitByteSlice;
101
102    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
103    where
104        B: SplitByteSlice,
105    {
106        ParsedMacFrame::parse(bytes, false)
107    }
108}
109
110impl TaggedField for MacFrame {
111    type Tag = mac::FrameType;
112
113    fn tag<B>(frame: &Self::Output<B>) -> Self::Tag
114    where
115        B: SplitByteSlice,
116    {
117        match frame {
118            ParsedMacFrame::Ctrl { .. } => mac::FrameType::CTRL,
119            ParsedMacFrame::Data { .. } => mac::FrameType::DATA,
120            ParsedMacFrame::Mgmt { .. } => mac::FrameType::MGMT,
121            ParsedMacFrame::Unsupported { frame_ctrl } => { *frame_ctrl }.frame_type(),
122        }
123    }
124}
125
126pub enum DataFrame {}
127
128impl AsBuffer<DataFrame> for fidl_tap::TxArgs {
129    fn as_buffer(&self) -> Option<&[u8]> {
130        Some(&self.packet.data)
131    }
132}
133
134impl Parse for DataFrame {
135    type Output<B>
136        = ParsedDataFrame<B>
137    where
138        B: SplitByteSlice;
139
140    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
141    where
142        B: SplitByteSlice,
143    {
144        ParsedDataFrame::parse(bytes, false)
145    }
146}
147
148impl TaggedVariant<MacFrame> for DataFrame {
149    const TAG: <MacFrame as TaggedField>::Tag = mac::FrameType::DATA;
150}
151
152pub enum MgmtFrame {}
153
154impl AsBuffer<MgmtFrame> for fidl_tap::TxArgs {
155    fn as_buffer(&self) -> Option<&[u8]> {
156        Some(&self.packet.data)
157    }
158}
159
160impl Parse for MgmtFrame {
161    type Output<B>
162        = ParsedMgmtFrame<B>
163    where
164        B: SplitByteSlice;
165
166    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
167    where
168        B: SplitByteSlice,
169    {
170        ParsedMgmtFrame::parse(bytes, false)
171    }
172}
173
174impl TaggedField for MgmtFrame {
175    type Tag = mac::MgmtSubtype;
176
177    fn tag<B>(frame: &Self::Output<B>) -> Self::Tag
178    where
179        B: SplitByteSlice,
180    {
181        { frame.mgmt_hdr.frame_ctrl }.mgmt_subtype()
182    }
183}
184
185impl TaggedVariant<MacFrame> for MgmtFrame {
186    const TAG: <MacFrame as TaggedField>::Tag = mac::FrameType::MGMT;
187}
188
189pub enum ActionFrame<const NO_ACK: bool> {}
190
191impl AsBuffer<ActionFrame<false>> for fidl_tap::TxArgs {
192    fn as_buffer(&self) -> Option<&[u8]> {
193        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false).and_then(|frame| {
194            ActionFrame::<false>::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body)
195        })
196    }
197}
198
199impl AsBuffer<ActionFrame<true>> for fidl_tap::TxArgs {
200    fn as_buffer(&self) -> Option<&[u8]> {
201        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false).and_then(|frame| {
202            ActionFrame::<true>::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body)
203        })
204    }
205}
206
207impl<const NO_ACK: bool> Parse for ActionFrame<NO_ACK> {
208    type Output<B>
209        = ParsedActionFrame<NO_ACK, B>
210    where
211        B: SplitByteSlice;
212
213    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
214    where
215        B: SplitByteSlice,
216    {
217        NoAck::<NO_ACK, ActionBody<B>>::parse(bytes)
218    }
219}
220
221impl TaggedVariant<MgmtFrame> for ActionFrame<false> {
222    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ACTION;
223}
224
225impl TaggedVariant<MgmtFrame> for ActionFrame<true> {
226    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ACTION_NO_ACK;
227}
228
229pub enum AssocReqFrame {}
230
231impl AsBuffer<AssocReqFrame> for fidl_tap::TxArgs {
232    fn as_buffer(&self) -> Option<&[u8]> {
233        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
234            .and_then(|frame| AssocReqFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
235    }
236}
237
238impl Parse for AssocReqFrame {
239    type Output<B>
240        = ParsedAssocReqFrame<B>
241    where
242        B: SplitByteSlice;
243
244    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
245    where
246        B: SplitByteSlice,
247    {
248        ParsedAssocReqFrame::parse(bytes)
249    }
250}
251
252impl TaggedVariant<MgmtFrame> for AssocReqFrame {
253    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ASSOC_REQ;
254}
255
256pub enum AssocRespFrame {}
257
258impl AsBuffer<AssocRespFrame> for fidl_tap::TxArgs {
259    fn as_buffer(&self) -> Option<&[u8]> {
260        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
261            .and_then(|frame| AssocRespFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
262    }
263}
264
265impl Parse for AssocRespFrame {
266    type Output<B>
267        = ParsedAssocRespFrame<B>
268    where
269        B: SplitByteSlice;
270
271    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
272    where
273        B: SplitByteSlice,
274    {
275        ParsedAssocRespFrame::parse(bytes)
276    }
277}
278
279impl TaggedVariant<MgmtFrame> for AssocRespFrame {
280    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ASSOC_RESP;
281}
282
283pub enum AuthFrame {}
284
285impl AsBuffer<AuthFrame> for fidl_tap::TxArgs {
286    fn as_buffer(&self) -> Option<&[u8]> {
287        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
288            .and_then(|frame| AuthFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
289    }
290}
291
292impl Parse for AuthFrame {
293    type Output<B>
294        = ParsedAuthFrame<B>
295    where
296        B: SplitByteSlice;
297
298    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
299    where
300        B: SplitByteSlice,
301    {
302        ParsedAuthFrame::parse(bytes)
303    }
304}
305
306impl TaggedVariant<MgmtFrame> for AuthFrame {
307    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::AUTH;
308}
309
310pub enum ProbeReqFrame {}
311
312impl AsBuffer<ProbeReqFrame> for fidl_tap::TxArgs {
313    fn as_buffer(&self) -> Option<&[u8]> {
314        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
315            .and_then(|frame| ProbeReqFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
316    }
317}
318
319impl Parse for ProbeReqFrame {
320    type Output<B>
321        = ParsedProbeReqFrame<B>
322    where
323        B: SplitByteSlice;
324
325    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
326    where
327        B: SplitByteSlice,
328    {
329        ParsedProbeReqFrame::parse(bytes)
330    }
331}
332
333impl TaggedVariant<MgmtFrame> for ProbeReqFrame {
334    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::PROBE_REQ;
335}
336
337/// A marker type that indicates that only supported tags of the given type may be parsed.
338///
339/// For example, to extract any supported management frame, the `Buffered<Supported<MgmtFrame>>`
340/// type can be used.
341pub struct Supported<T>(PhantomData<fn() -> T>);
342
343impl<T> Parse for Supported<T>
344where
345    T: Parse + TaggedField,
346{
347    type Output<B>
348        = T::Output<B>
349    where
350        B: SplitByteSlice;
351
352    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
353    where
354        B: SplitByteSlice,
355    {
356        T::parse(bytes).and_then(|parsed| T::tag(&parsed).is_supported().then_some(parsed))
357    }
358}
359
360impl<T, E> AsBuffer<Supported<T>> for E
361where
362    T: Parse + TaggedField,
363    E: AsBuffer<T>,
364{
365    fn as_buffer(&self) -> Option<&[u8]> {
366        AsBuffer::<T>::as_buffer(self)
367    }
368}
369
370/// A marker type that indicates that only **un**supported tags of the given type may be parsed.
371///
372/// For example, to extract any unsupported management frame, the
373/// `Buffered<Unsupported<MgmtFrame>>` type can be used.
374pub struct Unsupported<T>(PhantomData<fn() -> T>);
375
376impl<T> Parse for Unsupported<T>
377where
378    T: Parse + TaggedField,
379{
380    type Output<B>
381        = T::Output<B>
382    where
383        B: SplitByteSlice;
384
385    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
386    where
387        B: SplitByteSlice,
388    {
389        T::parse(bytes).and_then(|parsed| (!T::tag(&parsed).is_supported()).then_some(parsed))
390    }
391}
392
393impl<T, E> AsBuffer<Unsupported<T>> for E
394where
395    T: Parse + TaggedField,
396    E: AsBuffer<T>,
397{
398    fn as_buffer(&self) -> Option<&[u8]> {
399        AsBuffer::<T>::as_buffer(self)
400    }
401}
402
403/// A buffered `zerocopy` type that owns a copy of its underlying buffer.
404#[derive(Clone, Debug)]
405pub struct Buffered<T>
406where
407    T: Parse,
408{
409    buffer: Vec<u8>,
410    phantom: PhantomData<fn() -> T>,
411}
412
413impl<T> Buffered<T>
414where
415    T: Parse,
416{
417    /// Gets the parsed `zerocopy` type.
418    pub fn get(&self) -> T::Output<&'_ [u8]> {
419        T::parse(self.buffer.as_slice()).expect("buffered data failed to reparse")
420    }
421}
422
423impl<E, T> FromEvent<E> for Buffered<T>
424where
425    E: AsBuffer<T>,
426    T: Parse,
427{
428    fn from_event(event: &E) -> Option<Self> {
429        // The cost of emitting static data is two-fold for `zerocopy` types: any buffer must be
430        // cloned and the type must be parsed once here and likely at least one more time in
431        // handlers. However, this allows handlers and extractors to be very composable and
432        // flexible without exceptionally complex APIs and type bounds. See `Buffered`.
433        event.as_buffer().and_then(|buffer| {
434            T::parse(buffer).map(|_| Buffered { buffer: buffer.into(), phantom: PhantomData })
435        })
436    }
437}