Skip to main content

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