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