wlan_common/test_utils/
fake_stas.rs

1// Copyright 2021 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 crate::channel::{Cbw, Channel};
6use crate::ie::fake_ies::fake_wmm_param;
7use crate::ie::{self, write_wmm_param, IeType};
8use crate::mac;
9use crate::test_utils::fake_frames::{
10    fake_eap_rsne, fake_wpa1_ie, fake_wpa2_enterprise_rsne, fake_wpa2_rsne,
11    fake_wpa2_tkip_ccmp_rsne, fake_wpa2_tkip_only_rsne, fake_wpa2_wpa3_rsne,
12    fake_wpa3_enterprise_192_bit_rsne, fake_wpa3_rsne, fake_wpa3_transition_rsne,
13};
14use anyhow::Context;
15use ieee80211::Ssid;
16use num_derive::FromPrimitive;
17use num_traits::FromPrimitive;
18use rand::distr::{Distribution, StandardUniform};
19use rand::Rng;
20use {
21    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
22    fidl_fuchsia_wlan_sme as fidl_sme,
23};
24
25#[rustfmt::skip]
26const DEFAULT_MOCK_IES: &'static [u8] = &[
27    // DS parameter set: channel 140
28    0x03, 0x01, 0x8c,
29    // TIM - DTIM count: 0, DTIM period: 1, PVB: 2
30    0x05, 0x04, 0x00, 0x01, 0x00, 0x02,
31    // Country info
32    0x07, 0x10, 0x55, 0x53, 0x20, // US, Any environment
33    0x24, 0x04, 0x24, // 1st channel: 36, # channels: 4, maximum tx power: 36 dBm
34    0x34, 0x04, 0x1e, // 1st channel: 52, # channels: 4, maximum tx power: 30 dBm
35    0x64, 0x0c, 0x1e, // 1st channel: 100, # channels: 12, maximum tx power: 30 dBm
36    0x95, 0x05, 0x24, // 1st channel: 149, # channels: 5, maximum tx power: 36 dBm
37    0x00, // padding
38    // Power constraint: 0
39    0x20, 0x01, 0x00,
40    // TPC Report Transmit Power: 9, Link Margin: 0
41    0x23, 0x02, 0x09, 0x00,
42    // HT Capabilities
43    0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
44    0x17, // A-MPDU parameters
45    0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46    0x00, // MCS set
47    0x00, 0x00, // HT extended capabilities
48    0x00, 0x00, 0x00, 0x00, // Transmit beamforming
49    0x00, // Antenna selection capabilities
50    // HT Operation
51    0x3d, 0x16, 0x8c, // Primary channel: 140
52    0x0d, // HT info subset - secondary channel above, any channel width, RIFS permitted
53    0x16, 0x00, 0x00, 0x00, // HT info subsets
54    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55    0x00, // Basic MCS set
56    // Extended Capabilities: extended channel switching, BSS transition, operating mode notification
57    0x7f, 0x08, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40,
58    // VHT Capabilities
59    0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
60    0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
61    // VHT Operation
62    0xc0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
63    // VHT Tx Power Envelope
64    0xc3, 0x03, 0x01, 0x24, 0x24,
65    // Aruba, Hewlett Packard vendor-specific IE
66    0xdd, 0x07, 0x00, 0x0b, 0x86, 0x01, 0x04, 0x08, 0x09,
67];
68
69pub struct BssDescriptionCreator {
70    // *** Fields already in fidl_common::BssDescription
71    pub bssid: [u8; 6],
72    pub bss_type: fidl_common::BssType,
73    pub beacon_period: u16,
74    pub channel: Channel,
75    pub rssi_dbm: i8,
76    pub snr_db: i8,
77
78    // *** Custom arguments
79    pub protection_cfg: FakeProtectionCfg,
80    pub ssid: Ssid,
81    pub rates: Vec<u8>,
82    pub wmm_param: Option<ie::WmmParam>,
83
84    // *** Modifiable capability_info bits
85    // The privacy, ess, and ibss bits are reserved for the
86    // macro to set since they are implied by protection_cfg
87    // and bss_type.
88    pub cf_pollable: bool,
89    pub cf_poll_req: bool,
90    pub short_preamble: bool,
91    pub spectrum_mgmt: bool,
92    pub qos: bool,
93    pub short_slot_time: bool,
94    pub apsd: bool,
95    pub radio_measurement: bool,
96    pub delayed_block_ack: bool,
97    pub immediate_block_ack: bool,
98
99    pub ies_overrides: IesOverrides,
100}
101
102impl BssDescriptionCreator {
103    pub fn create_bss_description(self) -> Result<fidl_common::BssDescription, anyhow::Error> {
104        let mut ies_updater = ie::IesUpdater::new(DEFAULT_MOCK_IES.to_vec());
105        ies_updater.set(IeType::SSID, &self.ssid[..]).context("set SSID")?;
106
107        let rates_writer = ie::RatesWriter::try_new(&self.rates[..]).context("set rates")?;
108        let mut rates_buf = vec![];
109        rates_writer.write_supported_rates(&mut rates_buf);
110        ies_updater.set_raw(&rates_buf[..]).context("set rates")?;
111
112        let mut ext_rates_buf = vec![];
113        rates_writer.write_extended_supported_rates(&mut ext_rates_buf);
114        ies_updater.set_raw(&ext_rates_buf[..]).context("set extended rates")?;
115
116        if let Some(rsne) = derive_rsne(self.protection_cfg) {
117            ies_updater.set_raw(&rsne[..]).context("set RSNE")?;
118        }
119        if let Some(wpa1_vendor_ie) = derive_wpa1_vendor_ies(self.protection_cfg) {
120            ies_updater.set_raw(&wpa1_vendor_ie[..]).context("set WPA1 vendor IE")?;
121        }
122
123        if let Some(wmm_param) = self.wmm_param {
124            let mut wmm_param_vendor_ie = vec![];
125            write_wmm_param(&mut wmm_param_vendor_ie, &wmm_param)
126                .context("failed to write WmmParam to vendor IE buffer")?;
127            ies_updater.set_raw(&wmm_param_vendor_ie[..]).context("set WMM parameter IE")?;
128        }
129
130        let capability_info = mac::CapabilityInfo(0)
131            .with_cf_pollable(self.cf_pollable)
132            .with_cf_poll_req(self.cf_poll_req)
133            .with_short_preamble(self.short_preamble)
134            .with_spectrum_mgmt(self.spectrum_mgmt)
135            .with_qos(self.qos)
136            .with_short_slot_time(self.short_slot_time)
137            .with_apsd(self.apsd)
138            .with_radio_measurement(self.radio_measurement)
139            .with_delayed_block_ack(self.delayed_block_ack)
140            .with_immediate_block_ack(self.immediate_block_ack);
141
142        // Some values of capability_info are not permitted to be set by
143        // the macro since otherwise the BssDescription will be trivially invalid.
144        let capability_info = match self.protection_cfg {
145            FakeProtectionCfg::Open => capability_info.with_privacy(false),
146            _ => capability_info.with_privacy(true),
147        };
148        let capability_info = match self.bss_type {
149            fidl_common::BssType::Infrastructure => capability_info.with_ess(true).with_ibss(false),
150            _ => panic!("{:?} is not supported", self.bss_type),
151        };
152        let capability_info = capability_info.0;
153
154        for ovr in self.ies_overrides.overrides {
155            match ovr {
156                IeOverride::Remove(ie_type) => ies_updater.remove(&ie_type),
157                IeOverride::Set(ie_type, bytes) => {
158                    ies_updater
159                        .set(ie_type, &bytes[..])
160                        .with_context(|| format!("set IE type: {:?}", ie_type))?;
161                }
162                IeOverride::SetRaw(bytes) => {
163                    ies_updater.set_raw(&bytes[..]).context("set raw IE")?;
164                }
165            }
166        }
167
168        Ok(fidl_common::BssDescription {
169            bssid: self.bssid,
170            bss_type: self.bss_type,
171            beacon_period: self.beacon_period,
172            capability_info,
173            ies: ies_updater.finalize(),
174            channel: self.channel.into(),
175            rssi_dbm: self.rssi_dbm,
176            snr_db: self.snr_db,
177        })
178    }
179}
180
181pub struct IesOverrides {
182    overrides: Vec<IeOverride>,
183}
184
185impl IesOverrides {
186    pub fn new() -> Self {
187        Self { overrides: vec![] }
188    }
189
190    pub fn remove(mut self, ie_type: IeType) -> Self {
191        self.overrides.push(IeOverride::Remove(ie_type));
192        self
193    }
194
195    pub fn set(mut self, ie_type: IeType, bytes: Vec<u8>) -> Self {
196        self.overrides.push(IeOverride::Set(ie_type, bytes));
197        self
198    }
199
200    pub fn set_raw(mut self, bytes: Vec<u8>) -> Self {
201        self.overrides.push(IeOverride::SetRaw(bytes));
202        self
203    }
204}
205
206enum IeOverride {
207    Remove(IeType),
208    Set(IeType, Vec<u8>),
209    SetRaw(Vec<u8>),
210}
211
212const LAST_FAKE_PROTECTION_CFG_VALUE: isize = 14;
213
214#[derive(Debug, FromPrimitive, Copy, Clone, PartialEq)]
215pub enum FakeProtectionCfg {
216    Open = 0,
217    Wep,
218    Wpa1,
219    Wpa1Enhanced,
220    Wpa1Wpa2TkipOnly,
221    Wpa2TkipOnly,
222    Wpa1Wpa2,
223    Wpa2TkipCcmp,
224    Wpa2Enterprise,
225    Wpa2,
226    Wpa2Wpa3,
227    Wpa3Transition,
228    Wpa3,
229    Wpa3Enterprise,
230    Eap = LAST_FAKE_PROTECTION_CFG_VALUE,
231}
232
233impl Distribution<FakeProtectionCfg> for StandardUniform {
234    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> FakeProtectionCfg {
235        // NB: rand does not provide uniform distribution for isize.
236        let r = rng.random_range(0..usize::try_from(LAST_FAKE_PROTECTION_CFG_VALUE + 1).unwrap());
237        FromPrimitive::from_usize(r)
238            .unwrap_or_else(|| panic!("Out of range random value for FakeProtectionCfg: {:?}", r))
239    }
240}
241
242impl From<fidl_sme::Protection> for FakeProtectionCfg {
243    fn from(protection: fidl_sme::Protection) -> Self {
244        match protection {
245            fidl_sme::Protection::Unknown => panic!("unknown protection"),
246            fidl_sme::Protection::Open => FakeProtectionCfg::Open,
247            fidl_sme::Protection::Wep => FakeProtectionCfg::Wep,
248            fidl_sme::Protection::Wpa1 => FakeProtectionCfg::Wpa1,
249            fidl_sme::Protection::Wpa1Wpa2PersonalTkipOnly => FakeProtectionCfg::Wpa1Wpa2TkipOnly,
250            fidl_sme::Protection::Wpa2PersonalTkipOnly => FakeProtectionCfg::Wpa2TkipOnly,
251            fidl_sme::Protection::Wpa1Wpa2Personal => FakeProtectionCfg::Wpa1Wpa2,
252            fidl_sme::Protection::Wpa2Personal => FakeProtectionCfg::Wpa2,
253            fidl_sme::Protection::Wpa2Wpa3Personal => FakeProtectionCfg::Wpa2Wpa3,
254            fidl_sme::Protection::Wpa3Personal => FakeProtectionCfg::Wpa3,
255            fidl_sme::Protection::Wpa2Enterprise => FakeProtectionCfg::Wpa2Enterprise,
256            fidl_sme::Protection::Wpa3Enterprise => FakeProtectionCfg::Wpa3Enterprise,
257        }
258    }
259}
260
261pub fn build_fake_bss_description_creator__(
262    protection_cfg: FakeProtectionCfg,
263) -> BssDescriptionCreator {
264    BssDescriptionCreator {
265        bssid: [0x07, 0x01, 0x02, 0x4d, 0x35, 0x08],
266        bss_type: fidl_common::BssType::Infrastructure,
267        beacon_period: 100,
268        channel: Channel::new(3, Cbw::Cbw40),
269        rssi_dbm: 0,
270        snr_db: 0,
271
272        protection_cfg,
273        ssid: Ssid::try_from("fake-ssid").unwrap(),
274        rates: vec![0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c],
275        wmm_param: Some(fake_wmm_param()),
276
277        cf_pollable: false,
278        cf_poll_req: false,
279        short_preamble: false,
280        spectrum_mgmt: false,
281        qos: false,
282        short_slot_time: false,
283        apsd: false,
284        radio_measurement: false,
285        delayed_block_ack: false,
286        immediate_block_ack: false,
287
288        ies_overrides: IesOverrides::new(),
289    }
290}
291
292fn random_ecw_min_max(rng: &mut rand::prelude::ThreadRng) -> ie::EcwMinMax {
293    let min = rng.random_range(0x0..0xf);
294    let max = rng.random_range(min..0xf);
295    ie::EcwMinMax((max << 4) | min)
296}
297
298pub fn build_random_bss_description_creator__(
299    protection_cfg: FakeProtectionCfg,
300) -> BssDescriptionCreator {
301    // Only the Infrastructure BSS type is supported.
302    let bss_type = fidl_common::BssType::Infrastructure;
303
304    let mut rng = rand::rng();
305
306    // Random rates
307    let mut rates: Vec<u8> = vec![];
308    for _ in
309        0..rng.random_range(1..ie::SUPPORTED_RATES_MAX_LEN + ie::EXTENDED_SUPPORTED_RATES_MAX_LEN)
310    {
311        rates.push(rng.random());
312    }
313    let rates = rates; // shadow to make rates immutable
314
315    // Random IE bytes
316    let mut giant_vendor_ies = vec![];
317    for _j in 0..8 {
318        giant_vendor_ies.extend_from_slice(&[221, 250]);
319        giant_vendor_ies.extend((0..250).map(|_| rng.random::<u8>()))
320    }
321    let ies_overrides = IesOverrides::new().set_raw(giant_vendor_ies);
322
323    let qos: bool = rng.random();
324    let apsd: bool = qos && rng.random(); // APSD is a QoS capability, so the AP must also be QoS capable
325
326    BssDescriptionCreator {
327        bssid: (0..6).map(|_| rng.random::<u8>()).collect::<Vec<u8>>().try_into().unwrap(),
328        bss_type,
329        beacon_period: rng.random::<u16>(),
330        // TODO(https://fxbug.dev/42162492): Purely random valid channel values is not implemented.
331        channel: Channel::new(rng.random_range(1..255), Cbw::Cbw20),
332        rssi_dbm: rng.random::<i8>(),
333        snr_db: rng.random::<i8>(),
334
335        protection_cfg,
336        ssid: Ssid::from_bytes_unchecked(
337            (0..fidl_ieee80211::MAX_SSID_BYTE_LEN).map(|_| rng.random::<u8>()).collect::<Vec<u8>>(),
338        ),
339        rates,
340
341        // WMM is independent of 802.11 QoS, so the capabilities randomly indicated
342        // in wmm_param are not related to the QoS and APSD bits in the Capability Information
343        // field indicated. See WMM Specification, Section 1.3.
344        wmm_param: if rng.random() {
345            Some(ie::WmmParam {
346                wmm_info: ie::WmmInfo(0).with_ap_wmm_info(
347                    ie::ApWmmInfo(0)
348                        .with_parameter_set_count(rng.random_range(0x00..0xf))
349                        .with_uapsd(rng.random()),
350                ),
351                _reserved: rng.random(),
352                ac_be_params: ie::WmmAcParams {
353                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.random_range(2..0xf)).with_aci(0),
354                    ecw_min_max: random_ecw_min_max(&mut rng),
355                    txop_limit: rng.random(),
356                },
357                ac_bk_params: ie::WmmAcParams {
358                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.random_range(2..0xf)).with_aci(1),
359                    ecw_min_max: random_ecw_min_max(&mut rng),
360                    txop_limit: rng.random(),
361                },
362                ac_vi_params: ie::WmmAcParams {
363                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.random_range(2..0xf)).with_aci(2),
364                    ecw_min_max: random_ecw_min_max(&mut rng),
365                    txop_limit: rng.random(),
366                },
367                ac_vo_params: ie::WmmAcParams {
368                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.random_range(2..0xf)).with_aci(3),
369                    ecw_min_max: random_ecw_min_max(&mut rng),
370                    txop_limit: rng.random(),
371                },
372            })
373        } else {
374            None
375        },
376
377        cf_pollable: rng.random(),
378        cf_poll_req: rng.random(),
379        short_preamble: rng.random(),
380        spectrum_mgmt: rng.random(),
381        qos,
382        short_slot_time: rng.random(),
383        apsd,
384        radio_measurement: rng.random(),
385        delayed_block_ack: rng.random(),
386        immediate_block_ack: rng.random(),
387
388        // Generating completely random IEs would be chaotic at best, so we
389        // generate some random vendor ies instead.
390        ies_overrides,
391    }
392}
393
394fn derive_rsne(protection_cfg: FakeProtectionCfg) -> Option<Vec<u8>> {
395    match protection_cfg {
396        FakeProtectionCfg::Wpa3Enterprise => Some(fake_wpa3_enterprise_192_bit_rsne()),
397        FakeProtectionCfg::Wpa2Enterprise => Some(fake_wpa2_enterprise_rsne()),
398        FakeProtectionCfg::Wpa3 => Some(fake_wpa3_rsne()),
399        FakeProtectionCfg::Wpa3Transition => Some(fake_wpa3_transition_rsne()),
400        FakeProtectionCfg::Wpa2Wpa3 => Some(fake_wpa2_wpa3_rsne()),
401        FakeProtectionCfg::Wpa2TkipCcmp => Some(fake_wpa2_tkip_ccmp_rsne()),
402        FakeProtectionCfg::Wpa1Wpa2TkipOnly | FakeProtectionCfg::Wpa2TkipOnly => {
403            Some(fake_wpa2_tkip_only_rsne())
404        }
405        FakeProtectionCfg::Wpa1Wpa2 | FakeProtectionCfg::Wpa2 => Some(fake_wpa2_rsne()),
406        FakeProtectionCfg::Eap => Some(fake_eap_rsne()),
407        _ => None,
408    }
409}
410
411fn derive_wpa1_vendor_ies(protection_cfg: FakeProtectionCfg) -> Option<Vec<u8>> {
412    match protection_cfg {
413        FakeProtectionCfg::Wpa1
414        | FakeProtectionCfg::Wpa1Wpa2TkipOnly
415        | FakeProtectionCfg::Wpa1Wpa2 => Some(fake_wpa1_ie(false)),
416        FakeProtectionCfg::Wpa1Enhanced => Some(fake_wpa1_ie(true)),
417        _ => None,
418    }
419}
420
421#[macro_export]
422macro_rules! fake_fidl_bss_description__ {
423    ($build_fake_bss_description_creator__:path, $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)*) => {{
424        let bss_description_creator = $crate::test_utils::fake_stas::BssDescriptionCreator {
425            $(
426                $bss_key: $bss_value,
427            )*
428            ..$build_fake_bss_description_creator__($fake_protection_cfg.into())
429        };
430        bss_description_creator.create_bss_description().expect("expect creating BSS to succeed")
431    }};
432}
433
434#[macro_export]
435macro_rules! fake_fidl_bss_description {
436    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
437        $crate::fake_fidl_bss_description__!(
438            $crate::test_utils::fake_stas::build_fake_bss_description_creator__,
439            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name
440                $(, $bss_key: $bss_value)*)
441    }};
442    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
443        $crate::fake_fidl_bss_description__!(
444            $crate::test_utils::fake_stas::build_fake_bss_description_creator__,
445            $fake_protection_cfg
446                $(, $bss_key: $bss_value)*)
447    }};
448}
449
450#[macro_export]
451macro_rules! random_fidl_bss_description {
452    ($($bss_key:ident: $bss_value:expr),* $(,)?) => {{
453        let mut rng = rand::rng();
454        $crate::fake_fidl_bss_description__!(
455            $crate::test_utils::fake_stas::build_random_bss_description_creator__,
456            rng.random::<$crate::test_utils::fake_stas::FakeProtectionCfg>()
457                $(, $bss_key: $bss_value)*)
458    }};
459    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
460        $crate::fake_fidl_bss_description__!(
461            $crate::test_utils::fake_stas::build_random_bss_description_creator__,
462            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name
463                $(, $bss_key: $bss_value)*)
464    }};
465    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
466        $crate::fake_fidl_bss_description__!(
467            $crate::test_utils::fake_stas::build_random_bss_description_creator__,
468            $fake_protection_cfg
469                $(, $bss_key: $bss_value)*)
470    }};
471}
472
473#[macro_export]
474macro_rules! fake_bss_description__ {
475    ($fidl_bss_description_macro:ident, $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
476        let fidl_bss = $crate::$fidl_bss_description_macro!(protection => $fake_protection_cfg $(, $bss_key: $bss_value)*);
477        let bss_description: $crate::bss::BssDescription = std::convert::TryFrom::try_from(fidl_bss)
478            .expect("expect BSS conversion to succeed");
479        bss_description
480    }}
481}
482
483#[macro_export]
484macro_rules! fake_bss_description {
485    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
486        $crate::fake_bss_description__!(
487            fake_fidl_bss_description,
488            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name $(, $bss_key: $bss_value)*)
489    }};
490    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
491        $crate::fake_bss_description__!(
492            fake_fidl_bss_description,
493            $fake_protection_cfg $(, $bss_key: $bss_value)*)
494    }};
495}
496
497#[macro_export]
498macro_rules! random_bss_description {
499    ($($bss_key:ident: $bss_value:expr),* $(,)?) => {{
500        let mut rng = rand::rng();
501        $crate::fake_bss_description__!(
502            random_fidl_bss_description,
503            rng.random::<$crate::test_utils::fake_stas::FakeProtectionCfg>()
504                $(, $bss_key: $bss_value)*)
505    }};
506    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
507        $crate::fake_bss_description__!(
508            random_fidl_bss_description,
509            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name $(, $bss_key: $bss_value)*)
510    }};
511    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
512        $crate::fake_bss_description__!(
513            random_fidl_bss_description,
514            $fake_protection_cfg $(, $bss_key: $bss_value)*)
515    }};
516}
517
518#[cfg(test)]
519mod tests {
520    use super::*;
521    use crate::bss::{BssDescription, Protection};
522    use crate::test_utils::fake_frames::{fake_wmm_param_body, fake_wmm_param_header};
523
524    #[test]
525    fn check_simplest_macro_use() {
526        let fidl_bss_description = fake_fidl_bss_description!(Open);
527        let bss_description = fake_bss_description!(Open);
528        assert_eq!(
529            BssDescription::try_from(fidl_bss_description)
530                .expect("Failed to convert fake_fidl_bss_description value"),
531            bss_description
532        );
533
534        for i in 1..=11 {
535            if i > 10 {
536                panic!("random_bss_description is always equal to bss_description");
537            }
538
539            let random_fidl_bss_description = random_fidl_bss_description!(Open);
540            let random_bss_description =
541                BssDescription::try_from(random_fidl_bss_description.clone())
542                    .expect("Failed to convert random_fidl_bss_description value");
543            if random_bss_description != bss_description {
544                break;
545            }
546        }
547
548        for i in 1..=11 {
549            if i > 10 {
550                panic!("random_bss_description is always equal to other_random_bss_description");
551            }
552
553            let random_fidl_bss_description = random_fidl_bss_description!(Open);
554            let random_bss_description =
555                BssDescription::try_from(random_fidl_bss_description.clone())
556                    .expect("Failed to convert random_fidl_bss_description value");
557            let other_random_bss_description = random_bss_description!(Open);
558            if random_bss_description != other_random_bss_description {
559                break;
560            }
561        }
562    }
563
564    #[test]
565    fn fake_protection_cfg_from_primitive() {
566        assert!(
567            0 <= LAST_FAKE_PROTECTION_CFG_VALUE,
568            "LAST_FAKE_PROTECTION_CFG_VALUE is not positive: {}",
569            LAST_FAKE_PROTECTION_CFG_VALUE,
570        );
571
572        let too_low: Option<FakeProtectionCfg> = FromPrimitive::from_isize(-1);
573        assert_eq!(
574            too_low, None::<FakeProtectionCfg>,
575            "Successfully converted low out of range FakeProtectionCfg value"
576        );
577
578        for i in 0..(LAST_FAKE_PROTECTION_CFG_VALUE + 1) {
579            let _: FakeProtectionCfg = FromPrimitive::from_isize(i).unwrap_or_else(|| {
580                panic!("Failed to convert {:?} to a FakeProtectionCfg value", i)
581            });
582        }
583
584        let too_high: Option<FakeProtectionCfg> =
585            FromPrimitive::from_isize(LAST_FAKE_PROTECTION_CFG_VALUE + 1);
586        assert_eq!(
587            too_high, None::<FakeProtectionCfg>,
588            "Successfully converted high out of range FakeProtectionCfg value"
589        );
590    }
591
592    #[test]
593    fn fake_protection_cfg_expr_syntax() {
594        let fidl_bss_description =
595            fake_fidl_bss_description!(protection => FakeProtectionCfg::Open);
596        assert_eq!(
597            BssDescription::try_from(fidl_bss_description)
598                .expect("Failed to convert fake_fidl_bss_description value")
599                .protection(),
600            Protection::Open
601        );
602
603        let fidl_bss_description =
604            random_fidl_bss_description!(protection => FakeProtectionCfg::Open);
605        assert_eq!(
606            BssDescription::try_from(fidl_bss_description)
607                .expect("Failed to convert random_fidl_bss_description value")
608                .protection(),
609            Protection::Open
610        );
611
612        let bss_description = fake_bss_description!(protection => FakeProtectionCfg::Open);
613        assert_eq!(bss_description.protection(), Protection::Open);
614
615        let bss_description = random_bss_description!(protection => FakeProtectionCfg::Open);
616        assert_eq!(bss_description.protection(), Protection::Open);
617    }
618
619    #[test]
620    fn fake_protection_cfg_privacy_bit_and_protection() {
621        let bss = fake_bss_description!(Open);
622        assert!(!mac::CapabilityInfo(bss.capability_info).privacy());
623        assert_eq!(bss.protection(), Protection::Open);
624
625        let bss = fake_bss_description!(Wep);
626        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
627        assert_eq!(bss.protection(), Protection::Wep);
628
629        let bss = fake_bss_description!(Wpa1);
630        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
631        assert_eq!(bss.protection(), Protection::Wpa1);
632
633        let bss = fake_bss_description!(Wpa2);
634        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
635        assert_eq!(bss.protection(), Protection::Wpa2Personal);
636    }
637
638    #[test]
639    fn fake_protection_cfg_privacy_bit_and_protection_in_random_bss() {
640        let bss = random_bss_description!(Open);
641        assert!(!mac::CapabilityInfo(bss.capability_info).privacy());
642        assert_eq!(bss.protection(), Protection::Open);
643
644        let bss = random_bss_description!(Wep);
645        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
646        assert_eq!(bss.protection(), Protection::Wep);
647
648        let bss = random_bss_description!(Wpa1);
649        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
650        assert_eq!(bss.protection(), Protection::Wpa1);
651
652        let bss = random_bss_description!(Wpa2);
653        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
654        assert_eq!(bss.protection(), Protection::Wpa2Personal);
655    }
656
657    #[test]
658    fn set_capability_info_bits() {
659        macro_rules! check_bit {
660            ($bit_name:ident) => {{
661                let bss = fake_bss_description!(Open, $bit_name: true);
662                assert!(mac::CapabilityInfo(bss.capability_info).$bit_name());
663                let bss = fake_bss_description!(Open, $bit_name: false);
664                assert!(!mac::CapabilityInfo(bss.capability_info).$bit_name());
665            }}
666        }
667        check_bit!(cf_pollable);
668        check_bit!(cf_poll_req);
669        check_bit!(short_preamble);
670        check_bit!(spectrum_mgmt);
671        check_bit!(qos);
672        check_bit!(short_slot_time);
673        check_bit!(apsd);
674        check_bit!(radio_measurement);
675        check_bit!(delayed_block_ack);
676        check_bit!(immediate_block_ack);
677
678        let bss =
679            fake_bss_description!(Open, cf_pollable: true, apsd: false, immediate_block_ack: true);
680        assert!(mac::CapabilityInfo(bss.capability_info).cf_pollable());
681        assert!(!mac::CapabilityInfo(bss.capability_info).apsd());
682        assert!(mac::CapabilityInfo(bss.capability_info).immediate_block_ack());
683    }
684
685    #[test]
686    fn simple_default_override() {
687        let bss = fake_fidl_bss_description!(Open);
688        assert_eq!(bss.beacon_period, 100);
689
690        let bss = fake_fidl_bss_description!(Open, beacon_period: 50);
691        assert_eq!(bss.beacon_period, 50);
692    }
693
694    #[test]
695    #[should_panic(expected = "Personal is not supported")]
696    // TODO(https://fxbug.dev/42169733): LeakSanitizer flags leaks caused by panic.
697    #[cfg_attr(feature = "variant_asan", ignore)]
698    #[cfg_attr(feature = "variant_hwasan", ignore)]
699    fn unsupported_bss_type() {
700        fake_fidl_bss_description!(Open, bss_type: fidl_common::BssType::Personal);
701    }
702
703    #[test]
704    fn any_protection_syntax() {
705        let _ = random_fidl_bss_description!();
706        let _ = random_bss_description!();
707    }
708
709    #[test]
710    fn random_fidl_bss_decription_override() {
711        let random_bss = random_bss_description!(ssid: Ssid::try_from("foo").unwrap());
712        assert_eq!(random_bss.ssid, Ssid::try_from("foo").unwrap());
713    }
714
715    #[test]
716    fn valid_random_ecw_min_max() {
717        let mut rng = rand::rng();
718        for _ in 0..100 {
719            let ecw_min_max = random_ecw_min_max(&mut rng);
720            assert!(ecw_min_max.ecw_max() >= ecw_min_max.ecw_min());
721        }
722    }
723
724    #[test]
725    fn random_bss_is_not_constant() {
726        for _ in 0..10 {
727            let random_bss_1 = BssDescription::try_from(random_fidl_bss_description!())
728                .expect("Failed to convert random_bss_description value");
729            let random_bss_2 = BssDescription::try_from(random_fidl_bss_description!())
730                .expect("Failed to convert random_bss_description value");
731            if random_bss_1 != random_bss_2 {
732                return;
733            }
734        }
735        panic!("random bss is always the same");
736    }
737
738    #[test]
739    fn random_bss_protection_is_not_constant() {
740        for _ in 0..10 {
741            let random_bss_1 = BssDescription::try_from(random_fidl_bss_description!())
742                .expect("Failed to convert random_bss_description value");
743            let random_bss_2 = BssDescription::try_from(random_fidl_bss_description!())
744                .expect("Failed to convert random_bss_description value");
745            if random_bss_1.protection() != random_bss_2.protection() {
746                return;
747            }
748        }
749        panic!("random bss protection is always the same");
750    }
751
752    #[test]
753    fn some_random_bss_bits_are_fixed() {
754        for _ in 0..5 {
755            let random_bss = random_fidl_bss_description!(Open);
756            assert_eq!(random_bss.bss_type, fidl_common::BssType::Infrastructure);
757            assert!(mac::CapabilityInfo(random_bss.capability_info).ess());
758            assert!(!mac::CapabilityInfo(random_bss.capability_info).ibss());
759            assert!(!mac::CapabilityInfo(random_bss.capability_info).privacy());
760        }
761    }
762
763    // Test random_bss_description generation of random protection since
764    // it doesn't rely on random_fidl_bss_description for it.
765    #[test]
766    fn random_bss_decription_protection_randomness() {
767        for _ in 0..10 {
768            let random_bss_1 = random_bss_description!();
769            let random_bss_2 = random_bss_description!();
770            if random_bss_1.protection() != random_bss_2.protection() {
771                return;
772            }
773        }
774        panic!("random protection is always the same");
775    }
776
777    #[test]
778    fn ies_overrides() {
779        let bss = fake_bss_description!(Wpa1Wpa2,
780            ssid: Ssid::try_from("fuchsia").unwrap(),
781            rates: vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
782            ies_overrides: IesOverrides::new()
783                .remove(IeType::new_vendor([0x00, 0x0b, 0x86, 0x01, 0x04, 0x08]))
784                .set(IeType::DSSS_PARAM_SET, [136].to_vec()),
785        );
786
787        // Things to note:
788        // - SSID "fuchsia" is inserted.
789        // - Rates and extended supported rates are inserted.
790        // - WPA2 RSNE and WPA1 vendor IE are inserted.
791        // - DSSS Param set's value is changed.
792        // - Aruba vendor IE no longer there.
793        #[rustfmt::skip]
794        let mut expected_ies = vec![
795            // SSID
796            0x00, 0x07, b'f', b'u', b'c', b'h', b's', b'i', b'a',
797            // Rates
798            0x01, 0x08, 11, 12, 13, 14, 15, 16, 17, 18,
799            // DS parameter set: channel 136
800            0x03, 0x01, 136,
801            // TIM - DTIM count: 0, DTIM period: 1, PVB: 2
802            0x05, 0x04, 0x00, 0x01, 0x00, 0x02,
803            // Country info
804            0x07, 0x10, 0x55, 0x53, 0x20, // US, Any environment
805            0x24, 0x04, 0x24, // 1st channel: 36, # channels: 4, maximum tx power: 36 dBm
806            0x34, 0x04, 0x1e, // 1st channel: 52, # channels: 4, maximum tx power: 30 dBm
807            0x64, 0x0c, 0x1e, // 1st channel: 100, # channels: 12, maximum tx power: 30 dBm
808            0x95, 0x05, 0x24, // 1st channel: 149, # channels: 5, maximum tx power: 36 dBm
809            0x00, // padding
810            // Power constraint: 0
811            0x20, 0x01, 0x00,
812            // TPC Report Transmit Power: 9, Link Margin: 0
813            0x23, 0x02, 0x09, 0x00,
814            // HT Capabilities
815            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
816            0x17, // A-MPDU parameters
817            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
818            0x00, 0x00, // MCS set
819            0x00, 0x00, // HT extended capabilities
820            0x00, 0x00, 0x00, 0x00, // Transmit beamforming
821            0x00, // Antenna selection capabilities
822            // RSNE
823            0x30, 18, // Element header
824            1, 0, // Version
825            0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
826            1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
827            1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
828            // Extended supported rates
829            0x32, 0x06, 19, 20, 21, 22, 23, 24,
830            // HT Operation
831            0x3d, 0x16, 0x8c, // Primary channel: 140
832            0x0d, // HT info subset - secondary channel above, any channel width, RIFS permitted
833            0x16, 0x00, 0x00, 0x00, // HT info subsets
834            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
835            0x00, 0x00, // Basic MCS set
836            // Extended Capabilities: extended channel switching, BSS transition, operating mode notification
837            0x7f, 0x08, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40,
838            // VHT Capabilities
839            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
840            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
841            // VHT Operation
842            0xc0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, // VHT Tx Power Envelope
843            0xc3, 0x03, 0x01, 0x24, 0x24,
844            // WPA1 vendor IE
845            0xdd, 0x16, 0x00, 0x50, 0xf2, // IE header
846            0x01, // MSFT specific IE type (WPA)
847            0x01, 0x00, // WPA version
848            0x00, 0x50, 0xf2, 0x02, // multicast cipher: TKIP
849            0x01, 0x00, 0x00, 0x50, 0xf2, 0x02, // 1 unicast cipher
850            0x01, 0x00, 0x00, 0x50, 0xf2, 0x02, // 1 AKM: PSK
851        ];
852        expected_ies.extend(fake_wmm_param_header());
853        expected_ies.extend(fake_wmm_param_body());
854
855        assert_eq!(bss.ies(), &expected_ies[..]);
856    }
857}