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::distributions::{Distribution, Standard};
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 Standard {
234    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> FakeProtectionCfg {
235        let r = rng.gen_range(0..LAST_FAKE_PROTECTION_CFG_VALUE + 1);
236        FromPrimitive::from_isize(r)
237            .unwrap_or_else(|| panic!("Out of range random value for FakeProtectionCfg: {:?}", r))
238    }
239}
240
241impl From<fidl_sme::Protection> for FakeProtectionCfg {
242    fn from(protection: fidl_sme::Protection) -> Self {
243        match protection {
244            fidl_sme::Protection::Unknown => panic!("unknown protection"),
245            fidl_sme::Protection::Open => FakeProtectionCfg::Open,
246            fidl_sme::Protection::Wep => FakeProtectionCfg::Wep,
247            fidl_sme::Protection::Wpa1 => FakeProtectionCfg::Wpa1,
248            fidl_sme::Protection::Wpa1Wpa2PersonalTkipOnly => FakeProtectionCfg::Wpa1Wpa2TkipOnly,
249            fidl_sme::Protection::Wpa2PersonalTkipOnly => FakeProtectionCfg::Wpa2TkipOnly,
250            fidl_sme::Protection::Wpa1Wpa2Personal => FakeProtectionCfg::Wpa1Wpa2,
251            fidl_sme::Protection::Wpa2Personal => FakeProtectionCfg::Wpa2,
252            fidl_sme::Protection::Wpa2Wpa3Personal => FakeProtectionCfg::Wpa2Wpa3,
253            fidl_sme::Protection::Wpa3Personal => FakeProtectionCfg::Wpa3,
254            fidl_sme::Protection::Wpa2Enterprise => FakeProtectionCfg::Wpa2Enterprise,
255            fidl_sme::Protection::Wpa3Enterprise => FakeProtectionCfg::Wpa3Enterprise,
256        }
257    }
258}
259
260pub fn build_fake_bss_description_creator__(
261    protection_cfg: FakeProtectionCfg,
262) -> BssDescriptionCreator {
263    BssDescriptionCreator {
264        bssid: [0x07, 0x01, 0x02, 0x4d, 0x35, 0x08],
265        bss_type: fidl_common::BssType::Infrastructure,
266        beacon_period: 100,
267        channel: Channel::new(3, Cbw::Cbw40),
268        rssi_dbm: 0,
269        snr_db: 0,
270
271        protection_cfg,
272        ssid: Ssid::try_from("fake-ssid").unwrap(),
273        rates: vec![0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c],
274        wmm_param: Some(fake_wmm_param()),
275
276        cf_pollable: false,
277        cf_poll_req: false,
278        short_preamble: false,
279        spectrum_mgmt: false,
280        qos: false,
281        short_slot_time: false,
282        apsd: false,
283        radio_measurement: false,
284        delayed_block_ack: false,
285        immediate_block_ack: false,
286
287        ies_overrides: IesOverrides::new(),
288    }
289}
290
291fn random_ecw_min_max(rng: &mut rand::prelude::ThreadRng) -> ie::EcwMinMax {
292    let min = rng.gen_range(0x0..0xf);
293    let max = rng.gen_range(min..0xf);
294    ie::EcwMinMax((max << 4) | min)
295}
296
297pub fn build_random_bss_description_creator__(
298    protection_cfg: FakeProtectionCfg,
299) -> BssDescriptionCreator {
300    // Only the Infrastructure BSS type is supported.
301    let bss_type = fidl_common::BssType::Infrastructure;
302
303    let mut rng = rand::thread_rng();
304
305    // Random rates
306    let mut rates: Vec<u8> = vec![];
307    for _ in 0..rng.gen_range(1..ie::SUPPORTED_RATES_MAX_LEN + ie::EXTENDED_SUPPORTED_RATES_MAX_LEN)
308    {
309        rates.push(rng.gen());
310    }
311    let rates = rates; // shadow to make rates immutable
312
313    // Random IE bytes
314    let mut giant_vendor_ies = vec![];
315    for _j in 0..8 {
316        giant_vendor_ies.extend_from_slice(&[221, 250]);
317        giant_vendor_ies.extend((0..250).map(|_| rng.gen::<u8>()))
318    }
319    let ies_overrides = IesOverrides::new().set_raw(giant_vendor_ies);
320
321    let qos: bool = rng.gen();
322    let apsd: bool = qos && rng.gen(); // APSD is a QoS capability, so the AP must also be QoS capable
323
324    BssDescriptionCreator {
325        bssid: (0..6).map(|_| rng.gen::<u8>()).collect::<Vec<u8>>().try_into().unwrap(),
326        bss_type,
327        beacon_period: rng.gen::<u16>(),
328        // TODO(https://fxbug.dev/42162492): Purely random valid channel values is not implemented.
329        channel: Channel::new(rng.gen_range(1..255), Cbw::Cbw20),
330        rssi_dbm: rng.gen::<i8>(),
331        snr_db: rng.gen::<i8>(),
332
333        protection_cfg,
334        ssid: Ssid::from_bytes_unchecked(
335            (0..fidl_ieee80211::MAX_SSID_BYTE_LEN).map(|_| rng.gen::<u8>()).collect::<Vec<u8>>(),
336        ),
337        rates,
338
339        // WMM is independent of 802.11 QoS, so the capabilities randomly indicated
340        // in wmm_param are not related to the QoS and APSD bits in the Capability Information
341        // field indicated. See WMM Specification, Section 1.3.
342        wmm_param: if rng.gen() {
343            Some(ie::WmmParam {
344                wmm_info: ie::WmmInfo(0).with_ap_wmm_info(
345                    ie::ApWmmInfo(0)
346                        .with_parameter_set_count(rng.gen_range(0x00..0xf))
347                        .with_uapsd(rng.gen()),
348                ),
349                _reserved: rng.gen(),
350                ac_be_params: ie::WmmAcParams {
351                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.gen_range(2..0xf)).with_aci(0),
352                    ecw_min_max: random_ecw_min_max(&mut rng),
353                    txop_limit: rng.gen(),
354                },
355                ac_bk_params: ie::WmmAcParams {
356                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.gen_range(2..0xf)).with_aci(1),
357                    ecw_min_max: random_ecw_min_max(&mut rng),
358                    txop_limit: rng.gen(),
359                },
360                ac_vi_params: ie::WmmAcParams {
361                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.gen_range(2..0xf)).with_aci(2),
362                    ecw_min_max: random_ecw_min_max(&mut rng),
363                    txop_limit: rng.gen(),
364                },
365                ac_vo_params: ie::WmmAcParams {
366                    aci_aifsn: ie::WmmAciAifsn(0).with_aifsn(rng.gen_range(2..0xf)).with_aci(3),
367                    ecw_min_max: random_ecw_min_max(&mut rng),
368                    txop_limit: rng.gen(),
369                },
370            })
371        } else {
372            None
373        },
374
375        cf_pollable: rng.gen(),
376        cf_poll_req: rng.gen(),
377        short_preamble: rng.gen(),
378        spectrum_mgmt: rng.gen(),
379        qos,
380        short_slot_time: rng.gen(),
381        apsd,
382        radio_measurement: rng.gen(),
383        delayed_block_ack: rng.gen(),
384        immediate_block_ack: rng.gen(),
385
386        // Generating completely random IEs would be chaotic at best, so we
387        // generate some random vendor ies instead.
388        ies_overrides,
389    }
390}
391
392fn derive_rsne(protection_cfg: FakeProtectionCfg) -> Option<Vec<u8>> {
393    match protection_cfg {
394        FakeProtectionCfg::Wpa3Enterprise => Some(fake_wpa3_enterprise_192_bit_rsne()),
395        FakeProtectionCfg::Wpa2Enterprise => Some(fake_wpa2_enterprise_rsne()),
396        FakeProtectionCfg::Wpa3 => Some(fake_wpa3_rsne()),
397        FakeProtectionCfg::Wpa3Transition => Some(fake_wpa3_transition_rsne()),
398        FakeProtectionCfg::Wpa2Wpa3 => Some(fake_wpa2_wpa3_rsne()),
399        FakeProtectionCfg::Wpa2TkipCcmp => Some(fake_wpa2_tkip_ccmp_rsne()),
400        FakeProtectionCfg::Wpa1Wpa2TkipOnly | FakeProtectionCfg::Wpa2TkipOnly => {
401            Some(fake_wpa2_tkip_only_rsne())
402        }
403        FakeProtectionCfg::Wpa1Wpa2 | FakeProtectionCfg::Wpa2 => Some(fake_wpa2_rsne()),
404        FakeProtectionCfg::Eap => Some(fake_eap_rsne()),
405        _ => None,
406    }
407}
408
409fn derive_wpa1_vendor_ies(protection_cfg: FakeProtectionCfg) -> Option<Vec<u8>> {
410    match protection_cfg {
411        FakeProtectionCfg::Wpa1
412        | FakeProtectionCfg::Wpa1Wpa2TkipOnly
413        | FakeProtectionCfg::Wpa1Wpa2 => Some(fake_wpa1_ie(false)),
414        FakeProtectionCfg::Wpa1Enhanced => Some(fake_wpa1_ie(true)),
415        _ => None,
416    }
417}
418
419#[macro_export]
420macro_rules! fake_fidl_bss_description__ {
421    ($build_fake_bss_description_creator__:path, $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)*) => {{
422        let bss_description_creator = $crate::test_utils::fake_stas::BssDescriptionCreator {
423            $(
424                $bss_key: $bss_value,
425            )*
426            ..$build_fake_bss_description_creator__($fake_protection_cfg.into())
427        };
428        bss_description_creator.create_bss_description().expect("expect creating BSS to succeed")
429    }};
430}
431
432#[macro_export]
433macro_rules! fake_fidl_bss_description {
434    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
435        $crate::fake_fidl_bss_description__!(
436            $crate::test_utils::fake_stas::build_fake_bss_description_creator__,
437            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name
438                $(, $bss_key: $bss_value)*)
439    }};
440    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
441        $crate::fake_fidl_bss_description__!(
442            $crate::test_utils::fake_stas::build_fake_bss_description_creator__,
443            $fake_protection_cfg
444                $(, $bss_key: $bss_value)*)
445    }};
446}
447
448#[macro_export]
449macro_rules! random_fidl_bss_description {
450    ($($bss_key:ident: $bss_value:expr),* $(,)?) => {{
451        let mut rng = rand::thread_rng();
452        $crate::fake_fidl_bss_description__!(
453            $crate::test_utils::fake_stas::build_random_bss_description_creator__,
454            rng.gen::<$crate::test_utils::fake_stas::FakeProtectionCfg>()
455                $(, $bss_key: $bss_value)*)
456    }};
457    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
458        $crate::fake_fidl_bss_description__!(
459            $crate::test_utils::fake_stas::build_random_bss_description_creator__,
460            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name
461                $(, $bss_key: $bss_value)*)
462    }};
463    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
464        $crate::fake_fidl_bss_description__!(
465            $crate::test_utils::fake_stas::build_random_bss_description_creator__,
466            $fake_protection_cfg
467                $(, $bss_key: $bss_value)*)
468    }};
469}
470
471#[macro_export]
472macro_rules! fake_bss_description__ {
473    ($fidl_bss_description_macro:ident, $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
474        let fidl_bss = $crate::$fidl_bss_description_macro!(protection => $fake_protection_cfg $(, $bss_key: $bss_value)*);
475        let bss_description: $crate::bss::BssDescription = std::convert::TryFrom::try_from(fidl_bss)
476            .expect("expect BSS conversion to succeed");
477        bss_description
478    }}
479}
480
481#[macro_export]
482macro_rules! fake_bss_description {
483    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
484        $crate::fake_bss_description__!(
485            fake_fidl_bss_description,
486            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name $(, $bss_key: $bss_value)*)
487    }};
488    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
489        $crate::fake_bss_description__!(
490            fake_fidl_bss_description,
491            $fake_protection_cfg $(, $bss_key: $bss_value)*)
492    }};
493}
494
495#[macro_export]
496macro_rules! random_bss_description {
497    ($($bss_key:ident: $bss_value:expr),* $(,)?) => {{
498        let mut rng = rand::thread_rng();
499        $crate::fake_bss_description__!(
500            random_fidl_bss_description,
501            rng.gen::<$crate::test_utils::fake_stas::FakeProtectionCfg>()
502                $(, $bss_key: $bss_value)*)
503    }};
504    ($protection_name:ident $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
505        $crate::fake_bss_description__!(
506            random_fidl_bss_description,
507            $crate::test_utils::fake_stas::FakeProtectionCfg::$protection_name $(, $bss_key: $bss_value)*)
508    }};
509    (protection => $fake_protection_cfg:expr $(, $bss_key:ident: $bss_value:expr)* $(,)?) => {{
510        $crate::fake_bss_description__!(
511            random_fidl_bss_description,
512            $fake_protection_cfg $(, $bss_key: $bss_value)*)
513    }};
514}
515
516#[cfg(test)]
517mod tests {
518    use super::*;
519    use crate::bss::{BssDescription, Protection};
520    use crate::test_utils::fake_frames::{fake_wmm_param_body, fake_wmm_param_header};
521
522    #[test]
523    fn check_simplest_macro_use() {
524        let fidl_bss_description = fake_fidl_bss_description!(Open);
525        let bss_description = fake_bss_description!(Open);
526        assert_eq!(
527            BssDescription::try_from(fidl_bss_description)
528                .expect("Failed to convert fake_fidl_bss_description value"),
529            bss_description
530        );
531
532        for i in 1..=11 {
533            if i > 10 {
534                panic!("random_bss_description is always equal to bss_description");
535            }
536
537            let random_fidl_bss_description = random_fidl_bss_description!(Open);
538            let random_bss_description =
539                BssDescription::try_from(random_fidl_bss_description.clone())
540                    .expect("Failed to convert random_fidl_bss_description value");
541            if random_bss_description != bss_description {
542                break;
543            }
544        }
545
546        for i in 1..=11 {
547            if i > 10 {
548                panic!("random_bss_description is always equal to other_random_bss_description");
549            }
550
551            let random_fidl_bss_description = random_fidl_bss_description!(Open);
552            let random_bss_description =
553                BssDescription::try_from(random_fidl_bss_description.clone())
554                    .expect("Failed to convert random_fidl_bss_description value");
555            let other_random_bss_description = random_bss_description!(Open);
556            if random_bss_description != other_random_bss_description {
557                break;
558            }
559        }
560    }
561
562    #[test]
563    fn fake_protection_cfg_from_primitive() {
564        assert!(
565            0 <= LAST_FAKE_PROTECTION_CFG_VALUE,
566            "LAST_FAKE_PROTECTION_CFG_VALUE is not positive: {}",
567            LAST_FAKE_PROTECTION_CFG_VALUE,
568        );
569
570        let too_low: Option<FakeProtectionCfg> = FromPrimitive::from_isize(-1);
571        assert_eq!(
572            too_low, None::<FakeProtectionCfg>,
573            "Successfully converted low out of range FakeProtectionCfg value"
574        );
575
576        for i in 0..(LAST_FAKE_PROTECTION_CFG_VALUE + 1) {
577            let _: FakeProtectionCfg = FromPrimitive::from_isize(i).unwrap_or_else(|| {
578                panic!("Failed to convert {:?} to a FakeProtectionCfg value", i)
579            });
580        }
581
582        let too_high: Option<FakeProtectionCfg> =
583            FromPrimitive::from_isize(LAST_FAKE_PROTECTION_CFG_VALUE + 1);
584        assert_eq!(
585            too_high, None::<FakeProtectionCfg>,
586            "Successfully converted high out of range FakeProtectionCfg value"
587        );
588    }
589
590    #[test]
591    fn fake_protection_cfg_expr_syntax() {
592        let fidl_bss_description =
593            fake_fidl_bss_description!(protection => FakeProtectionCfg::Open);
594        assert_eq!(
595            BssDescription::try_from(fidl_bss_description)
596                .expect("Failed to convert fake_fidl_bss_description value")
597                .protection(),
598            Protection::Open
599        );
600
601        let fidl_bss_description =
602            random_fidl_bss_description!(protection => FakeProtectionCfg::Open);
603        assert_eq!(
604            BssDescription::try_from(fidl_bss_description)
605                .expect("Failed to convert random_fidl_bss_description value")
606                .protection(),
607            Protection::Open
608        );
609
610        let bss_description = fake_bss_description!(protection => FakeProtectionCfg::Open);
611        assert_eq!(bss_description.protection(), Protection::Open);
612
613        let bss_description = random_bss_description!(protection => FakeProtectionCfg::Open);
614        assert_eq!(bss_description.protection(), Protection::Open);
615    }
616
617    #[test]
618    fn fake_protection_cfg_privacy_bit_and_protection() {
619        let bss = fake_bss_description!(Open);
620        assert!(!mac::CapabilityInfo(bss.capability_info).privacy());
621        assert_eq!(bss.protection(), Protection::Open);
622
623        let bss = fake_bss_description!(Wep);
624        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
625        assert_eq!(bss.protection(), Protection::Wep);
626
627        let bss = fake_bss_description!(Wpa1);
628        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
629        assert_eq!(bss.protection(), Protection::Wpa1);
630
631        let bss = fake_bss_description!(Wpa2);
632        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
633        assert_eq!(bss.protection(), Protection::Wpa2Personal);
634    }
635
636    #[test]
637    fn fake_protection_cfg_privacy_bit_and_protection_in_random_bss() {
638        let bss = random_bss_description!(Open);
639        assert!(!mac::CapabilityInfo(bss.capability_info).privacy());
640        assert_eq!(bss.protection(), Protection::Open);
641
642        let bss = random_bss_description!(Wep);
643        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
644        assert_eq!(bss.protection(), Protection::Wep);
645
646        let bss = random_bss_description!(Wpa1);
647        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
648        assert_eq!(bss.protection(), Protection::Wpa1);
649
650        let bss = random_bss_description!(Wpa2);
651        assert!(mac::CapabilityInfo(bss.capability_info).privacy());
652        assert_eq!(bss.protection(), Protection::Wpa2Personal);
653    }
654
655    #[test]
656    fn set_capability_info_bits() {
657        macro_rules! check_bit {
658            ($bit_name:ident) => {{
659                let bss = fake_bss_description!(Open, $bit_name: true);
660                assert!(mac::CapabilityInfo(bss.capability_info).$bit_name());
661                let bss = fake_bss_description!(Open, $bit_name: false);
662                assert!(!mac::CapabilityInfo(bss.capability_info).$bit_name());
663            }}
664        }
665        check_bit!(cf_pollable);
666        check_bit!(cf_poll_req);
667        check_bit!(short_preamble);
668        check_bit!(spectrum_mgmt);
669        check_bit!(qos);
670        check_bit!(short_slot_time);
671        check_bit!(apsd);
672        check_bit!(radio_measurement);
673        check_bit!(delayed_block_ack);
674        check_bit!(immediate_block_ack);
675
676        let bss =
677            fake_bss_description!(Open, cf_pollable: true, apsd: false, immediate_block_ack: true);
678        assert!(mac::CapabilityInfo(bss.capability_info).cf_pollable());
679        assert!(!mac::CapabilityInfo(bss.capability_info).apsd());
680        assert!(mac::CapabilityInfo(bss.capability_info).immediate_block_ack());
681    }
682
683    #[test]
684    fn simple_default_override() {
685        let bss = fake_fidl_bss_description!(Open);
686        assert_eq!(bss.beacon_period, 100);
687
688        let bss = fake_fidl_bss_description!(Open, beacon_period: 50);
689        assert_eq!(bss.beacon_period, 50);
690    }
691
692    #[test]
693    #[should_panic(expected = "Personal is not supported")]
694    // TODO(https://fxbug.dev/42169733): LeakSanitizer flags leaks caused by panic.
695    #[cfg_attr(feature = "variant_asan", ignore)]
696    fn unsupported_bss_type() {
697        fake_fidl_bss_description!(Open, bss_type: fidl_common::BssType::Personal);
698    }
699
700    #[test]
701    fn any_protection_syntax() {
702        let _ = random_fidl_bss_description!();
703        let _ = random_bss_description!();
704    }
705
706    #[test]
707    fn random_fidl_bss_decription_override() {
708        let random_bss = random_bss_description!(ssid: Ssid::try_from("foo").unwrap());
709        assert_eq!(random_bss.ssid, Ssid::try_from("foo").unwrap());
710    }
711
712    #[test]
713    fn valid_random_ecw_min_max() {
714        let mut rng = rand::thread_rng();
715        for _ in 0..100 {
716            let ecw_min_max = random_ecw_min_max(&mut rng);
717            assert!(ecw_min_max.ecw_max() >= ecw_min_max.ecw_min());
718        }
719    }
720
721    #[test]
722    fn random_bss_is_not_constant() {
723        for _ in 0..10 {
724            let random_bss_1 = BssDescription::try_from(random_fidl_bss_description!())
725                .expect("Failed to convert random_bss_description value");
726            let random_bss_2 = BssDescription::try_from(random_fidl_bss_description!())
727                .expect("Failed to convert random_bss_description value");
728            if random_bss_1 != random_bss_2 {
729                return;
730            }
731        }
732        panic!("random bss is always the same");
733    }
734
735    #[test]
736    fn random_bss_protection_is_not_constant() {
737        for _ in 0..10 {
738            let random_bss_1 = BssDescription::try_from(random_fidl_bss_description!())
739                .expect("Failed to convert random_bss_description value");
740            let random_bss_2 = BssDescription::try_from(random_fidl_bss_description!())
741                .expect("Failed to convert random_bss_description value");
742            if random_bss_1.protection() != random_bss_2.protection() {
743                return;
744            }
745        }
746        panic!("random bss protection is always the same");
747    }
748
749    #[test]
750    fn some_random_bss_bits_are_fixed() {
751        for _ in 0..5 {
752            let random_bss = random_fidl_bss_description!(Open);
753            assert_eq!(random_bss.bss_type, fidl_common::BssType::Infrastructure);
754            assert!(mac::CapabilityInfo(random_bss.capability_info).ess());
755            assert!(!mac::CapabilityInfo(random_bss.capability_info).ibss());
756            assert!(!mac::CapabilityInfo(random_bss.capability_info).privacy());
757        }
758    }
759
760    // Test random_bss_description generation of random protection since
761    // it doesn't rely on random_fidl_bss_description for it.
762    #[test]
763    fn random_bss_decription_protection_randomness() {
764        for _ in 0..10 {
765            let random_bss_1 = random_bss_description!();
766            let random_bss_2 = random_bss_description!();
767            if random_bss_1.protection() != random_bss_2.protection() {
768                return;
769            }
770        }
771        panic!("random protection is always the same");
772    }
773
774    #[test]
775    fn ies_overrides() {
776        let bss = fake_bss_description!(Wpa1Wpa2,
777            ssid: Ssid::try_from("fuchsia").unwrap(),
778            rates: vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
779            ies_overrides: IesOverrides::new()
780                .remove(IeType::new_vendor([0x00, 0x0b, 0x86, 0x01, 0x04, 0x08]))
781                .set(IeType::DSSS_PARAM_SET, [136].to_vec()),
782        );
783
784        // Things to note:
785        // - SSID "fuchsia" is inserted.
786        // - Rates and extended supported rates are inserted.
787        // - WPA2 RSNE and WPA1 vendor IE are inserted.
788        // - DSSS Param set's value is changed.
789        // - Aruba vendor IE no longer there.
790        #[rustfmt::skip]
791        let mut expected_ies = vec![
792            // SSID
793            0x00, 0x07, b'f', b'u', b'c', b'h', b's', b'i', b'a',
794            // Rates
795            0x01, 0x08, 11, 12, 13, 14, 15, 16, 17, 18,
796            // DS parameter set: channel 136
797            0x03, 0x01, 136,
798            // TIM - DTIM count: 0, DTIM period: 1, PVB: 2
799            0x05, 0x04, 0x00, 0x01, 0x00, 0x02,
800            // Country info
801            0x07, 0x10, 0x55, 0x53, 0x20, // US, Any environment
802            0x24, 0x04, 0x24, // 1st channel: 36, # channels: 4, maximum tx power: 36 dBm
803            0x34, 0x04, 0x1e, // 1st channel: 52, # channels: 4, maximum tx power: 30 dBm
804            0x64, 0x0c, 0x1e, // 1st channel: 100, # channels: 12, maximum tx power: 30 dBm
805            0x95, 0x05, 0x24, // 1st channel: 149, # channels: 5, maximum tx power: 36 dBm
806            0x00, // padding
807            // Power constraint: 0
808            0x20, 0x01, 0x00,
809            // TPC Report Transmit Power: 9, Link Margin: 0
810            0x23, 0x02, 0x09, 0x00,
811            // HT Capabilities
812            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
813            0x17, // A-MPDU parameters
814            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
815            0x00, 0x00, // MCS set
816            0x00, 0x00, // HT extended capabilities
817            0x00, 0x00, 0x00, 0x00, // Transmit beamforming
818            0x00, // Antenna selection capabilities
819            // RSNE
820            0x30, 18, // Element header
821            1, 0, // Version
822            0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
823            1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
824            1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
825            // Extended supported rates
826            0x32, 0x06, 19, 20, 21, 22, 23, 24,
827            // HT Operation
828            0x3d, 0x16, 0x8c, // Primary channel: 140
829            0x0d, // HT info subset - secondary channel above, any channel width, RIFS permitted
830            0x16, 0x00, 0x00, 0x00, // HT info subsets
831            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
832            0x00, 0x00, // Basic MCS set
833            // Extended Capabilities: extended channel switching, BSS transition, operating mode notification
834            0x7f, 0x08, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x40,
835            // VHT Capabilities
836            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
837            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
838            // VHT Operation
839            0xc0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, // VHT Tx Power Envelope
840            0xc3, 0x03, 0x01, 0x24, 0x24,
841            // WPA1 vendor IE
842            0xdd, 0x16, 0x00, 0x50, 0xf2, // IE header
843            0x01, // MSFT specific IE type (WPA)
844            0x01, 0x00, // WPA version
845            0x00, 0x50, 0xf2, 0x02, // multicast cipher: TKIP
846            0x01, 0x00, 0x00, 0x50, 0xf2, 0x02, // 1 unicast cipher
847            0x01, 0x00, 0x00, 0x50, 0xf2, 0x02, // 1 AKM: PSK
848        ];
849        expected_ies.extend(fake_wmm_param_header());
850        expected_ies.extend(fake_wmm_param_body());
851
852        assert_eq!(bss.ies(), &expected_ies[..]);
853    }
854}