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) if self.sta.is_none() => {
163                responder.respond(Ok(fidl_stats::SignalReport::default()));
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_main_channel(),
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_main_channel() {
239                Some(main_channel) if main_channel.primary == rx_info.channel.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.channel
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: 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        channel: fidl_ieee80211::WlanChannel,
346    ) -> Result<(), zx::Status> {
347        self.channel_state.bind(&mut self.ctx, &mut self.scanner).set_main_channel(channel).await
348    }
349
350    async fn on_sme_scan(&mut self, req: fidl_mlme::ScanRequest) {
351        let txn_id = req.txn_id;
352        let _ = self.scanner.bind(&mut self.ctx).on_sme_scan(req).await.map_err(|e| {
353            error!("Scan failed in MLME: {:?}", e);
354            let code = match e {
355                Error::ScanError(scan_error) => scan_error.into(),
356                _ => fidl_mlme::ScanResultCode::InternalError,
357            };
358            self.ctx
359                .device
360                .send_mlme_event(fidl_mlme::MlmeEvent::OnScanEnd {
361                    end: fidl_mlme::ScanEnd { txn_id, code },
362                })
363                .unwrap_or_else(|e| error!("error sending MLME ScanEnd: {}", e));
364        });
365    }
366
367    async fn on_sme_connect(&mut self, req: fidl_mlme::ConnectRequest) -> Result<(), Error> {
368        // Cancel any ongoing scan so that it doesn't conflict with the connect request
369        // TODO(b/254290448): Use enable/disable scanning for better guarantees.
370        if let Err(e) = self.scanner.bind(&mut self.ctx).cancel_ongoing_scan().await {
371            warn!("Failed to cancel ongoing scan before connect: {}.", e);
372        }
373
374        let bssid = req.selected_bss.bssid;
375        let result = match req.selected_bss.try_into() {
376            Ok(bss) => {
377                let req = ParsedConnectRequest {
378                    selected_bss: bss,
379                    connect_failure_timeout: req.connect_failure_timeout,
380                    auth_type: req.auth_type,
381                    security_ie: req.security_ie,
382                };
383                self.join_device(&req.selected_bss).await.map(|cap| (req, cap))
384            }
385            Err(e) => Err(Error::Status(
386                format!("Error parsing BssDescription: {:?}", e),
387                zx::Status::IO_INVALID,
388            )),
389        };
390
391        match result {
392            Ok((req, client_capabilities)) => {
393                self.sta.replace(Client::new(
394                    req,
395                    device::try_query_iface_mac(&mut self.ctx.device).await?,
396                    client_capabilities,
397                ));
398                if let Some(sta) = &mut self.sta {
399                    sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
400                        .start_connecting()
401                        .await;
402                }
403                Ok(())
404            }
405            Err(e) => {
406                error!("Error setting up device for join: {}", e);
407                // TODO(https://fxbug.dev/42120718): Only one failure code defined in IEEE 802.11-2016 6.3.4.3
408                // Can we do better?
409                self.ctx.device.send_mlme_event(fidl_mlme::MlmeEvent::ConnectConf {
410                    resp: fidl_mlme::ConnectConfirm {
411                        peer_sta_address: bssid,
412                        result_code: fidl_ieee80211::StatusCode::JoinFailure,
413                        association_id: 0,
414                        association_ies: vec![],
415                    },
416                })?;
417                Err(e)
418            }
419        }
420    }
421
422    async fn join_device(&mut self, bss: &BssDescription) -> Result<ClientCapabilities, Error> {
423        let info = ddk_converter::mlme_device_info_from_softmac(
424            device::try_query(&mut self.ctx.device).await?,
425        )?;
426        let join_caps = derive_join_capabilities(Channel::from(bss.channel), bss.rates(), &info)
427            .map_err(|e| {
428                Error::Status(
429                    format!("Failed to derive join capabilities: {:?}", e),
430                    zx::Status::NOT_SUPPORTED,
431                )
432            })?;
433
434        self.set_main_channel(bss.channel.into())
435            .await
436            .map_err(|status| Error::Status(format!("Error setting device channel"), status))?;
437
438        let join_bss_request = fidl_driver_common::JoinBssRequest {
439            bssid: Some(bss.bssid.to_array()),
440            bss_type: Some(fidl_ieee80211::BssType::Infrastructure),
441            remote: Some(true),
442            beacon_period: Some(bss.beacon_period),
443            ..Default::default()
444        };
445
446        // Configure driver to pass frames from this BSS to MLME. Otherwise they will be dropped.
447        self.ctx
448            .device
449            .join_bss(&join_bss_request)
450            .await
451            .map(|()| join_caps)
452            .map_err(|status| Error::Status(format!("Error setting BSS in driver"), status))
453    }
454
455    async fn on_sme_query_device_info(
456        &mut self,
457        responder: wlan_sme::responder::Responder<fidl_mlme::DeviceInfo>,
458    ) -> Result<(), Error> {
459        let info = ddk_converter::mlme_device_info_from_softmac(
460            device::try_query(&mut self.ctx.device).await?,
461        )?;
462        responder.respond(info);
463        Ok(())
464    }
465
466    async fn on_sme_query_mac_sublayer_support(
467        &mut self,
468        responder: wlan_sme::responder::Responder<fidl_common::MacSublayerSupport>,
469    ) -> Result<(), Error> {
470        let support = device::try_query_mac_sublayer_support(&mut self.ctx.device).await?;
471        responder.respond(support);
472        Ok(())
473    }
474
475    async fn on_sme_query_security_support(
476        &mut self,
477        responder: wlan_sme::responder::Responder<fidl_common::SecuritySupport>,
478    ) -> Result<(), Error> {
479        let support = device::try_query_security_support(&mut self.ctx.device).await?;
480        responder.respond(support);
481        Ok(())
482    }
483
484    async fn on_sme_query_spectrum_management_support(
485        &mut self,
486        responder: wlan_sme::responder::Responder<fidl_common::SpectrumManagementSupport>,
487    ) -> Result<(), Error> {
488        let support = device::try_query_spectrum_management_support(&mut self.ctx.device).await?;
489        responder.respond(support);
490        Ok(())
491    }
492}
493
494pub struct ParsedAssociateResp {
495    pub association_id: u16,
496    pub capabilities: CapabilityInfo,
497    pub rates: Vec<ie::SupportedRate>,
498    pub ht_cap: Option<ie::HtCapabilities>,
499    pub vht_cap: Option<ie::VhtCapabilities>,
500}
501
502impl ParsedAssociateResp {
503    pub fn parse<B: SplitByteSlice>(assoc_resp_frame: &mac::AssocRespFrame<B>) -> Self {
504        let mut parsed = ParsedAssociateResp {
505            association_id: assoc_resp_frame.assoc_resp_hdr.aid,
506            capabilities: assoc_resp_frame.assoc_resp_hdr.capabilities,
507            rates: vec![],
508            ht_cap: None,
509            vht_cap: None,
510        };
511        for (id, body) in assoc_resp_frame.ies() {
512            match id {
513                Id::SUPPORTED_RATES => match ie::parse_supported_rates(body) {
514                    Err(e) => warn!("invalid Supported Rates: {}", e),
515                    Ok(supported_rates) => {
516                        // safe to unwrap because supported rate is 1-byte long thus always aligned
517                        parsed.rates.extend(supported_rates.iter());
518                    }
519                },
520                Id::EXTENDED_SUPPORTED_RATES => match ie::parse_extended_supported_rates(body) {
521                    Err(e) => warn!("invalid Extended Supported Rates: {}", e),
522                    Ok(supported_rates) => {
523                        // safe to unwrap because supported rate is 1-byte long thus always aligned
524                        parsed.rates.extend(supported_rates.iter());
525                    }
526                },
527                Id::HT_CAPABILITIES => match ie::parse_ht_capabilities(body) {
528                    Err(e) => warn!("invalid HT Capabilities: {}", e),
529                    Ok(ht_cap) => {
530                        parsed.ht_cap = Some(*ht_cap);
531                    }
532                },
533                Id::VHT_CAPABILITIES => match ie::parse_vht_capabilities(body) {
534                    Err(e) => warn!("invalid VHT Capabilities: {}", e),
535                    Ok(vht_cap) => {
536                        parsed.vht_cap = Some(*vht_cap);
537                    }
538                },
539                // TODO(https://fxbug.dev/42120297): parse vendor ID and include WMM param if exists
540                _ => {}
541            }
542        }
543        parsed
544    }
545}
546
547#[cfg(test)]
548mod tests {
549    use super::state::DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT;
550    use super::*;
551    use crate::MlmeImpl;
552    use crate::client::test_utils::*;
553    use crate::device::{FakeDevice, LinkStatus, test_utils};
554    use crate::test_utils::MockWlanRxInfo;
555    use assert_matches::assert_matches;
556    use fidl_fuchsia_wlan_common as fidl_common;
557    use fidl_fuchsia_wlan_internal as fidl_internal;
558    use fidl_fuchsia_wlan_mlme as fidl_mlme;
559    use ieee80211::Ssid;
560    use wlan_common::channel::Cbw;
561    use wlan_common::fake_fidl_bss_description;
562    use wlan_sme::responder::Responder;
563
564    #[fuchsia::test(allow_stalls = false)]
565    async fn spawns_new_sta_on_connect_request_from_sme() {
566        let mut m = MockObjects::new().await;
567        let mut me = m.make_mlme().await;
568        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
569        me.on_sme_connect(fidl_mlme::ConnectRequest {
570            selected_bss: fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
571            connect_failure_timeout: 100,
572            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
573            sae_password: vec![],
574            wep_key: None,
575            security_ie: vec![],
576            owe_public_key: None,
577        })
578        .await
579        .expect("valid ConnectRequest should be handled successfully");
580        me.get_bound_client().expect("client sta should have been created by now.");
581    }
582
583    #[fuchsia::test(allow_stalls = false)]
584    async fn fails_to_connect_if_channel_unknown() {
585        let mut m = MockObjects::new().await;
586        let mut me = m.make_mlme().await;
587        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
588        let mut req = fidl_mlme::ConnectRequest {
589            selected_bss: fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
590            connect_failure_timeout: 100,
591            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
592            sae_password: vec![],
593            wep_key: None,
594            security_ie: vec![],
595            owe_public_key: None,
596        };
597
598        req.selected_bss.channel.cbw = fidl_fuchsia_wlan_ieee80211::ChannelBandwidth::unknown();
599        me.on_sme_connect(req)
600            .await
601            .expect_err("ConnectRequest with unknown channel should be rejected");
602        assert!(me.get_bound_client().is_none());
603    }
604
605    /// Consumes `TimedEvent` values from the `timer::EventStream` held by `mock_objects` and
606    /// handles each `TimedEvent` value with `mlme`. This function makes the following assertions:
607    ///
608    ///   - The `timer::EventStream` held by `mock_objects` starts with one `StatusCheckTimeout`
609    ///     pending.
610    ///   - For the `beacon_count` specified, `mlme` will consume the current `StatusCheckTimeout`
611    ///     and schedule the next.
612    ///   - `mlme` produces a `fidl_mlme::SignalReportIndication` for each StatusCheckTimeout
613    ///     consumed.
614    async fn handle_association_status_checks_and_signal_reports(
615        mock_objects: &mut MockObjects,
616        mlme: &mut ClientMlme<FakeDevice>,
617        beacon_count: u32,
618    ) {
619        for _ in 0..beacon_count / super::state::ASSOCIATION_STATUS_TIMEOUT_BEACON_COUNT {
620            let (_, timed_event, _) = mock_objects
621                .time_stream
622                .try_next()
623                .unwrap()
624                .expect("Should have scheduled a timed event");
625            mlme.handle_timeout(timed_event.event).await;
626            assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 0);
627            mock_objects
628                .fake_device_state
629                .lock()
630                .next_mlme_msg::<fidl_internal::SignalReportIndication>()
631                .expect("error reading SignalReport.indication");
632        }
633    }
634
635    #[fuchsia::test(allow_stalls = false)]
636    async fn test_auto_deauth_uninterrupted_interval() {
637        let mut mock_objects = MockObjects::new().await;
638        let mut mlme = mock_objects.make_mlme().await;
639        mlme.make_client_station();
640        let mut client = mlme.get_bound_client().expect("client should be present");
641
642        client.move_to_associated_state();
643
644        // Verify timer is scheduled and move the time to immediately before auto deauth is triggered.
645        handle_association_status_checks_and_signal_reports(
646            &mut mock_objects,
647            &mut mlme,
648            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
649        )
650        .await;
651
652        // One more timeout to trigger the auto deauth
653        let (_, timed_event, _) = mock_objects
654            .time_stream
655            .try_next()
656            .unwrap()
657            .expect("Should have scheduled a timed event");
658
659        // Verify that triggering event at deadline causes deauth
660        mlme.handle_timeout(timed_event.event).await;
661        mock_objects
662            .fake_device_state
663            .lock()
664            .next_mlme_msg::<fidl_internal::SignalReportIndication>()
665            .expect("error reading SignalReport.indication");
666        assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 1);
667        #[rustfmt::skip]
668        assert_eq!(&mock_objects.fake_device_state.lock().wlan_queue[0].0[..], &[
669            // Mgmt header:
670            0b1100_00_00, 0b00000000, // FC
671            0, 0, // Duration
672            6, 6, 6, 6, 6, 6, // addr1
673            7, 7, 7, 7, 7, 7, // addr2
674            6, 6, 6, 6, 6, 6, // addr3
675            0x10, 0, // Sequence Control
676            3, 0, // reason code
677        ][..]);
678        let deauth_ind = mock_objects
679            .fake_device_state
680            .lock()
681            .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
682            .expect("error reading DEAUTHENTICATE.indication");
683        assert_eq!(
684            deauth_ind,
685            fidl_mlme::DeauthenticateIndication {
686                peer_sta_address: BSSID.to_array(),
687                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
688                locally_initiated: true,
689            }
690        );
691    }
692
693    #[fuchsia::test(allow_stalls = false)]
694    async fn test_auto_deauth_received_beacon() {
695        let mut mock_objects = MockObjects::new().await;
696        let mut mlme = mock_objects.make_mlme().await;
697        mlme.make_client_station();
698        let mut client = mlme.get_bound_client().expect("client should be present");
699
700        client.move_to_associated_state();
701
702        // Move the countdown to just about to cause auto deauth.
703        handle_association_status_checks_and_signal_reports(
704            &mut mock_objects,
705            &mut mlme,
706            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
707        )
708        .await;
709
710        // Receive beacon midway, so lost bss countdown is reset.
711        // If this beacon is not received, the next timeout will trigger auto deauth.
712        mlme.handle_mac_frame_rx(
713            BEACON_FRAME,
714            fidl_softmac::WlanRxInfo {
715                rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
716                valid_fields: fidl_softmac::WlanRxInfoValid::empty(),
717                phy: fidl_ieee80211::WlanPhyType::Dsss,
718                data_rate: 0,
719                channel: mlme.channel_state.get_main_channel().unwrap(),
720                mcs: 0,
721                rssi_dbm: 0,
722                snr_dbh: 0,
723            },
724            0.into(),
725        )
726        .await;
727
728        // Verify auto deauth is not triggered for the entire duration.
729        handle_association_status_checks_and_signal_reports(
730            &mut mock_objects,
731            &mut mlme,
732            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
733        )
734        .await;
735
736        // Verify more timer is scheduled
737        let (_, timed_event2, _) = mock_objects
738            .time_stream
739            .try_next()
740            .unwrap()
741            .expect("Should have scheduled a timed event");
742
743        // Verify that triggering event at new deadline causes deauth
744        mlme.handle_timeout(timed_event2.event).await;
745        mock_objects
746            .fake_device_state
747            .lock()
748            .next_mlme_msg::<fidl_internal::SignalReportIndication>()
749            .expect("error reading SignalReport.indication");
750        assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 1);
751        #[rustfmt::skip]
752        assert_eq!(&mock_objects.fake_device_state.lock().wlan_queue[0].0[..], &[
753            // Mgmt header:
754            0b1100_00_00, 0b00000000, // FC
755            0, 0, // Duration
756            6, 6, 6, 6, 6, 6, // addr1
757            7, 7, 7, 7, 7, 7, // addr2
758            6, 6, 6, 6, 6, 6, // addr3
759            0x10, 0, // Sequence Control
760            3, 0, // reason code
761        ][..]);
762        let deauth_ind = mock_objects
763            .fake_device_state
764            .lock()
765            .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
766            .expect("error reading DEAUTHENTICATE.indication");
767        assert_eq!(
768            deauth_ind,
769            fidl_mlme::DeauthenticateIndication {
770                peer_sta_address: BSSID.to_array(),
771                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
772                locally_initiated: true,
773            }
774        );
775    }
776
777    #[fuchsia::test(allow_stalls = false)]
778    async fn client_send_scan_end_on_mlme_scan_busy() {
779        let mut m = MockObjects::new().await;
780        let mut me = m.make_mlme().await;
781        me.make_client_station();
782
783        // Issue a second scan before the first finishes
784        me.on_sme_scan(scan_req()).await;
785        me.on_sme_scan(fidl_mlme::ScanRequest { txn_id: 1338, ..scan_req() }).await;
786
787        let scan_end = m
788            .fake_device_state
789            .lock()
790            .next_mlme_msg::<fidl_mlme::ScanEnd>()
791            .expect("error reading MLME ScanEnd");
792        assert_eq!(
793            scan_end,
794            fidl_mlme::ScanEnd { txn_id: 1338, code: fidl_mlme::ScanResultCode::NotSupported }
795        );
796    }
797
798    #[fuchsia::test(allow_stalls = false)]
799    async fn client_send_scan_end_on_scan_busy() {
800        let mut m = MockObjects::new().await;
801        let mut me = m.make_mlme().await;
802        me.make_client_station();
803
804        // Issue a second scan before the first finishes
805        me.on_sme_scan(scan_req()).await;
806        me.on_sme_scan(fidl_mlme::ScanRequest { txn_id: 1338, ..scan_req() }).await;
807
808        let scan_end = m
809            .fake_device_state
810            .lock()
811            .next_mlme_msg::<fidl_mlme::ScanEnd>()
812            .expect("error reading MLME ScanEnd");
813        assert_eq!(
814            scan_end,
815            fidl_mlme::ScanEnd { txn_id: 1338, code: fidl_mlme::ScanResultCode::NotSupported }
816        );
817    }
818
819    #[fuchsia::test(allow_stalls = false)]
820    async fn client_send_scan_end_on_mlme_scan_invalid_args() {
821        let mut m = MockObjects::new().await;
822        let mut me = m.make_mlme().await;
823
824        me.make_client_station();
825        me.on_sme_scan(fidl_mlme::ScanRequest {
826            txn_id: 1337,
827            scan_type: fidl_mlme::ScanTypes::Passive,
828            channel_list: vec![], // empty channel list
829            ssid_list: vec![Ssid::try_from("ssid").unwrap().into()],
830            probe_delay: 0,
831            min_channel_time: 100,
832            max_channel_time: 300,
833        })
834        .await;
835        let scan_end = m
836            .fake_device_state
837            .lock()
838            .next_mlme_msg::<fidl_mlme::ScanEnd>()
839            .expect("error reading MLME ScanEnd");
840        assert_eq!(
841            scan_end,
842            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::InvalidArgs }
843        );
844    }
845
846    #[fuchsia::test(allow_stalls = false)]
847    async fn client_send_scan_end_on_scan_invalid_args() {
848        let mut m = MockObjects::new().await;
849        let mut me = m.make_mlme().await;
850
851        me.make_client_station();
852        me.on_sme_scan(fidl_mlme::ScanRequest {
853            txn_id: 1337,
854            scan_type: fidl_mlme::ScanTypes::Passive,
855            channel_list: vec![6],
856            ssid_list: vec![Ssid::try_from("ssid").unwrap().into()],
857            probe_delay: 0,
858            min_channel_time: 300, // min > max
859            max_channel_time: 100,
860        })
861        .await;
862        let scan_end = m
863            .fake_device_state
864            .lock()
865            .next_mlme_msg::<fidl_mlme::ScanEnd>()
866            .expect("error reading MLME ScanEnd");
867        assert_eq!(
868            scan_end,
869            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::InvalidArgs }
870        );
871    }
872
873    #[fuchsia::test(allow_stalls = false)]
874    async fn client_send_scan_end_on_passive_scan_fails() {
875        let mut m = MockObjects::new().await;
876        m.fake_device_state.lock().config.start_passive_scan_fails = true;
877        let mut me = m.make_mlme().await;
878
879        me.make_client_station();
880        me.on_sme_scan(scan_req()).await;
881        let scan_end = m
882            .fake_device_state
883            .lock()
884            .next_mlme_msg::<fidl_mlme::ScanEnd>()
885            .expect("error reading MLME ScanEnd");
886        assert_eq!(
887            scan_end,
888            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::NotSupported }
889        );
890    }
891
892    #[fuchsia::test(allow_stalls = false)]
893    async fn mlme_respond_to_query_device_info() {
894        let mut mock_objects = MockObjects::new().await;
895        let mut mlme = mock_objects.make_mlme().await;
896
897        let (responder, receiver) = Responder::new();
898        mlme.handle_mlme_request(wlan_sme::MlmeRequest::QueryDeviceInfo(responder))
899            .await
900            .expect("Failed to send MlmeRequest::Connect");
901        assert_eq!(
902            receiver.await.unwrap(),
903            fidl_mlme::DeviceInfo {
904                sta_addr: IFACE_MAC.to_array(),
905                factory_addr: IFACE_MAC.to_array(),
906                role: fidl_common::WlanMacRole::Client,
907                bands: test_utils::fake_mlme_band_caps(),
908                softmac_hardware_capability: 0,
909                qos_capable: false,
910            }
911        );
912    }
913
914    #[fuchsia::test(allow_stalls = false)]
915    async fn mlme_respond_to_query_mac_sublayer_support() {
916        let mut m = MockObjects::new().await;
917        let mut me = m.make_mlme().await;
918
919        let (responder, receiver) = Responder::new();
920        me.handle_mlme_request(wlan_sme::MlmeRequest::QueryMacSublayerSupport(responder))
921            .await
922            .expect("Failed to send MlmeRequest::Connect");
923        let resp = receiver.await.unwrap();
924        assert_eq!(resp.rate_selection_offload.unwrap().supported, Some(false));
925        assert_eq!(
926            resp.data_plane.unwrap().data_plane_type,
927            Some(fidl_common::DataPlaneType::EthernetDevice)
928        );
929        assert_eq!(resp.device.as_ref().unwrap().is_synthetic, Some(true));
930        assert_eq!(
931            resp.device.as_ref().unwrap().mac_implementation_type,
932            Some(fidl_common::MacImplementationType::Softmac)
933        );
934        assert_eq!(resp.device.unwrap().tx_status_report_supported, Some(true));
935    }
936
937    #[fuchsia::test(allow_stalls = false)]
938    async fn mlme_respond_to_query_security_support() {
939        let mut m = MockObjects::new().await;
940        let mut me = m.make_mlme().await;
941
942        let (responder, receiver) = Responder::new();
943        assert_matches!(
944            me.handle_mlme_request(wlan_sme::MlmeRequest::QuerySecuritySupport(responder)).await,
945            Ok(())
946        );
947        let resp = receiver.await.unwrap();
948        assert_eq!(resp.mfp.unwrap().supported, Some(false));
949        assert_eq!(resp.sae.as_ref().unwrap().driver_handler_supported, Some(false));
950        assert_eq!(resp.sae.unwrap().sme_handler_supported, Some(false));
951    }
952
953    #[fuchsia::test(allow_stalls = false)]
954    async fn mlme_respond_to_query_spectrum_management_support() {
955        let mut m = MockObjects::new().await;
956        let mut me = m.make_mlme().await;
957
958        let (responder, receiver) = Responder::new();
959        me.handle_mlme_request(wlan_sme::MlmeRequest::QuerySpectrumManagementSupport(responder))
960            .await
961            .expect("Failed to send MlmeRequest::QuerySpectrumManagementSupport");
962        assert_eq!(receiver.await.unwrap().dfs.unwrap().supported, Some(true));
963    }
964
965    #[fuchsia::test(allow_stalls = false)]
966    async fn mlme_connect_unprotected_happy_path() {
967        let mut m = MockObjects::new().await;
968        let mut me = m.make_mlme().await;
969        let channel = Channel::new(6, Cbw::Cbw40);
970        let connect_req = fidl_mlme::ConnectRequest {
971            selected_bss: fake_fidl_bss_description!(Open,
972                ssid: Ssid::try_from("ssid").unwrap().into(),
973                bssid: BSSID.to_array(),
974                channel: channel.clone(),
975            ),
976            connect_failure_timeout: 100,
977            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
978            sae_password: vec![],
979            wep_key: None,
980            security_ie: vec![],
981            owe_public_key: None,
982        };
983        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
984            .await
985            .expect("Failed to send MlmeRequest::Connect");
986
987        // Verify an event was queued up in the timer.
988        assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
989            assert_eq!(ids.len(), 1);
990        });
991
992        // Verify authentication frame was sent to AP.
993        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
994        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
995        #[rustfmt::skip]
996        let expected = vec![
997            // Mgmt Header:
998            0b1011_00_00, 0b00000000, // Frame Control
999            0, 0, // Duration
1000            6, 6, 6, 6, 6, 6, // Addr1
1001            7, 7, 7, 7, 7, 7, // Addr2
1002            6, 6, 6, 6, 6, 6, // Addr3
1003            0x10, 0, // Sequence Control
1004            // Auth Header:
1005            0, 0, // Algorithm Number (Open)
1006            1, 0, // Txn Sequence Number
1007            0, 0, // Status Code
1008        ];
1009        assert_eq!(&frame[..], &expected[..]);
1010
1011        // Mock auth frame response from the AP
1012        #[rustfmt::skip]
1013        let auth_resp_success = vec![
1014            // Mgmt Header:
1015            0b1011_00_00, 0b00000000, // Frame Control
1016            0, 0, // Duration
1017            7, 7, 7, 7, 7, 7, // Addr1
1018            7, 7, 7, 7, 7, 7, // Addr2
1019            6, 6, 6, 6, 6, 6, // Addr3
1020            0x10, 0, // Sequence Control
1021            // Auth Header:
1022            0, 0, // Algorithm Number (Open)
1023            2, 0, // Txn Sequence Number
1024            0, 0, // Status Code
1025        ];
1026        me.handle_mac_frame_rx(
1027            &auth_resp_success[..],
1028            MockWlanRxInfo::with_channel(channel.into()).into(),
1029            0.into(),
1030        )
1031        .await;
1032
1033        // Verify association request frame was went to AP
1034        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1035        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1036        #[rustfmt::skip]
1037        let expected = vec![
1038            // Mgmt header:
1039            0, 0, // FC
1040            0, 0, // Duration
1041            6, 6, 6, 6, 6, 6, // addr1
1042            7, 7, 7, 7, 7, 7, // addr2
1043            6, 6, 6, 6, 6, 6, // addr3
1044            0x20, 0, // Sequence Control
1045            // Association Request header:
1046            0x01, 0x00, // capability info
1047            0, 0, // listen interval
1048            // IEs
1049            0, 4, // SSID id and length
1050            0x73, 0x73, 0x69, 0x64, // SSID
1051            1, 8, // supp rates id and length
1052            2, 4, 11, 22, 12, 18, 24, 36, // supp rates
1053            50, 4, // ext supp rates and length
1054            48, 72, 96, 108, // ext supp rates
1055            45, 26, // HT Cap id and length
1056            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
1057            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
1058            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
1059        ];
1060        assert_eq!(&frame[..], &expected[..]);
1061
1062        // Mock assoc resp frame from the AP
1063        #[rustfmt::skip]
1064        let assoc_resp_success = vec![
1065            // Mgmt Header:
1066            0b0001_00_00, 0b00000000, // Frame Control
1067            0, 0, // Duration
1068            7, 7, 7, 7, 7, 7, // Addr1 == IFACE_MAC
1069            7, 7, 7, 7, 7, 7, // Addr2
1070            6, 6, 6, 6, 6, 6, // Addr3
1071            0x20, 0, // Sequence Control
1072            // Assoc Resp Header:
1073            0, 0, // Capabilities
1074            0, 0, // Status Code
1075            42, 0, // AID
1076            // IEs
1077            // Basic Rates
1078            0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
1079            // HT Capabilities
1080            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1081            0x17, // A-MPDU parameters
1082            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1083            // VHT Capabilities
1084            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1085            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1086        ];
1087        me.handle_mac_frame_rx(
1088            &assoc_resp_success[..],
1089            MockWlanRxInfo::with_channel(channel.into()).into(),
1090            0.into(),
1091        )
1092        .await;
1093
1094        // Verify a successful connect conf is sent
1095        let msg = m
1096            .fake_device_state
1097            .lock()
1098            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1099            .expect("expect ConnectConf");
1100        assert_eq!(
1101            msg,
1102            fidl_mlme::ConnectConfirm {
1103                peer_sta_address: BSSID.to_array(),
1104                result_code: fidl_ieee80211::StatusCode::Success,
1105                association_id: 42,
1106                association_ies: vec![
1107                    // IEs
1108                    // Basic Rates
1109                    0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
1110                    // HT Capabilities
1111                    0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1112                    0x17, // A-MPDU parameters
1113                    0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1114                    0x00, 0x00, // VHT Capabilities
1115                    0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1116                    0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1117                ],
1118            }
1119        );
1120
1121        // Verify eth link is up
1122        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::UP);
1123    }
1124
1125    #[fuchsia::test(allow_stalls = false)]
1126    async fn mlme_connect_protected_happy_path() {
1127        let mut m = MockObjects::new().await;
1128        let mut me = m.make_mlme().await;
1129        let channel = Channel::new(6, Cbw::Cbw40);
1130        let connect_req = fidl_mlme::ConnectRequest {
1131            selected_bss: fake_fidl_bss_description!(Wpa2,
1132                ssid: Ssid::try_from("ssid").unwrap().into(),
1133                bssid: BSSID.to_array(),
1134                channel: channel.clone(),
1135            ),
1136            connect_failure_timeout: 100,
1137            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1138            sae_password: vec![],
1139            wep_key: None,
1140            security_ie: vec![
1141                48, 18, // RSNE header
1142                1, 0, // Version
1143                0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
1144                1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
1145                1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
1146            ],
1147            owe_public_key: None,
1148        };
1149        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
1150            .await
1151            .expect("Failed to send MlmeRequest::Connect");
1152
1153        // Verify an event was queued up in the timer.
1154        assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
1155            assert_eq!(ids.len(), 1);
1156        });
1157
1158        // Verify authentication frame was sent to AP.
1159        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1160        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1161        #[rustfmt::skip]
1162        let expected = vec![
1163            // Mgmt Header:
1164            0b1011_00_00, 0b00000000, // Frame Control
1165            0, 0, // Duration
1166            6, 6, 6, 6, 6, 6, // Addr1
1167            7, 7, 7, 7, 7, 7, // Addr2
1168            6, 6, 6, 6, 6, 6, // Addr3
1169            0x10, 0, // Sequence Control
1170            // Auth Header:
1171            0, 0, // Algorithm Number (Open)
1172            1, 0, // Txn Sequence Number
1173            0, 0, // Status Code
1174        ];
1175        assert_eq!(&frame[..], &expected[..]);
1176
1177        // Mock auth frame response from the AP
1178        #[rustfmt::skip]
1179        let auth_resp_success = vec![
1180            // Mgmt Header:
1181            0b1011_00_00, 0b00000000, // Frame Control
1182            0, 0, // Duration
1183            7, 7, 7, 7, 7, 7, // Addr1
1184            7, 7, 7, 7, 7, 7, // Addr2
1185            6, 6, 6, 6, 6, 6, // Addr3
1186            0x10, 0, // Sequence Control
1187            // Auth Header:
1188            0, 0, // Algorithm Number (Open)
1189            2, 0, // Txn Sequence Number
1190            0, 0, // Status Code
1191        ];
1192        me.handle_mac_frame_rx(
1193            &auth_resp_success[..],
1194            MockWlanRxInfo::with_channel(channel.into()).into(),
1195            0.into(),
1196        )
1197        .await;
1198
1199        // Verify association request frame was went to AP
1200        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1201        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1202        #[rustfmt::skip]
1203        let expected = vec![
1204            // Mgmt header:
1205            0, 0, // FC
1206            0, 0, // Duration
1207            6, 6, 6, 6, 6, 6, // addr1
1208            7, 7, 7, 7, 7, 7, // addr2
1209            6, 6, 6, 6, 6, 6, // addr3
1210            0x20, 0, // Sequence Control
1211            // Association Request header:
1212            0x01, 0x00, // capability info
1213            0, 0, // listen interval
1214            // IEs
1215            0, 4, // SSID id and length
1216            0x73, 0x73, 0x69, 0x64, // SSID
1217            1, 8, // supp rates id and length
1218            2, 4, 11, 22, 12, 18, 24, 36, // supp rates
1219            50, 4, // ext supp rates and length
1220            48, 72, 96, 108, // ext supp rates
1221            48, 18, // RSNE id and length
1222            1, 0, // RSN \
1223            0x00, 0x0F, 0xAC, 4, // RSN \
1224            1, 0, 0x00, 0x0F, 0xAC, 4, // RSN \
1225            1, 0, 0x00, 0x0F, 0xAC, 2, // RSN
1226            45, 26, // HT Cap id and length
1227            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
1228            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
1229            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
1230        ];
1231        assert_eq!(&frame[..], &expected[..]);
1232
1233        // Mock assoc resp frame from the AP
1234        #[rustfmt::skip]
1235        let assoc_resp_success = vec![
1236            // Mgmt Header:
1237            0b0001_00_00, 0b00000000, // Frame Control
1238            0, 0, // Duration
1239            7, 7, 7, 7, 7, 7, // Addr1 == IFACE_MAC
1240            7, 7, 7, 7, 7, 7, // Addr2
1241            6, 6, 6, 6, 6, 6, // Addr3
1242            0x20, 0, // Sequence Control
1243            // Assoc Resp Header:
1244            0, 0, // Capabilities
1245            0, 0, // Status Code
1246            42, 0, // AID
1247            // IEs
1248            // Basic Rates
1249            0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
1250            // RSN
1251            0x30, 18, 1, 0, // RSN header and version
1252            0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
1253            1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
1254            1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
1255            // HT Capabilities
1256            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1257            0x17, // A-MPDU parameters
1258            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Other HT Cap fields
1259            // VHT Capabilities
1260            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1261            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1262        ];
1263        me.handle_mac_frame_rx(
1264            &assoc_resp_success[..],
1265            MockWlanRxInfo::with_channel(channel.into()).into(),
1266            0.into(),
1267        )
1268        .await;
1269
1270        // Verify a successful connect conf is sent
1271        let msg = m
1272            .fake_device_state
1273            .lock()
1274            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1275            .expect("expect ConnectConf");
1276        assert_eq!(
1277            msg,
1278            fidl_mlme::ConnectConfirm {
1279                peer_sta_address: BSSID.to_array(),
1280                result_code: fidl_ieee80211::StatusCode::Success,
1281                association_id: 42,
1282                association_ies: vec![
1283                    // IEs
1284                    // Basic Rates
1285                    0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, // RSN
1286                    0x30, 18, 1, 0, // RSN header and version
1287                    0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
1288                    1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
1289                    1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
1290                    // HT Capabilities
1291                    0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
1292                    0x17, // A-MPDU parameters
1293                    0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1294                    0x00, 0x00, // Other HT Cap fields
1295                    // VHT Capabilities
1296                    0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
1297                    0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
1298                ],
1299            }
1300        );
1301
1302        // Verify that link is still down
1303        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::DOWN);
1304
1305        // Send a request to open controlled port
1306        me.handle_mlme_request(wlan_sme::MlmeRequest::SetCtrlPort(
1307            fidl_mlme::SetControlledPortRequest {
1308                peer_sta_address: BSSID.to_array(),
1309                state: fidl_mlme::ControlledPortState::Open,
1310            },
1311        ))
1312        .await
1313        .expect("expect sending msg to succeed");
1314
1315        // Verify that link is now up
1316        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::UP);
1317    }
1318
1319    #[fuchsia::test(allow_stalls = false)]
1320    async fn mlme_connect_vht() {
1321        let mut m = MockObjects::new().await;
1322        let mut me = m.make_mlme().await;
1323        let channel = Channel::new(36, Cbw::Cbw40);
1324        let connect_req = fidl_mlme::ConnectRequest {
1325            selected_bss: fake_fidl_bss_description!(Open,
1326                ssid: Ssid::try_from("ssid").unwrap().into(),
1327                bssid: BSSID.to_array(),
1328                channel: channel.clone(),
1329            ),
1330            connect_failure_timeout: 100,
1331            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1332            sae_password: vec![],
1333            wep_key: None,
1334            security_ie: vec![],
1335            owe_public_key: None,
1336        };
1337        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
1338            .await
1339            .expect("Failed to send MlmeRequest::Connect.");
1340
1341        // Verify an event was queued up in the timer.
1342        assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
1343            assert_eq!(ids.len(), 1);
1344        });
1345
1346        // Auth frame
1347        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1348        let (_frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1349
1350        // Mock auth frame response from the AP
1351        #[rustfmt::skip]
1352        let auth_resp_success = vec![
1353            // Mgmt Header:
1354            0b1011_00_00, 0b00000000, // Frame Control
1355            0, 0, // Duration
1356            7, 7, 7, 7, 7, 7, // Addr1
1357            7, 7, 7, 7, 7, 7, // Addr2
1358            6, 6, 6, 6, 6, 6, // Addr3
1359            0x10, 0, // Sequence Control
1360            // Auth Header:
1361            0, 0, // Algorithm Number (Open)
1362            2, 0, // Txn Sequence Number
1363            0, 0, // Status Code
1364        ];
1365        me.handle_mac_frame_rx(
1366            &auth_resp_success[..],
1367            MockWlanRxInfo::with_channel(channel.into()).into(),
1368            0.into(),
1369        )
1370        .await;
1371
1372        // Verify association request frame was went to AP
1373        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1374        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1375        #[rustfmt::skip]
1376        let expected = vec![
1377            // Mgmt header:
1378            0, 0, // FC
1379            0, 0, // Duration
1380            6, 6, 6, 6, 6, 6, // addr1
1381            7, 7, 7, 7, 7, 7, // addr2
1382            6, 6, 6, 6, 6, 6, // addr3
1383            0x20, 0, // Sequence Control
1384            // Association Request header:
1385            0x01, 0x00, // capability info
1386            0, 0, // listen interval
1387            // IEs
1388            0, 4, // SSID id and length
1389            0x73, 0x73, 0x69, 0x64, // SSID
1390            1, 6, // supp rates id and length
1391            2, 4, 11, 22, 48, 96, // supp rates
1392            45, 26, // HT Cap id and length
1393            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
1394            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
1395            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
1396            191, 12, // VHT Cap id and length
1397            50, 80, 128, 15, 254, 255, 0, 0, 254, 255, 0, 0, // VHT Cap
1398        ];
1399        assert_eq!(&frame[..], &expected[..]);
1400    }
1401
1402    #[fuchsia::test(allow_stalls = false)]
1403    async fn mlme_connect_timeout() {
1404        let mut m = MockObjects::new().await;
1405        let mut me = m.make_mlme().await;
1406        let connect_req = fidl_mlme::ConnectRequest {
1407            selected_bss: fake_fidl_bss_description!(Open, bssid: BSSID.to_array()),
1408            connect_failure_timeout: 100,
1409            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1410            sae_password: vec![],
1411            wep_key: None,
1412            security_ie: vec![],
1413            owe_public_key: None,
1414        };
1415        me.handle_mlme_request(wlan_sme::MlmeRequest::Connect(connect_req))
1416            .await
1417            .expect("Failed to send MlmeRequest::Connect.");
1418
1419        // Verify an event was queued up in the timer.
1420        let (event, _id) = assert_matches!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(events) => {
1421            assert_eq!(events.len(), 1);
1422            events[0].clone()
1423        });
1424
1425        // Quick check that a frame was sent (this is authentication frame).
1426        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1427        let (_frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
1428
1429        // Send connect timeout
1430        me.handle_timeout(event).await;
1431
1432        // Verify a connect confirm message was sent
1433        let msg = m
1434            .fake_device_state
1435            .lock()
1436            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1437            .expect("expect msg");
1438        assert_eq!(
1439            msg,
1440            fidl_mlme::ConnectConfirm {
1441                peer_sta_address: BSSID.to_array(),
1442                result_code: fidl_ieee80211::StatusCode::RejectedSequenceTimeout,
1443                association_id: 0,
1444                association_ies: vec![],
1445            },
1446        );
1447    }
1448
1449    #[fuchsia::test(allow_stalls = false)]
1450    async fn mlme_reconnect_no_sta() {
1451        let mut m = MockObjects::new().await;
1452        let mut me = m.make_mlme().await;
1453
1454        let reconnect_req = fidl_mlme::ReconnectRequest { peer_sta_address: [1, 2, 3, 4, 5, 6] };
1455        let result = me.handle_mlme_request(wlan_sme::MlmeRequest::Reconnect(reconnect_req)).await;
1456        let err = result.unwrap_err();
1457        let mlme_err = err.downcast_ref::<Error>().expect("expected Mlme Error");
1458        assert_matches!(mlme_err, Error::Status(_, zx::Status::BAD_STATE));
1459
1460        // Verify a connect confirm message was sent
1461        let msg = m
1462            .fake_device_state
1463            .lock()
1464            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
1465            .expect("expect msg");
1466        assert_eq!(
1467            msg,
1468            fidl_mlme::ConnectConfirm {
1469                peer_sta_address: [1, 2, 3, 4, 5, 6],
1470                result_code: fidl_ieee80211::StatusCode::DeniedNoAssociationExists,
1471                association_id: 0,
1472                association_ies: vec![],
1473            },
1474        );
1475    }
1476
1477    #[fuchsia::test(allow_stalls = false)]
1478    async fn mlme_respond_to_get_iface_stats_with_error_status() {
1479        let mut m = MockObjects::new().await;
1480        let mut me = m.make_mlme().await;
1481
1482        let (responder, receiver) = Responder::new();
1483        me.handle_mlme_request(wlan_sme::MlmeRequest::GetIfaceStats(responder))
1484            .await
1485            .expect("Failed to send MlmeRequest::GetIfaceStats.");
1486        assert_eq!(
1487            receiver.await,
1488            Ok(fidl_mlme::GetIfaceStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED))
1489        );
1490    }
1491
1492    #[fuchsia::test(allow_stalls = false)]
1493    async fn mlme_respond_to_get_iface_histogram_stats_with_error_status() {
1494        let mut m = MockObjects::new().await;
1495        let mut me = m.make_mlme().await;
1496
1497        let (responder, receiver) = Responder::new();
1498        me.handle_mlme_request(wlan_sme::MlmeRequest::GetIfaceHistogramStats(responder))
1499            .await
1500            .expect("Failed to send MlmeRequest::GetIfaceHistogramStats");
1501        assert_eq!(
1502            receiver.await,
1503            Ok(fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(
1504                zx::sys::ZX_ERR_NOT_SUPPORTED
1505            ))
1506        );
1507    }
1508
1509    #[test]
1510    fn drop_mgmt_frame_wrong_bssid() {
1511        let frame = [
1512            // Mgmt header 1101 for action frame
1513            0b11010000, 0b00000000, // frame control
1514            0, 0, // duration
1515            7, 7, 7, 7, 7, 7, // addr1
1516            6, 6, 6, 6, 6, 6, // addr2
1517            0, 0, 0, 0, 0, 0, // addr3 (bssid should have been [6; 6])
1518            0x10, 0, // sequence control
1519        ];
1520        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1521        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1522    }
1523
1524    #[test]
1525    fn drop_mgmt_frame_wrong_dst_addr() {
1526        let frame = [
1527            // Mgmt header 1101 for action frame
1528            0b11010000, 0b00000000, // frame control
1529            0, 0, // duration
1530            0, 0, 0, 0, 0, 0, // addr1 (dst_addr should have been [7; 6])
1531            6, 6, 6, 6, 6, 6, // addr2
1532            6, 6, 6, 6, 6, 6, // addr3
1533            0x10, 0, // sequence control
1534        ];
1535        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1536        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1537    }
1538
1539    #[test]
1540    fn mgmt_frame_ok_broadcast() {
1541        let frame = [
1542            // Mgmt header 1101 for action frame
1543            0b11010000, 0b00000000, // frame control
1544            0, 0, // duration
1545            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // addr1 (dst_addr is broadcast)
1546            6, 6, 6, 6, 6, 6, // addr2
1547            6, 6, 6, 6, 6, 6, // addr3
1548            0x10, 0, // sequence control
1549        ];
1550        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1551        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1552    }
1553
1554    #[test]
1555    fn mgmt_frame_ok_client_addr() {
1556        let frame = [
1557            // Mgmt header 1101 for action frame
1558            0b11010000, 0b00000000, // frame control
1559            0, 0, // duration
1560            7, 7, 7, 7, 7, 7, // addr1 (dst_addr should have been [7; 6])
1561            6, 6, 6, 6, 6, 6, // addr2
1562            6, 6, 6, 6, 6, 6, // addr3
1563            0x10, 0, // sequence control
1564        ];
1565        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1566        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1567    }
1568
1569    #[test]
1570    fn drop_data_frame_wrong_bssid() {
1571        let frame = [
1572            // Data header 0100
1573            0b01001000,
1574            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1575            0, 0, // duration
1576            7, 7, 7, 7, 7, 7, // addr1 (dst_addr)
1577            0, 0, 0, 0, 0, 0, // addr2 (bssid should have been [6; 6])
1578            6, 6, 6, 6, 6, 6, // addr3
1579            0x10, 0, // sequence control
1580        ];
1581        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1582        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1583    }
1584
1585    #[test]
1586    fn drop_data_frame_wrong_dst_addr() {
1587        let frame = [
1588            // Data header 0100
1589            0b01001000,
1590            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1591            0, 0, // duration
1592            0, 0, 0, 0, 0, 0, // addr1 (dst_addr should have been [7; 6])
1593            6, 6, 6, 6, 6, 6, // addr2 (bssid)
1594            6, 6, 6, 6, 6, 6, // addr3
1595            0x10, 0, // sequence control
1596        ];
1597        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1598        assert_eq!(false, make_client_station().should_handle_frame(&frame));
1599    }
1600
1601    #[test]
1602    fn data_frame_ok_broadcast() {
1603        let frame = [
1604            // Data header 0100
1605            0b01001000,
1606            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1607            0, 0, // duration
1608            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // addr1 (dst_addr is broadcast)
1609            6, 6, 6, 6, 6, 6, // addr2 (bssid)
1610            6, 6, 6, 6, 6, 6, // addr3
1611            0x10, 0, // sequence control
1612        ];
1613        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1614        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1615    }
1616
1617    #[test]
1618    fn data_frame_ok_client_addr() {
1619        let frame = [
1620            // Data header 0100
1621            0b01001000,
1622            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
1623            0, 0, // duration
1624            7, 7, 7, 7, 7, 7, // addr1 (dst_addr)
1625            6, 6, 6, 6, 6, 6, // addr2 (bssid)
1626            6, 6, 6, 6, 6, 6, // addr3
1627            0x10, 0, // sequence control
1628        ];
1629        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
1630        assert_eq!(true, make_client_station().should_handle_frame(&frame));
1631    }
1632}