Skip to main content

wlan_mlme/client/
mod.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
5mod bound;
6mod channel_switch;
7mod convert_beacon;
8mod lost_bss;
9mod scanner;
10mod state;
11mod station;
12
13use bound::BoundClient;
14use station::{Client, ParsedConnectRequest};
15#[cfg(test)]
16mod test_utils;
17
18use crate::ddk_converter;
19use crate::device::{self, DeviceOps};
20use crate::error::Error;
21use channel_switch::ChannelState;
22use fidl_fuchsia_wlan_common as fidl_common;
23use fidl_fuchsia_wlan_driver as fidl_driver_common;
24use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
25use fidl_fuchsia_wlan_minstrel as fidl_minstrel;
26use fidl_fuchsia_wlan_mlme as fidl_mlme;
27use fidl_fuchsia_wlan_softmac as fidl_softmac;
28use fidl_fuchsia_wlan_stats as fidl_stats;
29use fuchsia_trace as trace;
30use ieee80211::{Bssid, MacAddr, MacAddrBytes};
31use log::{error, warn};
32use scanner::Scanner;
33use wlan_common::bss::BssDescription;
34use wlan_common::capabilities::{ClientCapabilities, derive_join_capabilities};
35use wlan_common::channel::Channel;
36use wlan_common::ie::{self, Id};
37use wlan_common::mac::{self, CapabilityInfo};
38use wlan_common::sequence::SequenceManager;
39use wlan_common::timer::Timer;
40use wlan_trace as wtrace;
41use zerocopy::SplitByteSlice;
42
43pub use scanner::ScanError;
44
45#[derive(Debug, Clone, PartialEq)]
46pub enum TimedEvent {
47    /// Connecting to AP timed out.
48    Connecting,
49    /// Timeout for reassociating after a disassociation.
50    Reassociating,
51    /// Association status update includes checking for auto deauthentication due to beacon loss
52    /// and report signal strength
53    AssociationStatusCheck,
54    /// The delay for a scheduled channel switch has elapsed.
55    ChannelSwitch,
56}
57
58#[cfg(test)]
59impl TimedEvent {
60    fn class(&self) -> TimedEventClass {
61        match self {
62            Self::Connecting => TimedEventClass::Connecting,
63            Self::Reassociating => TimedEventClass::Reassociating,
64            Self::AssociationStatusCheck => TimedEventClass::AssociationStatusCheck,
65            Self::ChannelSwitch => TimedEventClass::ChannelSwitch,
66        }
67    }
68}
69
70#[cfg(test)]
71#[derive(Debug, PartialEq, Eq, Hash)]
72pub enum TimedEventClass {
73    Connecting,
74    Reassociating,
75    AssociationStatusCheck,
76    ChannelSwitch,
77}
78
79/// ClientConfig affects time duration used for different timeouts.
80/// Originally added to more easily control behavior in tests.
81#[repr(C)]
82#[derive(Debug, Clone, Default)]
83pub struct ClientConfig {
84    pub ensure_on_channel_time: zx::sys::zx_duration_t,
85}
86
87pub struct Context<D> {
88    _config: ClientConfig,
89    device: D,
90    timer: Timer<TimedEvent>,
91    seq_mgr: SequenceManager,
92}
93
94pub struct ClientMlme<D> {
95    sta: Option<Client>,
96    ctx: Context<D>,
97    scanner: Scanner,
98    channel_state: ChannelState,
99}
100impl<D: DeviceOps> crate::MlmeImpl for ClientMlme<D> {
101    type Config = ClientConfig;
102    type Device = D;
103    type TimerEvent = TimedEvent;
104    async fn new(
105        config: Self::Config,
106        mut device: Self::Device,
107        timer: Timer<TimedEvent>,
108    ) -> Result<Self, anyhow::Error> {
109        let iface_mac = device::try_query_iface_mac(&mut device).await?;
110        Ok(Self {
111            sta: None,
112            ctx: Context { _config: config, device, timer, seq_mgr: SequenceManager::new() },
113            scanner: Scanner::new(iface_mac.into()),
114            channel_state: Default::default(),
115        })
116    }
117    async fn handle_mlme_request(
118        &mut self,
119        req: wlan_sme::MlmeRequest,
120    ) -> Result<(), anyhow::Error> {
121        match req {
122            wlan_sme::MlmeRequest::Scan(req) => {
123                self.on_sme_scan(req).await;
124                Ok(())
125            }
126            wlan_sme::MlmeRequest::Connect(req) => {
127                self.on_sme_connect(req).await?;
128                Ok(())
129            }
130            wlan_sme::MlmeRequest::GetIfaceStats(responder) => {
131                self.on_sme_get_iface_stats(responder)?;
132                Ok(())
133            }
134            wlan_sme::MlmeRequest::GetIfaceHistogramStats(responder) => {
135                self.on_sme_get_iface_histogram_stats(responder)?;
136                Ok(())
137            }
138            wlan_sme::MlmeRequest::QueryDeviceInfo(responder) => {
139                self.on_sme_query_device_info(responder).await?;
140                Ok(())
141            }
142            wlan_sme::MlmeRequest::QueryMacSublayerSupport(responder) => {
143                self.on_sme_query_mac_sublayer_support(responder).await?;
144                Ok(())
145            }
146            wlan_sme::MlmeRequest::QuerySecuritySupport(responder) => {
147                self.on_sme_query_security_support(responder).await?;
148                Ok(())
149            }
150            wlan_sme::MlmeRequest::QuerySpectrumManagementSupport(responder) => {
151                self.on_sme_query_spectrum_management_support(responder).await?;
152                Ok(())
153            }
154            wlan_sme::MlmeRequest::ListMinstrelPeers(responder) => {
155                self.on_sme_list_minstrel_peers(responder)?;
156                Ok(())
157            }
158            wlan_sme::MlmeRequest::GetMinstrelStats(req, responder) => {
159                self.on_sme_get_minstrel_stats(responder, &req.peer_addr.into())?;
160                Ok(())
161            }
162            wlan_sme::MlmeRequest::GetSignalReport(responder) => {
163                self.on_sme_get_signal_report(responder);
164                Ok(())
165            }
166            req if self.sta.is_some() => {
167                let sta = self.sta.as_mut().unwrap();
168                sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
169                    .handle_mlme_request(req)
170                    .await;
171                Ok(())
172            }
173            unhandled_request => {
174                if let wlan_sme::MlmeRequest::Reconnect(req) = &unhandled_request {
175                    self.ctx.device.send_mlme_event(fidl_mlme::MlmeEvent::ConnectConf {
176                        resp: fidl_mlme::ConnectConfirm {
177                            peer_sta_address: req.peer_sta_address,
178                            result_code: fidl_ieee80211::StatusCode::DeniedNoAssociationExists,
179                            association_id: 0,
180                            association_ies: vec![],
181                        },
182                    })?;
183                }
184
185                Err(Error::Status(
186                    format!(
187                        "Failed to handle {} MLME request: request is unhandled in the current state. \
188                         Connection context exists: {}, Main channel: {:?}, Scanning: {}.",
189                        unhandled_request.name(),
190                        self.sta.is_some(),
191                        self.channel_state.get_primary(),
192                        self.scanner.is_scanning(),
193                    ),
194                    zx::Status::BAD_STATE,
195                ).into())
196            }
197        }
198    }
199    async fn handle_mac_frame_rx(
200        &mut self,
201        bytes: &[u8],
202        rx_info: fidl_softmac::WlanRxInfo,
203        async_id: trace::Id,
204    ) {
205        wtrace::duration!("ClientMlme::handle_mac_frame_rx");
206        // TODO(https://fxbug.dev/42120906): Send the entire frame to scanner.
207        if let Some(mgmt_frame) = mac::MgmtFrame::parse(bytes, false) {
208            let bssid = Bssid::from(mgmt_frame.mgmt_hdr.addr3);
209            match mgmt_frame.try_into_mgmt_body().1 {
210                Some(mac::MgmtBody::Beacon { bcn_hdr, elements }) => {
211                    wtrace::duration!("MgmtBody::Beacon");
212                    self.scanner.bind(&mut self.ctx).handle_ap_advertisement(
213                        bssid,
214                        bcn_hdr.beacon_interval,
215                        bcn_hdr.capabilities,
216                        elements,
217                        rx_info.clone(),
218                    );
219                }
220                Some(mac::MgmtBody::ProbeResp { probe_resp_hdr, elements }) => {
221                    wtrace::duration!("MgmtBody::ProbeResp");
222                    self.scanner.bind(&mut self.ctx).handle_ap_advertisement(
223                        bssid,
224                        probe_resp_hdr.beacon_interval,
225                        probe_resp_hdr.capabilities,
226                        elements,
227                        rx_info.clone(),
228                    )
229                }
230                _ => (),
231            }
232        }
233
234        if let Some(sta) = self.sta.as_mut() {
235            // Only pass the frame to a BoundClient under the following conditions:
236            //   - ChannelState currently has a main channel.
237            //   - ClientMlme received the frame on the main channel.
238            match self.channel_state.get_primary() {
239                Some(main_channel) if main_channel == rx_info.primary => {
240                    sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
241                        .handle_mac_frame_rx(bytes, rx_info, async_id)
242                        .await;
243                }
244                Some(_) => {
245                    wtrace::async_end_wlansoftmac_rx(async_id, "off main channel");
246                }
247                // TODO(https://fxbug.dev/42075118): This is only reachable because the Client state machine
248                // returns to the Joined state and clears the main channel upon deauthentication.
249                None => {
250                    error!(
251                        "Received MAC frame on channel {:?} while main channel is not set.",
252                        rx_info.primary
253                    );
254                    wtrace::async_end_wlansoftmac_rx(async_id, "main channel not set");
255                }
256            }
257        } else {
258            wtrace::async_end_wlansoftmac_rx(async_id, "no bound client");
259        }
260    }
261    fn handle_eth_frame_tx(
262        &mut self,
263        bytes: &[u8],
264        async_id: trace::Id,
265    ) -> Result<(), anyhow::Error> {
266        wtrace::duration!("ClientMlme::handle_eth_frame_tx");
267        match self.sta.as_mut() {
268            None => Err(Error::Status(
269                "Ethernet frame dropped (Client does not exist).".to_string(),
270                zx::Status::BAD_STATE,
271            )
272            .into()),
273            Some(sta) => sta
274                .bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
275                .handle_eth_frame_tx(bytes, async_id)
276                .map_err(From::from),
277        }
278    }
279    async fn handle_scan_complete(&mut self, status: Result<(), zx::Status>, scan_id: u64) {
280        self.scanner.bind(&mut self.ctx).handle_scan_complete(status, scan_id).await;
281    }
282    async fn handle_timeout(&mut self, event: TimedEvent) {
283        if let Some(sta) = self.sta.as_mut() {
284            let mut bound = sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state);
285            bound.sta.state =
286                Some(bound.sta.state.take().unwrap().on_timed_event(&mut bound, event).await);
287        }
288    }
289}
290
291impl<D> ClientMlme<D> {
292    pub fn seq_mgr(&mut self) -> &mut SequenceManager {
293        &mut self.ctx.seq_mgr
294    }
295
296    fn on_sme_get_iface_stats(
297        &self,
298        responder: wlan_sme::responder::Responder<fidl_mlme::GetIfaceStatsResponse>,
299    ) -> Result<(), Error> {
300        // TODO(https://fxbug.dev/42119762): Implement stats
301        let resp = fidl_mlme::GetIfaceStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED);
302        responder.respond(resp);
303        Ok(())
304    }
305
306    fn on_sme_get_iface_histogram_stats(
307        &self,
308        responder: wlan_sme::responder::Responder<fidl_mlme::GetIfaceHistogramStatsResponse>,
309    ) -> Result<(), Error> {
310        // TODO(https://fxbug.dev/42119762): Implement stats
311        let resp =
312            fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED);
313        responder.respond(resp);
314        Ok(())
315    }
316
317    fn on_sme_list_minstrel_peers(
318        &self,
319        responder: wlan_sme::responder::Responder<fidl_mlme::MinstrelListResponse>,
320    ) -> Result<(), Error> {
321        // TODO(https://fxbug.dev/42159791): Implement once Minstrel is in Rust.
322        error!("ListMinstrelPeers is not supported.");
323        let peers = fidl_minstrel::Peers { addrs: vec![] };
324        let resp = fidl_mlme::MinstrelListResponse { peers };
325        responder.respond(resp);
326        Ok(())
327    }
328
329    fn on_sme_get_minstrel_stats(
330        &self,
331        responder: wlan_sme::responder::Responder<fidl_mlme::MinstrelStatsResponse>,
332        _addr: &MacAddr,
333    ) -> Result<(), Error> {
334        // TODO(https://fxbug.dev/42159791): Implement once Minstrel is in Rust.
335        error!("GetMinstrelStats is not supported.");
336        let resp = fidl_mlme::MinstrelStatsResponse { peer: None };
337        responder.respond(resp);
338        Ok(())
339    }
340}
341
342impl<D: DeviceOps> ClientMlme<D> {
343    pub async fn set_main_channel(
344        &mut self,
345        primary: fidl_ieee80211::ChannelNumber,
346        bandwidth: fidl_ieee80211::ChannelBandwidth,
347        vht_secondary_80_channel: fidl_ieee80211::ChannelNumber,
348    ) -> Result<(), zx::Status> {
349        self.channel_state
350            .bind(&mut self.ctx, &mut self.scanner)
351            .set_main_channel(primary, bandwidth, vht_secondary_80_channel)
352            .await
353    }
354
355    async fn on_sme_scan(&mut self, req: fidl_mlme::ScanRequest) {
356        let txn_id = req.txn_id;
357        let _ = self.scanner.bind(&mut self.ctx).on_sme_scan(req).await.map_err(|e| {
358            error!("Scan failed in MLME: {:?}", e);
359            let code = match e {
360                Error::ScanError(scan_error) => scan_error.into(),
361                _ => fidl_mlme::ScanResultCode::InternalError,
362            };
363            self.ctx
364                .device
365                .send_mlme_event(fidl_mlme::MlmeEvent::OnScanEnd {
366                    end: fidl_mlme::ScanEnd { txn_id, code },
367                })
368                .unwrap_or_else(|e| error!("error sending MLME ScanEnd: {}", e));
369        });
370    }
371
372    async fn on_sme_connect(&mut self, req: fidl_mlme::ConnectRequest) -> Result<(), Error> {
373        // Cancel any ongoing scan so that it doesn't conflict with the connect request
374        // TODO(b/254290448): Use enable/disable scanning for better guarantees.
375        if let Err(e) = self.scanner.bind(&mut self.ctx).cancel_ongoing_scan().await {
376            warn!("Failed to cancel ongoing scan before connect: {}.", e);
377        }
378
379        let bssid = req.selected_bss.bssid;
380        let result = match req.selected_bss.try_into() {
381            Ok(bss) => {
382                let req = ParsedConnectRequest {
383                    selected_bss: bss,
384                    connect_failure_timeout: req.connect_failure_timeout,
385                    auth_type: req.auth_type,
386                    security_ie: req.security_ie,
387                };
388                self.join_device(&req.selected_bss).await.map(|cap| (req, cap))
389            }
390            Err(e) => Err(Error::Status(
391                format!("Error parsing BssDescription: {:?}", e),
392                zx::Status::IO_INVALID,
393            )),
394        };
395
396        match result {
397            Ok((req, client_capabilities)) => {
398                self.sta.replace(Client::new(
399                    req,
400                    device::try_query_iface_mac(&mut self.ctx.device).await?,
401                    client_capabilities,
402                ));
403                if let Some(sta) = &mut self.sta {
404                    sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
405                        .start_connecting()
406                        .await;
407                }
408                Ok(())
409            }
410            Err(e) => {
411                error!("Error setting up device for join: {}", e);
412                // TODO(https://fxbug.dev/42120718): Only one failure code defined in IEEE 802.11-2016 6.3.4.3
413                // Can we do better?
414                self.ctx.device.send_mlme_event(fidl_mlme::MlmeEvent::ConnectConf {
415                    resp: fidl_mlme::ConnectConfirm {
416                        peer_sta_address: bssid,
417                        result_code: fidl_ieee80211::StatusCode::JoinFailure,
418                        association_id: 0,
419                        association_ies: vec![],
420                    },
421                })?;
422                Err(e)
423            }
424        }
425    }
426
427    async fn join_device(&mut self, bss: &BssDescription) -> Result<ClientCapabilities, Error> {
428        let info = ddk_converter::mlme_device_info_from_softmac(
429            device::try_query(&mut self.ctx.device).await?,
430        )?;
431        let join_caps = derive_join_capabilities(Channel::from(bss.channel), bss.rates(), &info)
432            .map_err(|e| {
433                Error::Status(
434                    format!("Failed to derive join capabilities: {:?}", e),
435                    zx::Status::NOT_SUPPORTED,
436                )
437            })?;
438
439        let (bandwidth, secondary80_num) = bss.channel.bandwidth.to_fidl();
440        let vht_secondary_80_channel =
441            fidl_ieee80211::ChannelNumber { band: bss.channel.band, number: secondary80_num };
442        self.set_main_channel(bss.channel.into(), bandwidth, vht_secondary_80_channel)
443            .await
444            .map_err(|status| Error::Status(format!("Error setting device channel"), status))?;
445
446        let join_bss_request = fidl_driver_common::JoinBssRequest {
447            bssid: Some(bss.bssid.to_array()),
448            bss_type: Some(fidl_ieee80211::BssType::Infrastructure),
449            remote: Some(true),
450            beacon_period: Some(bss.beacon_period),
451            ..Default::default()
452        };
453
454        // Configure driver to pass frames from this BSS to MLME. Otherwise they will be dropped.
455        self.ctx
456            .device
457            .join_bss(&join_bss_request)
458            .await
459            .map(|()| join_caps)
460            .map_err(|status| Error::Status(format!("Error setting BSS in driver"), status))
461    }
462
463    async fn on_sme_query_device_info(
464        &mut self,
465        responder: wlan_sme::responder::Responder<fidl_mlme::DeviceInfo>,
466    ) -> Result<(), Error> {
467        let info = ddk_converter::mlme_device_info_from_softmac(
468            device::try_query(&mut self.ctx.device).await?,
469        )?;
470        responder.respond(info);
471        Ok(())
472    }
473
474    async fn on_sme_query_mac_sublayer_support(
475        &mut self,
476        responder: wlan_sme::responder::Responder<fidl_common::MacSublayerSupport>,
477    ) -> Result<(), Error> {
478        let support = device::try_query_mac_sublayer_support(&mut self.ctx.device).await?;
479        responder.respond(support);
480        Ok(())
481    }
482
483    async fn on_sme_query_security_support(
484        &mut self,
485        responder: wlan_sme::responder::Responder<fidl_common::SecuritySupport>,
486    ) -> Result<(), Error> {
487        let support = device::try_query_security_support(&mut self.ctx.device).await?;
488        responder.respond(support);
489        Ok(())
490    }
491
492    async fn on_sme_query_spectrum_management_support(
493        &mut self,
494        responder: wlan_sme::responder::Responder<fidl_common::SpectrumManagementSupport>,
495    ) -> Result<(), Error> {
496        let support = device::try_query_spectrum_management_support(&mut self.ctx.device).await?;
497        responder.respond(support);
498        Ok(())
499    }
500
501    fn on_sme_get_signal_report(
502        &mut self,
503        responder: wlan_sme::responder::Responder<fidl_mlme::MlmeGetSignalReportResult>,
504    ) {
505        let report = match self.sta.as_mut() {
506            Some(sta) => sta
507                .bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
508                .get_signal_report(),
509            None => fidl_stats::SignalReport::default(),
510        };
511        responder.respond(Ok(report));
512    }
513}
514
515pub struct ParsedAssociateResp {
516    pub association_id: u16,
517    pub capabilities: CapabilityInfo,
518    pub rates: Vec<ie::SupportedRate>,
519    pub ht_cap: Option<ie::HtCapabilities>,
520    pub vht_cap: Option<ie::VhtCapabilities>,
521}
522
523impl ParsedAssociateResp {
524    pub fn parse<B: SplitByteSlice>(assoc_resp_frame: &mac::AssocRespFrame<B>) -> Self {
525        let mut parsed = ParsedAssociateResp {
526            association_id: assoc_resp_frame.assoc_resp_hdr.aid,
527            capabilities: assoc_resp_frame.assoc_resp_hdr.capabilities,
528            rates: vec![],
529            ht_cap: None,
530            vht_cap: None,
531        };
532        for (id, body) in assoc_resp_frame.ies() {
533            match id {
534                Id::SUPPORTED_RATES => match ie::parse_supported_rates(body) {
535                    Err(e) => warn!("invalid Supported Rates: {}", e),
536                    Ok(supported_rates) => {
537                        // safe to unwrap because supported rate is 1-byte long thus always aligned
538                        parsed.rates.extend(supported_rates.iter());
539                    }
540                },
541                Id::EXTENDED_SUPPORTED_RATES => match ie::parse_extended_supported_rates(body) {
542                    Err(e) => warn!("invalid Extended Supported Rates: {}", e),
543                    Ok(supported_rates) => {
544                        // safe to unwrap because supported rate is 1-byte long thus always aligned
545                        parsed.rates.extend(supported_rates.iter());
546                    }
547                },
548                Id::HT_CAPABILITIES => match ie::parse_ht_capabilities(body) {
549                    Err(e) => warn!("invalid HT Capabilities: {}", e),
550                    Ok(ht_cap) => {
551                        parsed.ht_cap = Some(*ht_cap);
552                    }
553                },
554                Id::VHT_CAPABILITIES => match ie::parse_vht_capabilities(body) {
555                    Err(e) => warn!("invalid VHT Capabilities: {}", e),
556                    Ok(vht_cap) => {
557                        parsed.vht_cap = Some(*vht_cap);
558                    }
559                },
560                // TODO(https://fxbug.dev/42120297): parse vendor ID and include WMM param if exists
561                _ => {}
562            }
563        }
564        parsed
565    }
566}
567
568#[cfg(test)]
569mod tests {
570    use super::state::DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT;
571    use super::*;
572    use crate::MlmeImpl;
573    use crate::client::test_utils::*;
574    use crate::device::{FakeDevice, LinkStatus, test_utils};
575    use crate::test_utils::MockWlanRxInfo;
576    use assert_matches::assert_matches;
577    use fidl_fuchsia_wlan_common as fidl_common;
578    use fidl_fuchsia_wlan_internal as fidl_internal;
579    use fidl_fuchsia_wlan_mlme as fidl_mlme;
580    use ieee80211::Ssid;
581    use wlan_common::channel::Bandwidth;
582    use wlan_common::fake_fidl_bss_description;
583    use wlan_sme::responder::Responder;
584
585    #[fuchsia::test(allow_stalls = false)]
586    async fn spawns_new_sta_on_connect_request_from_sme() {
587        let mut m = MockObjects::new().await;
588        let mut me = m.make_mlme().await;
589        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
590        me.on_sme_connect(fidl_mlme::ConnectRequest {
591            selected_bss: fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
592            connect_failure_timeout: 100,
593            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
594            sae_password: vec![],
595            wep_key: None,
596            security_ie: vec![],
597            owe_public_key: None,
598        })
599        .await
600        .expect("valid ConnectRequest should be handled successfully");
601        me.get_bound_client().expect("client sta should have been created by now.");
602    }
603
604    #[fuchsia::test(allow_stalls = false)]
605    async fn fails_to_connect_if_channel_unknown() {
606        let mut m = MockObjects::new().await;
607        let mut me = m.make_mlme().await;
608        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
609        let mut req = fidl_mlme::ConnectRequest {
610            selected_bss: fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
611            connect_failure_timeout: 100,
612            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
613            sae_password: vec![],
614            wep_key: None,
615            security_ie: vec![],
616            owe_public_key: None,
617        };
618
619        req.selected_bss.bandwidth = fidl_fuchsia_wlan_ieee80211::ChannelBandwidth::unknown();
620        me.on_sme_connect(req)
621            .await
622            .expect_err("ConnectRequest with unknown channel should be rejected");
623        assert!(me.get_bound_client().is_none());
624    }
625
626    /// Consumes `TimedEvent` values from the `timer::EventStream` held by `mock_objects` and
627    /// handles each `TimedEvent` value with `mlme`. This function makes the following assertions:
628    ///
629    ///   - The `timer::EventStream` held by `mock_objects` starts with one `StatusCheckTimeout`
630    ///     pending.
631    ///   - For the `beacon_count` specified, `mlme` will consume the current `StatusCheckTimeout`
632    ///     and schedule the next.
633    ///   - `mlme` produces a `fidl_mlme::SignalReportIndication` for each StatusCheckTimeout
634    ///     consumed.
635    async fn handle_association_status_checks_and_signal_reports(
636        mock_objects: &mut MockObjects,
637        mlme: &mut ClientMlme<FakeDevice>,
638        beacon_count: u32,
639    ) {
640        for _ in 0..beacon_count / super::state::ASSOCIATION_STATUS_TIMEOUT_BEACON_COUNT {
641            let (_, timed_event, _) =
642                mock_objects.time_stream.try_recv().expect("Should have scheduled a timed event");
643            mlme.handle_timeout(timed_event.event).await;
644            assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 0);
645            mock_objects
646                .fake_device_state
647                .lock()
648                .next_mlme_msg::<fidl_internal::SignalReportIndication>()
649                .expect("error reading SignalReport.indication");
650        }
651    }
652
653    #[fuchsia::test(allow_stalls = false)]
654    async fn test_auto_deauth_uninterrupted_interval() {
655        let mut mock_objects = MockObjects::new().await;
656        let mut mlme = mock_objects.make_mlme().await;
657        mlme.make_client_station();
658        let mut client = mlme.get_bound_client().expect("client should be present");
659
660        client.move_to_associated_state();
661
662        // Verify timer is scheduled and move the time to immediately before auto deauth is triggered.
663        handle_association_status_checks_and_signal_reports(
664            &mut mock_objects,
665            &mut mlme,
666            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
667        )
668        .await;
669
670        // One more timeout to trigger the auto deauth
671        let (_, timed_event, _) =
672            mock_objects.time_stream.try_recv().expect("Should have scheduled a timed event");
673
674        // Verify that triggering event at deadline causes deauth
675        mlme.handle_timeout(timed_event.event).await;
676        mock_objects
677            .fake_device_state
678            .lock()
679            .next_mlme_msg::<fidl_internal::SignalReportIndication>()
680            .expect("error reading SignalReport.indication");
681        assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 1);
682        #[rustfmt::skip]
683        assert_eq!(&mock_objects.fake_device_state.lock().wlan_queue[0].0[..], &[
684            // Mgmt header:
685            0b1100_00_00, 0b00000000, // FC
686            0, 0, // Duration
687            6, 6, 6, 6, 6, 6, // addr1
688            7, 7, 7, 7, 7, 7, // addr2
689            6, 6, 6, 6, 6, 6, // addr3
690            0x10, 0, // Sequence Control
691            3, 0, // reason code
692        ][..]);
693        let deauth_ind = mock_objects
694            .fake_device_state
695            .lock()
696            .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
697            .expect("error reading DEAUTHENTICATE.indication");
698        assert_eq!(
699            deauth_ind,
700            fidl_mlme::DeauthenticateIndication {
701                peer_sta_address: BSSID.to_array(),
702                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
703                locally_initiated: true,
704            }
705        );
706    }
707
708    #[fuchsia::test(allow_stalls = false)]
709    async fn test_auto_deauth_received_beacon() {
710        let mut mock_objects = MockObjects::new().await;
711        let mut mlme = mock_objects.make_mlme().await;
712        mlme.make_client_station();
713        let mut client = mlme.get_bound_client().expect("client should be present");
714
715        client.move_to_associated_state();
716
717        // Move the countdown to just about to cause auto deauth.
718        handle_association_status_checks_and_signal_reports(
719            &mut mock_objects,
720            &mut mlme,
721            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
722        )
723        .await;
724
725        // Receive beacon midway, so lost bss countdown is reset.
726        // If this beacon is not received, the next timeout will trigger auto deauth.
727        let main_channel = mlme.channel_state.get_primary().unwrap();
728        mlme.handle_mac_frame_rx(
729            BEACON_FRAME,
730            fidl_softmac::WlanRxInfo {
731                rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
732                valid_fields: fidl_softmac::WlanRxInfoValid::empty(),
733                phy: fidl_ieee80211::WlanPhyType::Dsss,
734                data_rate: 0,
735                primary: main_channel,
736                mcs: 0,
737                rssi_dbm: 0,
738                snr_dbh: 0,
739                bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
740                vht_secondary_80_channel: fidl_ieee80211::ChannelNumber {
741                    band: main_channel.band,
742                    number: 0,
743                },
744            },
745            0.into(),
746        )
747        .await;
748
749        // Verify auto deauth is not triggered for the entire duration.
750        handle_association_status_checks_and_signal_reports(
751            &mut mock_objects,
752            &mut mlme,
753            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
754        )
755        .await;
756
757        // Verify more timer is scheduled
758        let (_, timed_event2, _) =
759            mock_objects.time_stream.try_recv().expect("Should have scheduled a timed event");
760
761        // Verify that triggering event at new deadline causes deauth
762        mlme.handle_timeout(timed_event2.event).await;
763        mock_objects
764            .fake_device_state
765            .lock()
766            .next_mlme_msg::<fidl_internal::SignalReportIndication>()
767            .expect("error reading SignalReport.indication");
768        assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 1);
769        #[rustfmt::skip]
770        assert_eq!(&mock_objects.fake_device_state.lock().wlan_queue[0].0[..], &[
771            // Mgmt header:
772            0b1100_00_00, 0b00000000, // FC
773            0, 0, // Duration
774            6, 6, 6, 6, 6, 6, // addr1
775            7, 7, 7, 7, 7, 7, // addr2
776            6, 6, 6, 6, 6, 6, // addr3
777            0x10, 0, // Sequence Control
778            3, 0, // reason code
779        ][..]);
780        let deauth_ind = mock_objects
781            .fake_device_state
782            .lock()
783            .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
784            .expect("error reading DEAUTHENTICATE.indication");
785        assert_eq!(
786            deauth_ind,
787            fidl_mlme::DeauthenticateIndication {
788                peer_sta_address: BSSID.to_array(),
789                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
790                locally_initiated: true,
791            }
792        );
793    }
794
795    #[fuchsia::test(allow_stalls = false)]
796    async fn client_send_scan_end_on_mlme_scan_busy() {
797        let mut m = MockObjects::new().await;
798        let mut me = m.make_mlme().await;
799        me.make_client_station();
800
801        // Issue a second scan before the first finishes
802        me.on_sme_scan(scan_req()).await;
803        me.on_sme_scan(fidl_mlme::ScanRequest { txn_id: 1338, ..scan_req() }).await;
804
805        let scan_end = m
806            .fake_device_state
807            .lock()
808            .next_mlme_msg::<fidl_mlme::ScanEnd>()
809            .expect("error reading MLME ScanEnd");
810        assert_eq!(
811            scan_end,
812            fidl_mlme::ScanEnd { txn_id: 1338, code: fidl_mlme::ScanResultCode::NotSupported }
813        );
814    }
815
816    #[fuchsia::test(allow_stalls = false)]
817    async fn client_send_scan_end_on_scan_busy() {
818        let mut m = MockObjects::new().await;
819        let mut me = m.make_mlme().await;
820        me.make_client_station();
821
822        // Issue a second scan before the first finishes
823        me.on_sme_scan(scan_req()).await;
824        me.on_sme_scan(fidl_mlme::ScanRequest { txn_id: 1338, ..scan_req() }).await;
825
826        let scan_end = m
827            .fake_device_state
828            .lock()
829            .next_mlme_msg::<fidl_mlme::ScanEnd>()
830            .expect("error reading MLME ScanEnd");
831        assert_eq!(
832            scan_end,
833            fidl_mlme::ScanEnd { txn_id: 1338, code: fidl_mlme::ScanResultCode::NotSupported }
834        );
835    }
836
837    #[fuchsia::test(allow_stalls = false)]
838    async fn client_send_scan_end_on_mlme_scan_invalid_args() {
839        let mut m = MockObjects::new().await;
840        let mut me = m.make_mlme().await;
841
842        me.make_client_station();
843        me.on_sme_scan(fidl_mlme::ScanRequest {
844            txn_id: 1337,
845            scan_type: fidl_mlme::ScanTypes::Passive,
846            channel_list: vec![], // empty channel list
847            ssid_list: vec![Ssid::try_from("ssid").unwrap().into()],
848            probe_delay: 0,
849            min_channel_time: 100,
850            max_channel_time: 300,
851        })
852        .await;
853        let scan_end = m
854            .fake_device_state
855            .lock()
856            .next_mlme_msg::<fidl_mlme::ScanEnd>()
857            .expect("error reading MLME ScanEnd");
858        assert_eq!(
859            scan_end,
860            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::InvalidArgs }
861        );
862    }
863
864    #[fuchsia::test(allow_stalls = false)]
865    async fn client_send_scan_end_on_scan_invalid_args() {
866        let mut m = MockObjects::new().await;
867        let mut me = m.make_mlme().await;
868
869        me.make_client_station();
870        me.on_sme_scan(fidl_mlme::ScanRequest {
871            txn_id: 1337,
872            scan_type: fidl_mlme::ScanTypes::Passive,
873            channel_list: vec![fidl_ieee80211::ChannelNumber {
874                band: fidl_ieee80211::WlanBand::TwoGhz,
875                number: 6,
876            }],
877            ssid_list: vec![Ssid::try_from("ssid").unwrap().into()],
878            probe_delay: 0,
879            min_channel_time: 300, // min > max
880            max_channel_time: 100,
881        })
882        .await;
883        let scan_end = m
884            .fake_device_state
885            .lock()
886            .next_mlme_msg::<fidl_mlme::ScanEnd>()
887            .expect("error reading MLME ScanEnd");
888        assert_eq!(
889            scan_end,
890            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::InvalidArgs }
891        );
892    }
893
894    #[fuchsia::test(allow_stalls = false)]
895    async fn client_send_scan_end_on_passive_scan_fails() {
896        let mut m = MockObjects::new().await;
897        m.fake_device_state.lock().config.start_passive_scan_fails = true;
898        let mut me = m.make_mlme().await;
899
900        me.make_client_station();
901        me.on_sme_scan(scan_req()).await;
902        let scan_end = m
903            .fake_device_state
904            .lock()
905            .next_mlme_msg::<fidl_mlme::ScanEnd>()
906            .expect("error reading MLME ScanEnd");
907        assert_eq!(
908            scan_end,
909            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::NotSupported }
910        );
911    }
912
913    #[fuchsia::test(allow_stalls = false)]
914    async fn mlme_respond_to_query_device_info() {
915        let mut mock_objects = MockObjects::new().await;
916        let mut mlme = mock_objects.make_mlme().await;
917
918        let (responder, receiver) = Responder::new();
919        mlme.handle_mlme_request(wlan_sme::MlmeRequest::QueryDeviceInfo(responder))
920            .await
921            .expect("Failed to send MlmeRequest::Connect");
922        assert_eq!(
923            receiver.await.unwrap(),
924            fidl_mlme::DeviceInfo {
925                sta_addr: IFACE_MAC.to_array(),
926                factory_addr: IFACE_MAC.to_array(),
927                role: fidl_common::WlanMacRole::Client,
928                bands: test_utils::fake_mlme_band_caps(),
929                softmac_hardware_capability: 0,
930                qos_capable: false,
931            }
932        );
933    }
934
935    #[fuchsia::test(allow_stalls = false)]
936    async fn mlme_respond_to_query_mac_sublayer_support() {
937        let mut m = MockObjects::new().await;
938        let mut me = m.make_mlme().await;
939
940        let (responder, receiver) = Responder::new();
941        me.handle_mlme_request(wlan_sme::MlmeRequest::QueryMacSublayerSupport(responder))
942            .await
943            .expect("Failed to send MlmeRequest::Connect");
944        let resp = receiver.await.unwrap();
945        assert_eq!(resp.rate_selection_offload.unwrap().supported, Some(false));
946        assert_eq!(
947            resp.data_plane.unwrap().data_plane_type,
948            Some(fidl_common::DataPlaneType::EthernetDevice)
949        );
950        assert_eq!(resp.device.as_ref().unwrap().is_synthetic, Some(true));
951        assert_eq!(
952            resp.device.as_ref().unwrap().mac_implementation_type,
953            Some(fidl_common::MacImplementationType::Softmac)
954        );
955        assert_eq!(resp.device.unwrap().tx_status_report_supported, Some(true));
956    }
957
958    #[fuchsia::test(allow_stalls = false)]
959    async fn mlme_respond_to_query_security_support() {
960        let mut m = MockObjects::new().await;
961        let mut me = m.make_mlme().await;
962
963        let (responder, receiver) = Responder::new();
964        assert_matches!(
965            me.handle_mlme_request(wlan_sme::MlmeRequest::QuerySecuritySupport(responder)).await,
966            Ok(())
967        );
968        let resp = receiver.await.unwrap();
969        assert_eq!(resp.mfp.unwrap().supported, Some(false));
970        assert_eq!(resp.sae.as_ref().unwrap().driver_handler_supported, Some(false));
971        assert_eq!(resp.sae.unwrap().sme_handler_supported, Some(false));
972    }
973
974    #[fuchsia::test(allow_stalls = false)]
975    async fn mlme_respond_to_query_spectrum_management_support() {
976        let mut m = MockObjects::new().await;
977        let mut me = m.make_mlme().await;
978
979        let (responder, receiver) = Responder::new();
980        me.handle_mlme_request(wlan_sme::MlmeRequest::QuerySpectrumManagementSupport(responder))
981            .await
982            .expect("Failed to send MlmeRequest::QuerySpectrumManagementSupport");
983        assert_eq!(receiver.await.unwrap().dfs.unwrap().supported, Some(true));
984    }
985
986    #[fuchsia::test(allow_stalls = false)]
987    async fn mlme_connect_unprotected_happy_path() {
988        let mut m = MockObjects::new().await;
989        let mut me = m.make_mlme().await;
990        let channel = Channel::new(6, Bandwidth::Cbw40, fidl_ieee80211::WlanBand::TwoGhz);
991        let connect_req = fidl_mlme::ConnectRequest {
992            selected_bss: fake_fidl_bss_description!(Open,
993                ssid: Ssid::try_from("ssid").unwrap().into(),
994                bssid: BSSID.to_array(),
995                channel: channel.clone(),
996            ),
997            connect_failure_timeout: 100,
998            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
999            sae_password: vec![],
1000            wep_key: None,
1001            security_ie: vec![],
1002            owe_public_key: None,
1003        };
1004        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
1005            .await
1006            .expect("Failed to send MlmeRequest::Connect");
1007
1008        // Verify an event was queued up in the timer.
1009        assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
1010            assert_eq!(ids.len(), 1);
1011        });
1012
1013        // Verify authentication frame was sent to AP.
1014        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1015        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1016        #[rustfmt::skip]
1017        let expected = vec![
1018            // Mgmt Header:
1019            0b1011_00_00, 0b00000000, // Frame Control
1020            0, 0, // Duration
1021            6, 6, 6, 6, 6, 6, // Addr1
1022            7, 7, 7, 7, 7, 7, // Addr2
1023            6, 6, 6, 6, 6, 6, // Addr3
1024            0x10, 0, // Sequence Control
1025            // Auth Header:
1026            0, 0, // Algorithm Number (Open)
1027            1, 0, // Txn Sequence Number
1028            0, 0, // Status Code
1029        ];
1030        assert_eq!(&frame[..], &expected[..]);
1031
1032        // Mock auth frame response from the AP
1033        #[rustfmt::skip]
1034        let auth_resp_success = vec![
1035            // Mgmt Header:
1036            0b1011_00_00, 0b00000000, // Frame Control
1037            0, 0, // Duration
1038            7, 7, 7, 7, 7, 7, // Addr1
1039            7, 7, 7, 7, 7, 7, // Addr2
1040            6, 6, 6, 6, 6, 6, // Addr3
1041            0x10, 0, // Sequence Control
1042            // Auth Header:
1043            0, 0, // Algorithm Number (Open)
1044            2, 0, // Txn Sequence Number
1045            0, 0, // Status Code
1046        ];
1047        me.handle_mac_frame_rx(
1048            &auth_resp_success[..],
1049            MockWlanRxInfo::with_channel(channel.into()).into(),
1050            0.into(),
1051        )
1052        .await;
1053
1054        // Verify association request frame was went to AP
1055        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1056        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1057        #[rustfmt::skip]
1058        let expected = vec![
1059            // Mgmt header:
1060            0, 0, // FC
1061            0, 0, // Duration
1062            6, 6, 6, 6, 6, 6, // addr1
1063            7, 7, 7, 7, 7, 7, // addr2
1064            6, 6, 6, 6, 6, 6, // addr3
1065            0x20, 0, // Sequence Control
1066            // Association Request header:
1067            0x01, 0x00, // capability info
1068            0, 0, // listen interval
1069            // IEs
1070            0, 4, // SSID id and length
1071            0x73, 0x73, 0x69, 0x64, // SSID
1072            1, 8, // supp rates id and length
1073            2, 4, 11, 22, 12, 18, 24, 36, // supp rates
1074            50, 4, // ext supp rates and length
1075            48, 72, 96, 108, // ext supp rates
1076            45, 26, // HT Cap id and length
1077            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
1078            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
1079            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
1080        ];
1081        assert_eq!(&frame[..], &expected[..]);
1082
1083        // Mock assoc resp frame from the AP
1084        #[rustfmt::skip]
1085        let assoc_resp_success = vec![
1086            // Mgmt Header:
1087            0b0001_00_00, 0b00000000, // Frame Control
1088            0, 0, // Duration
1089            7, 7, 7, 7, 7, 7, // Addr1 == IFACE_MAC
1090            7, 7, 7, 7, 7, 7, // Addr2
1091            6, 6, 6, 6, 6, 6, // Addr3
1092            0x20, 0, // Sequence Control
1093            // Assoc Resp Header:
1094            0, 0, // Capabilities
1095            0, 0, // Status Code
1096            42, 0, // AID
1097            // IEs
1098            // Basic Rates
1099            0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
1100            // HT Capabilities
1101            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1102            0x17, // A-MPDU parameters
1103            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1104            // VHT Capabilities
1105            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1106            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1107        ];
1108        me.handle_mac_frame_rx(
1109            &assoc_resp_success[..],
1110            MockWlanRxInfo::with_channel(channel.into()).into(),
1111            0.into(),
1112        )
1113        .await;
1114
1115        // Verify a successful connect conf is sent
1116        let msg = m
1117            .fake_device_state
1118            .lock()
1119            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1120            .expect("expect ConnectConf");
1121        assert_eq!(
1122            msg,
1123            fidl_mlme::ConnectConfirm {
1124                peer_sta_address: BSSID.to_array(),
1125                result_code: fidl_ieee80211::StatusCode::Success,
1126                association_id: 42,
1127                association_ies: vec![
1128                    // IEs
1129                    // Basic Rates
1130                    0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
1131                    // HT Capabilities
1132                    0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1133                    0x17, // A-MPDU parameters
1134                    0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1135                    0x00, 0x00, // VHT Capabilities
1136                    0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1137                    0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1138                ],
1139            }
1140        );
1141
1142        // Verify eth link is up
1143        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::UP);
1144    }
1145
1146    #[fuchsia::test(allow_stalls = false)]
1147    async fn mlme_connect_protected_happy_path() {
1148        let mut m = MockObjects::new().await;
1149        let mut me = m.make_mlme().await;
1150        let channel = Channel::new(6, Bandwidth::Cbw40, fidl_ieee80211::WlanBand::TwoGhz);
1151        let connect_req = fidl_mlme::ConnectRequest {
1152            selected_bss: fake_fidl_bss_description!(Wpa2,
1153                ssid: Ssid::try_from("ssid").unwrap().into(),
1154                bssid: BSSID.to_array(),
1155                channel: channel.clone(),
1156            ),
1157            connect_failure_timeout: 100,
1158            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1159            sae_password: vec![],
1160            wep_key: None,
1161            security_ie: vec![
1162                48, 18, // RSNE header
1163                1, 0, // Version
1164                0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
1165                1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
1166                1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
1167            ],
1168            owe_public_key: None,
1169        };
1170        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
1171            .await
1172            .expect("Failed to send MlmeRequest::Connect");
1173
1174        // Verify an event was queued up in the timer.
1175        assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
1176            assert_eq!(ids.len(), 1);
1177        });
1178
1179        // Verify authentication frame was sent to AP.
1180        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1181        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1182        #[rustfmt::skip]
1183        let expected = vec![
1184            // Mgmt Header:
1185            0b1011_00_00, 0b00000000, // Frame Control
1186            0, 0, // Duration
1187            6, 6, 6, 6, 6, 6, // Addr1
1188            7, 7, 7, 7, 7, 7, // Addr2
1189            6, 6, 6, 6, 6, 6, // Addr3
1190            0x10, 0, // Sequence Control
1191            // Auth Header:
1192            0, 0, // Algorithm Number (Open)
1193            1, 0, // Txn Sequence Number
1194            0, 0, // Status Code
1195        ];
1196        assert_eq!(&frame[..], &expected[..]);
1197
1198        // Mock auth frame response from the AP
1199        #[rustfmt::skip]
1200        let auth_resp_success = vec![
1201            // Mgmt Header:
1202            0b1011_00_00, 0b00000000, // Frame Control
1203            0, 0, // Duration
1204            7, 7, 7, 7, 7, 7, // Addr1
1205            7, 7, 7, 7, 7, 7, // Addr2
1206            6, 6, 6, 6, 6, 6, // Addr3
1207            0x10, 0, // Sequence Control
1208            // Auth Header:
1209            0, 0, // Algorithm Number (Open)
1210            2, 0, // Txn Sequence Number
1211            0, 0, // Status Code
1212        ];
1213        me.handle_mac_frame_rx(
1214            &auth_resp_success[..],
1215            MockWlanRxInfo::with_channel(channel.into()).into(),
1216            0.into(),
1217        )
1218        .await;
1219
1220        // Verify association request frame was went to AP
1221        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1222        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1223        #[rustfmt::skip]
1224        let expected = vec![
1225            // Mgmt header:
1226            0, 0, // FC
1227            0, 0, // Duration
1228            6, 6, 6, 6, 6, 6, // addr1
1229            7, 7, 7, 7, 7, 7, // addr2
1230            6, 6, 6, 6, 6, 6, // addr3
1231            0x20, 0, // Sequence Control
1232            // Association Request header:
1233            0x01, 0x00, // capability info
1234            0, 0, // listen interval
1235            // IEs
1236            0, 4, // SSID id and length
1237            0x73, 0x73, 0x69, 0x64, // SSID
1238            1, 8, // supp rates id and length
1239            2, 4, 11, 22, 12, 18, 24, 36, // supp rates
1240            50, 4, // ext supp rates and length
1241            48, 72, 96, 108, // ext supp rates
1242            48, 18, // RSNE id and length
1243            1, 0, // RSN \
1244            0x00, 0x0F, 0xAC, 4, // RSN \
1245            1, 0, 0x00, 0x0F, 0xAC, 4, // RSN \
1246            1, 0, 0x00, 0x0F, 0xAC, 2, // RSN
1247            45, 26, // HT Cap id and length
1248            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
1249            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
1250            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
1251        ];
1252        assert_eq!(&frame[..], &expected[..]);
1253
1254        // Mock assoc resp frame from the AP
1255        #[rustfmt::skip]
1256        let assoc_resp_success = vec![
1257            // Mgmt Header:
1258            0b0001_00_00, 0b00000000, // Frame Control
1259            0, 0, // Duration
1260            7, 7, 7, 7, 7, 7, // Addr1 == IFACE_MAC
1261            7, 7, 7, 7, 7, 7, // Addr2
1262            6, 6, 6, 6, 6, 6, // Addr3
1263            0x20, 0, // Sequence Control
1264            // Assoc Resp Header:
1265            0, 0, // Capabilities
1266            0, 0, // Status Code
1267            42, 0, // AID
1268            // IEs
1269            // Basic Rates
1270            0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
1271            // RSN
1272            0x30, 18, 1, 0, // RSN header and version
1273            0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
1274            1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
1275            1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
1276            // HT Capabilities
1277            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1278            0x17, // A-MPDU parameters
1279            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Other HT Cap fields
1280            // VHT Capabilities
1281            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1282            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1283        ];
1284        me.handle_mac_frame_rx(
1285            &assoc_resp_success[..],
1286            MockWlanRxInfo::with_channel(channel.into()).into(),
1287            0.into(),
1288        )
1289        .await;
1290
1291        // Verify a successful connect conf is sent
1292        let msg = m
1293            .fake_device_state
1294            .lock()
1295            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1296            .expect("expect ConnectConf");
1297        assert_eq!(
1298            msg,
1299            fidl_mlme::ConnectConfirm {
1300                peer_sta_address: BSSID.to_array(),
1301                result_code: fidl_ieee80211::StatusCode::Success,
1302                association_id: 42,
1303                association_ies: vec![
1304                    // IEs
1305                    // Basic Rates
1306                    0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, // RSN
1307                    0x30, 18, 1, 0, // RSN header and version
1308                    0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
1309                    1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
1310                    1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
1311                    // HT Capabilities
1312                    0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1313                    0x17, // A-MPDU parameters
1314                    0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1315                    0x00, 0x00, // Other HT Cap fields
1316                    // VHT Capabilities
1317                    0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1318                    0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1319                ],
1320            }
1321        );
1322
1323        // Verify that link is still down
1324        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::DOWN);
1325
1326        // Send a request to open controlled port
1327        me.handle_mlme_request(wlan_sme::MlmeRequest::SetCtrlPort(
1328            fidl_mlme::SetControlledPortRequest {
1329                peer_sta_address: BSSID.to_array(),
1330                state: fidl_mlme::ControlledPortState::Open,
1331            },
1332        ))
1333        .await
1334        .expect("expect sending msg to succeed");
1335
1336        // Verify that link is now up
1337        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::UP);
1338    }
1339
1340    #[fuchsia::test(allow_stalls = false)]
1341    async fn mlme_connect_vht() {
1342        let mut m = MockObjects::new().await;
1343        let mut me = m.make_mlme().await;
1344        let channel = Channel::new(36, Bandwidth::Cbw40, fidl_ieee80211::WlanBand::FiveGhz);
1345        let connect_req = fidl_mlme::ConnectRequest {
1346            selected_bss: fake_fidl_bss_description!(Open,
1347                ssid: Ssid::try_from("ssid").unwrap().into(),
1348                bssid: BSSID.to_array(),
1349                channel: channel.clone(),
1350            ),
1351            connect_failure_timeout: 100,
1352            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1353            sae_password: vec![],
1354            wep_key: None,
1355            security_ie: vec![],
1356            owe_public_key: None,
1357        };
1358        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
1359            .await
1360            .expect("Failed to send MlmeRequest::Connect.");
1361
1362        // Verify an event was queued up in the timer.
1363        assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
1364            assert_eq!(ids.len(), 1);
1365        });
1366
1367        // Auth frame
1368        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1369        let (_frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1370
1371        // Mock auth frame response from the AP
1372        #[rustfmt::skip]
1373        let auth_resp_success = vec![
1374            // Mgmt Header:
1375            0b1011_00_00, 0b00000000, // Frame Control
1376            0, 0, // Duration
1377            7, 7, 7, 7, 7, 7, // Addr1
1378            7, 7, 7, 7, 7, 7, // Addr2
1379            6, 6, 6, 6, 6, 6, // Addr3
1380            0x10, 0, // Sequence Control
1381            // Auth Header:
1382            0, 0, // Algorithm Number (Open)
1383            2, 0, // Txn Sequence Number
1384            0, 0, // Status Code
1385        ];
1386        me.handle_mac_frame_rx(
1387            &auth_resp_success[..],
1388            MockWlanRxInfo::with_channel(channel.into()).into(),
1389            0.into(),
1390        )
1391        .await;
1392
1393        // Verify association request frame was went to AP
1394        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1395        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1396        #[rustfmt::skip]
1397        let expected = vec![
1398            // Mgmt header:
1399            0, 0, // FC
1400            0, 0, // Duration
1401            6, 6, 6, 6, 6, 6, // addr1
1402            7, 7, 7, 7, 7, 7, // addr2
1403            6, 6, 6, 6, 6, 6, // addr3
1404            0x20, 0, // Sequence Control
1405            // Association Request header:
1406            0x01, 0x00, // capability info
1407            0, 0, // listen interval
1408            // IEs
1409            0, 4, // SSID id and length
1410            0x73, 0x73, 0x69, 0x64, // SSID
1411            1, 6, // supp rates id and length
1412            2, 4, 11, 22, 48, 96, // supp rates
1413            45, 26, // HT Cap id and length
1414            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
1415            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
1416            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
1417            191, 12, // VHT Cap id and length
1418            50, 80, 128, 15, 254, 255, 0, 0, 254, 255, 0, 0, // VHT Cap
1419        ];
1420        assert_eq!(&frame[..], &expected[..]);
1421    }
1422
1423    #[fuchsia::test(allow_stalls = false)]
1424    async fn mlme_connect_timeout() {
1425        let mut m = MockObjects::new().await;
1426        let mut me = m.make_mlme().await;
1427        let connect_req = fidl_mlme::ConnectRequest {
1428            selected_bss: fake_fidl_bss_description!(Open, bssid: BSSID.to_array()),
1429            connect_failure_timeout: 100,
1430            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1431            sae_password: vec![],
1432            wep_key: None,
1433            security_ie: vec![],
1434            owe_public_key: None,
1435        };
1436        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
1437            .await
1438            .expect("Failed to send MlmeRequest::Connect.");
1439
1440        // Verify an event was queued up in the timer.
1441        let (event, _id) = assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(events) => {
1442            assert_eq!(events.len(), 1);
1443            events[0].clone()
1444        });
1445
1446        // Quick check that a frame was sent (this is authentication frame).
1447        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1448        let (_frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1449
1450        // Send connect timeout
1451        me.handle_timeout(event).await;
1452
1453        // Verify a connect confirm message was sent
1454        let msg = m
1455            .fake_device_state
1456            .lock()
1457            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1458            .expect("expect msg");
1459        assert_eq!(
1460            msg,
1461            fidl_mlme::ConnectConfirm {
1462                peer_sta_address: BSSID.to_array(),
1463                result_code: fidl_ieee80211::StatusCode::RejectedSequenceTimeout,
1464                association_id: 0,
1465                association_ies: vec![],
1466            },
1467        );
1468    }
1469
1470    #[fuchsia::test(allow_stalls = false)]
1471    async fn mlme_reconnect_no_sta() {
1472        let mut m = MockObjects::new().await;
1473        let mut me = m.make_mlme().await;
1474
1475        let reconnect_req = fidl_mlme::ReconnectRequest { peer_sta_address: [1, 2, 3, 4, 5, 6] };
1476        let result = me.handle_mlme_request(wlan_sme::MlmeRequest::Reconnect(reconnect_req)).await;
1477        let err = result.unwrap_err();
1478        let mlme_err = err.downcast_ref::<Error>().expect("expected Mlme Error");
1479        assert_matches!(mlme_err, Error::Status(_, zx::Status::BAD_STATE));
1480
1481        // Verify a connect confirm message was sent
1482        let msg = m
1483            .fake_device_state
1484            .lock()
1485            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1486            .expect("expect msg");
1487        assert_eq!(
1488            msg,
1489            fidl_mlme::ConnectConfirm {
1490                peer_sta_address: [1, 2, 3, 4, 5, 6],
1491                result_code: fidl_ieee80211::StatusCode::DeniedNoAssociationExists,
1492                association_id: 0,
1493                association_ies: vec![],
1494            },
1495        );
1496    }
1497
1498    #[fuchsia::test(allow_stalls = false)]
1499    async fn mlme_respond_to_get_iface_stats_with_error_status() {
1500        let mut m = MockObjects::new().await;
1501        let mut me = m.make_mlme().await;
1502
1503        let (responder, receiver) = Responder::new();
1504        me.handle_mlme_request(wlan_sme::MlmeRequest::GetIfaceStats(responder))
1505            .await
1506            .expect("Failed to send MlmeRequest::GetIfaceStats.");
1507        assert_eq!(
1508            receiver.await,
1509            Ok(fidl_mlme::GetIfaceStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED))
1510        );
1511    }
1512
1513    #[fuchsia::test(allow_stalls = false)]
1514    async fn mlme_respond_to_get_iface_histogram_stats_with_error_status() {
1515        let mut m = MockObjects::new().await;
1516        let mut me = m.make_mlme().await;
1517
1518        let (responder, receiver) = Responder::new();
1519        me.handle_mlme_request(wlan_sme::MlmeRequest::GetIfaceHistogramStats(responder))
1520            .await
1521            .expect("Failed to send MlmeRequest::GetIfaceHistogramStats");
1522        assert_eq!(
1523            receiver.await,
1524            Ok(fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(
1525                zx::sys::ZX_ERR_NOT_SUPPORTED
1526            ))
1527        );
1528    }
1529
1530    #[test]
1531    fn drop_mgmt_frame_wrong_bssid() {
1532        let frame = [
1533            // Mgmt header 1101 for action frame
1534            0b11010000, 0b00000000, // frame control
1535            0, 0, // duration
1536            7, 7, 7, 7, 7, 7, // addr1
1537            6, 6, 6, 6, 6, 6, // addr2
1538            0, 0, 0, 0, 0, 0, // addr3 (bssid should have been [6; 6])
1539            0x10, 0, // sequence control
1540        ];
1541        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1542        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1543    }
1544
1545    #[test]
1546    fn drop_mgmt_frame_wrong_dst_addr() {
1547        let frame = [
1548            // Mgmt header 1101 for action frame
1549            0b11010000, 0b00000000, // frame control
1550            0, 0, // duration
1551            0, 0, 0, 0, 0, 0, // addr1 (dst_addr should have been [7; 6])
1552            6, 6, 6, 6, 6, 6, // addr2
1553            6, 6, 6, 6, 6, 6, // addr3
1554            0x10, 0, // sequence control
1555        ];
1556        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1557        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1558    }
1559
1560    #[test]
1561    fn mgmt_frame_ok_broadcast() {
1562        let frame = [
1563            // Mgmt header 1101 for action frame
1564            0b11010000, 0b00000000, // frame control
1565            0, 0, // duration
1566            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // addr1 (dst_addr is broadcast)
1567            6, 6, 6, 6, 6, 6, // addr2
1568            6, 6, 6, 6, 6, 6, // addr3
1569            0x10, 0, // sequence control
1570        ];
1571        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1572        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1573    }
1574
1575    #[test]
1576    fn mgmt_frame_ok_client_addr() {
1577        let frame = [
1578            // Mgmt header 1101 for action frame
1579            0b11010000, 0b00000000, // frame control
1580            0, 0, // duration
1581            7, 7, 7, 7, 7, 7, // addr1 (dst_addr should have been [7; 6])
1582            6, 6, 6, 6, 6, 6, // addr2
1583            6, 6, 6, 6, 6, 6, // addr3
1584            0x10, 0, // sequence control
1585        ];
1586        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1587        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1588    }
1589
1590    #[test]
1591    fn drop_data_frame_wrong_bssid() {
1592        let frame = [
1593            // Data header 0100
1594            0b01001000,
1595            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1596            0, 0, // duration
1597            7, 7, 7, 7, 7, 7, // addr1 (dst_addr)
1598            0, 0, 0, 0, 0, 0, // addr2 (bssid should have been [6; 6])
1599            6, 6, 6, 6, 6, 6, // addr3
1600            0x10, 0, // sequence control
1601        ];
1602        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1603        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1604    }
1605
1606    #[test]
1607    fn drop_data_frame_wrong_dst_addr() {
1608        let frame = [
1609            // Data header 0100
1610            0b01001000,
1611            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1612            0, 0, // duration
1613            0, 0, 0, 0, 0, 0, // addr1 (dst_addr should have been [7; 6])
1614            6, 6, 6, 6, 6, 6, // addr2 (bssid)
1615            6, 6, 6, 6, 6, 6, // addr3
1616            0x10, 0, // sequence control
1617        ];
1618        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1619        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1620    }
1621
1622    #[test]
1623    fn data_frame_ok_broadcast() {
1624        let frame = [
1625            // Data header 0100
1626            0b01001000,
1627            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1628            0, 0, // duration
1629            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // addr1 (dst_addr is broadcast)
1630            6, 6, 6, 6, 6, 6, // addr2 (bssid)
1631            6, 6, 6, 6, 6, 6, // addr3
1632            0x10, 0, // sequence control
1633        ];
1634        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1635        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1636    }
1637
1638    #[test]
1639    fn data_frame_ok_client_addr() {
1640        let frame = [
1641            // Data header 0100
1642            0b01001000,
1643            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1644            0, 0, // duration
1645            7, 7, 7, 7, 7, 7, // addr1 (dst_addr)
1646            6, 6, 6, 6, 6, 6, // addr2 (bssid)
1647            6, 6, 6, 6, 6, 6, // addr3
1648            0x10, 0, // sequence control
1649        ];
1650        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1651        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1652    }
1653}