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> = ParsedMacFrame<B> where B: SplitByteSlice;
98
99    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
100    where
101        B: SplitByteSlice,
102    {
103        ParsedMacFrame::parse(bytes, false)
104    }
105}
106
107impl TaggedField for MacFrame {
108    type Tag = mac::FrameType;
109
110    fn tag<B>(frame: &Self::Output<B>) -> Self::Tag
111    where
112        B: SplitByteSlice,
113    {
114        match frame {
115            ParsedMacFrame::Ctrl { .. } => mac::FrameType::CTRL,
116            ParsedMacFrame::Data { .. } => mac::FrameType::DATA,
117            ParsedMacFrame::Mgmt { .. } => mac::FrameType::MGMT,
118            ParsedMacFrame::Unsupported { ref frame_ctrl } => { *frame_ctrl }.frame_type(),
119        }
120    }
121}
122
123pub enum DataFrame {}
124
125impl AsBuffer<DataFrame> for fidl_tap::TxArgs {
126    fn as_buffer(&self) -> Option<&[u8]> {
127        Some(&self.packet.data)
128    }
129}
130
131impl Parse for DataFrame {
132    type Output<B> = ParsedDataFrame<B> where B: SplitByteSlice;
133
134    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
135    where
136        B: SplitByteSlice,
137    {
138        ParsedDataFrame::parse(bytes, false)
139    }
140}
141
142impl TaggedVariant<MacFrame> for DataFrame {
143    const TAG: <MacFrame as TaggedField>::Tag = mac::FrameType::DATA;
144}
145
146pub enum MgmtFrame {}
147
148impl AsBuffer<MgmtFrame> for fidl_tap::TxArgs {
149    fn as_buffer(&self) -> Option<&[u8]> {
150        Some(&self.packet.data)
151    }
152}
153
154impl Parse for MgmtFrame {
155    type Output<B> = ParsedMgmtFrame<B> where B: SplitByteSlice;
156
157    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
158    where
159        B: SplitByteSlice,
160    {
161        ParsedMgmtFrame::parse(bytes, false)
162    }
163}
164
165impl TaggedField for MgmtFrame {
166    type Tag = mac::MgmtSubtype;
167
168    fn tag<B>(frame: &Self::Output<B>) -> Self::Tag
169    where
170        B: SplitByteSlice,
171    {
172        { frame.mgmt_hdr.frame_ctrl }.mgmt_subtype()
173    }
174}
175
176impl TaggedVariant<MacFrame> for MgmtFrame {
177    const TAG: <MacFrame as TaggedField>::Tag = mac::FrameType::MGMT;
178}
179
180pub enum ActionFrame<const NO_ACK: bool> {}
181
182impl AsBuffer<ActionFrame<false>> for fidl_tap::TxArgs {
183    fn as_buffer(&self) -> Option<&[u8]> {
184        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false).and_then(|frame| {
185            ActionFrame::<false>::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body)
186        })
187    }
188}
189
190impl AsBuffer<ActionFrame<true>> for fidl_tap::TxArgs {
191    fn as_buffer(&self) -> Option<&[u8]> {
192        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false).and_then(|frame| {
193            ActionFrame::<true>::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body)
194        })
195    }
196}
197
198impl<const NO_ACK: bool> Parse for ActionFrame<NO_ACK> {
199    type Output<B> = ParsedActionFrame<NO_ACK, B> where B: SplitByteSlice;
200
201    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
202    where
203        B: SplitByteSlice,
204    {
205        NoAck::<NO_ACK, ActionBody<B>>::parse(bytes)
206    }
207}
208
209impl TaggedVariant<MgmtFrame> for ActionFrame<false> {
210    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ACTION;
211}
212
213impl TaggedVariant<MgmtFrame> for ActionFrame<true> {
214    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ACTION_NO_ACK;
215}
216
217pub enum AssocReqFrame {}
218
219impl AsBuffer<AssocReqFrame> for fidl_tap::TxArgs {
220    fn as_buffer(&self) -> Option<&[u8]> {
221        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
222            .and_then(|frame| AssocReqFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
223    }
224}
225
226impl Parse for AssocReqFrame {
227    type Output<B> = ParsedAssocReqFrame<B> where B: SplitByteSlice;
228
229    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
230    where
231        B: SplitByteSlice,
232    {
233        ParsedAssocReqFrame::parse(bytes)
234    }
235}
236
237impl TaggedVariant<MgmtFrame> for AssocReqFrame {
238    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ASSOC_REQ;
239}
240
241pub enum AssocRespFrame {}
242
243impl AsBuffer<AssocRespFrame> for fidl_tap::TxArgs {
244    fn as_buffer(&self) -> Option<&[u8]> {
245        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
246            .and_then(|frame| AssocRespFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
247    }
248}
249
250impl Parse for AssocRespFrame {
251    type Output<B> = ParsedAssocRespFrame<B> where B: SplitByteSlice;
252
253    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
254    where
255        B: SplitByteSlice,
256    {
257        ParsedAssocRespFrame::parse(bytes)
258    }
259}
260
261impl TaggedVariant<MgmtFrame> for AssocRespFrame {
262    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::ASSOC_RESP;
263}
264
265pub enum AuthFrame {}
266
267impl AsBuffer<AuthFrame> for fidl_tap::TxArgs {
268    fn as_buffer(&self) -> Option<&[u8]> {
269        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
270            .and_then(|frame| AuthFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
271    }
272}
273
274impl Parse for AuthFrame {
275    type Output<B> = ParsedAuthFrame<B> where B: SplitByteSlice;
276
277    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
278    where
279        B: SplitByteSlice,
280    {
281        ParsedAuthFrame::parse(bytes)
282    }
283}
284
285impl TaggedVariant<MgmtFrame> for AuthFrame {
286    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::AUTH;
287}
288
289pub enum ProbeReqFrame {}
290
291impl AsBuffer<ProbeReqFrame> for fidl_tap::TxArgs {
292    fn as_buffer(&self) -> Option<&[u8]> {
293        ParsedMgmtFrame::parse(self.packet.data.as_slice(), false)
294            .and_then(|frame| ProbeReqFrame::has_tag(MgmtFrame::tag(&frame)).then_some(frame.body))
295    }
296}
297
298impl Parse for ProbeReqFrame {
299    type Output<B> = ParsedProbeReqFrame<B> where B: SplitByteSlice;
300
301    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
302    where
303        B: SplitByteSlice,
304    {
305        ParsedProbeReqFrame::parse(bytes)
306    }
307}
308
309impl TaggedVariant<MgmtFrame> for ProbeReqFrame {
310    const TAG: <MgmtFrame as TaggedField>::Tag = mac::MgmtSubtype::PROBE_REQ;
311}
312
313/// A marker type that indicates that only supported tags of the given type may be parsed.
314///
315/// For example, to extract any supported management frame, the `Buffered<Supported<MgmtFrame>>`
316/// type can be used.
317pub struct Supported<T>(PhantomData<fn() -> T>);
318
319impl<T> Parse for Supported<T>
320where
321    T: Parse + TaggedField,
322{
323    type Output<B> = T::Output<B> where B: SplitByteSlice;
324
325    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
326    where
327        B: SplitByteSlice,
328    {
329        T::parse(bytes).and_then(|parsed| T::tag(&parsed).is_supported().then_some(parsed))
330    }
331}
332
333impl<T, E> AsBuffer<Supported<T>> for E
334where
335    T: Parse + TaggedField,
336    E: AsBuffer<T>,
337{
338    fn as_buffer(&self) -> Option<&[u8]> {
339        AsBuffer::<T>::as_buffer(self)
340    }
341}
342
343/// A marker type that indicates that only **un**supported tags of the given type may be parsed.
344///
345/// For example, to extract any unsupported management frame, the
346/// `Buffered<Unsupported<MgmtFrame>>` type can be used.
347pub struct Unsupported<T>(PhantomData<fn() -> T>);
348
349impl<T> Parse for Unsupported<T>
350where
351    T: Parse + TaggedField,
352{
353    type Output<B> = T::Output<B> where B: SplitByteSlice;
354
355    fn parse<B>(bytes: B) -> Option<Self::Output<B>>
356    where
357        B: SplitByteSlice,
358    {
359        T::parse(bytes).and_then(|parsed| (!T::tag(&parsed).is_supported()).then_some(parsed))
360    }
361}
362
363impl<T, E> AsBuffer<Unsupported<T>> for E
364where
365    T: Parse + TaggedField,
366    E: AsBuffer<T>,
367{
368    fn as_buffer(&self) -> Option<&[u8]> {
369        AsBuffer::<T>::as_buffer(self)
370    }
371}
372
373/// A buffered `zerocopy` type that owns a copy of its underlying buffer.
374#[derive(Clone, Debug)]
375pub struct Buffered<T>
376where
377    T: Parse,
378{
379    buffer: Vec<u8>,
380    phantom: PhantomData<fn() -> T>,
381}
382
383impl<T> Buffered<T>
384where
385    T: Parse,
386{
387    /// Gets the parsed `zerocopy` type.
388    pub fn get(&self) -> T::Output<&'_ [u8]> {
389        T::parse(self.buffer.as_slice()).expect("buffered data failed to reparse")
390    }
391}
392
393impl<E, T> FromEvent<E> for Buffered<T>
394where
395    E: AsBuffer<T>,
396    T: Parse,
397{
398    fn from_event(event: &E) -> Option<Self> {
399        // The cost of emitting static data is two-fold for `zerocopy` types: any buffer must be
400        // cloned and the type must be parsed once here and likely at least one more time in
401        // handlers. However, this allows handlers and extractors to be very composable and
402        // flexible without exceptionally complex APIs and type bounds. See `Buffered`.
403        event.as_buffer().and_then(|buffer| {
404            T::parse(buffer).map(|_| Buffered { buffer: buffer.into(), phantom: PhantomData })
405        })
406    }
407}