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