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 channel_switch;
6mod convert_beacon;
7mod lost_bss;
8mod scanner;
9mod state;
10#[cfg(test)]
11mod test_utils;
12
13use crate::block_ack::BlockAckTx;
14use crate::device::{self, DeviceOps};
15use crate::disconnect::LocallyInitiated;
16use crate::error::Error;
17use crate::{akm_algorithm, ddk_converter};
18use anyhow::format_err;
19use channel_switch::ChannelState;
20use fdf::{Arena, ArenaBox, ArenaStaticBox};
21use ieee80211::{Bssid, MacAddr, MacAddrBytes, Ssid};
22use log::{error, warn};
23use scanner::Scanner;
24use state::States;
25use std::mem;
26use std::ptr::NonNull;
27use wlan_common::append::Append;
28use wlan_common::bss::BssDescription;
29use wlan_common::buffer_writer::BufferWriter;
30use wlan_common::capabilities::{derive_join_capabilities, ClientCapabilities};
31use wlan_common::channel::Channel;
32use wlan_common::ie::rsn::rsne;
33use wlan_common::ie::{self, Id};
34use wlan_common::mac::{self, Aid, CapabilityInfo};
35use wlan_common::sequence::SequenceManager;
36use wlan_common::time::TimeUnit;
37use wlan_common::timer::{EventHandle, Timer};
38use wlan_common::{data_writer, mgmt_writer, wmm};
39use wlan_frame_writer::{append_frame_to, write_frame, write_frame_with_fixed_slice};
40use zerocopy::SplitByteSlice;
41use {
42    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
43    fidl_fuchsia_wlan_minstrel as fidl_minstrel, fidl_fuchsia_wlan_mlme as fidl_mlme,
44    fidl_fuchsia_wlan_softmac as fidl_softmac, fuchsia_trace as trace, wlan_trace as wtrace,
45};
46
47pub use scanner::ScanError;
48
49#[derive(Debug, Clone, PartialEq)]
50pub enum TimedEvent {
51    /// Connecting to AP timed out.
52    Connecting,
53    /// Timeout for reassociating after a disassociation.
54    Reassociating,
55    /// Association status update includes checking for auto deauthentication due to beacon loss
56    /// and report signal strength
57    AssociationStatusCheck,
58    /// The delay for a scheduled channel switch has elapsed.
59    ChannelSwitch,
60}
61
62#[cfg(test)]
63impl TimedEvent {
64    fn class(&self) -> TimedEventClass {
65        match self {
66            Self::Connecting => TimedEventClass::Connecting,
67            Self::Reassociating => TimedEventClass::Reassociating,
68            Self::AssociationStatusCheck => TimedEventClass::AssociationStatusCheck,
69            Self::ChannelSwitch => TimedEventClass::ChannelSwitch,
70        }
71    }
72}
73
74#[cfg(test)]
75#[derive(Debug, PartialEq, Eq, Hash)]
76pub enum TimedEventClass {
77    Connecting,
78    Reassociating,
79    AssociationStatusCheck,
80    ChannelSwitch,
81}
82
83/// ClientConfig affects time duration used for different timeouts.
84/// Originally added to more easily control behavior in tests.
85#[repr(C)]
86#[derive(Debug, Clone, Default)]
87pub struct ClientConfig {
88    pub ensure_on_channel_time: zx::sys::zx_duration_t,
89}
90
91pub struct Context<D> {
92    _config: ClientConfig,
93    device: D,
94    timer: Timer<TimedEvent>,
95    seq_mgr: SequenceManager,
96}
97
98pub struct ClientMlme<D> {
99    sta: Option<Client>,
100    ctx: Context<D>,
101    scanner: Scanner,
102    channel_state: ChannelState,
103}
104
105impl<D: DeviceOps> crate::MlmeImpl for ClientMlme<D> {
106    type Config = ClientConfig;
107    type Device = D;
108    type TimerEvent = TimedEvent;
109    async fn new(
110        config: Self::Config,
111        device: Self::Device,
112        timer: Timer<TimedEvent>,
113    ) -> Result<Self, anyhow::Error> {
114        Self::new(config, device, timer).await.map_err(From::from)
115    }
116    async fn handle_mlme_request(
117        &mut self,
118        req: wlan_sme::MlmeRequest,
119    ) -> Result<(), anyhow::Error> {
120        Self::handle_mlme_req(self, req).await.map_err(From::from)
121    }
122    async fn handle_mac_frame_rx(
123        &mut self,
124        bytes: &[u8],
125        rx_info: fidl_softmac::WlanRxInfo,
126        async_id: trace::Id,
127    ) {
128        wtrace::duration!(c"ClientMlme::handle_mac_frame_rx");
129        Self::on_mac_frame_rx(self, bytes, rx_info, async_id).await
130    }
131    fn handle_eth_frame_tx(
132        &mut self,
133        bytes: &[u8],
134        async_id: trace::Id,
135    ) -> Result<(), anyhow::Error> {
136        wtrace::duration!(c"ClientMlme::handle_eth_frame_tx");
137        Self::on_eth_frame_tx(self, bytes, async_id).map_err(From::from)
138    }
139    async fn handle_scan_complete(&mut self, status: zx::Status, scan_id: u64) {
140        Self::handle_scan_complete(self, status, scan_id).await;
141    }
142    async fn handle_timeout(&mut self, event: TimedEvent) {
143        Self::handle_timed_event(self, event).await
144    }
145    fn access_device(&mut self) -> &mut Self::Device {
146        &mut self.ctx.device
147    }
148}
149
150impl<D> ClientMlme<D> {
151    pub fn seq_mgr(&mut self) -> &mut SequenceManager {
152        &mut self.ctx.seq_mgr
153    }
154
155    fn on_sme_get_iface_stats(
156        &self,
157        responder: wlan_sme::responder::Responder<fidl_mlme::GetIfaceStatsResponse>,
158    ) -> Result<(), Error> {
159        // TODO(https://fxbug.dev/42119762): Implement stats
160        let resp = fidl_mlme::GetIfaceStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED);
161        responder.respond(resp);
162        Ok(())
163    }
164
165    fn on_sme_get_iface_histogram_stats(
166        &self,
167        responder: wlan_sme::responder::Responder<fidl_mlme::GetIfaceHistogramStatsResponse>,
168    ) -> Result<(), Error> {
169        // TODO(https://fxbug.dev/42119762): Implement stats
170        let resp =
171            fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED);
172        responder.respond(resp);
173        Ok(())
174    }
175
176    fn on_sme_list_minstrel_peers(
177        &self,
178        responder: wlan_sme::responder::Responder<fidl_mlme::MinstrelListResponse>,
179    ) -> Result<(), Error> {
180        // TODO(https://fxbug.dev/42159791): Implement once Minstrel is in Rust.
181        error!("ListMinstrelPeers is not supported.");
182        let peers = fidl_minstrel::Peers { addrs: vec![] };
183        let resp = fidl_mlme::MinstrelListResponse { peers };
184        responder.respond(resp);
185        Ok(())
186    }
187
188    fn on_sme_get_minstrel_stats(
189        &self,
190        responder: wlan_sme::responder::Responder<fidl_mlme::MinstrelStatsResponse>,
191        _addr: &MacAddr,
192    ) -> Result<(), Error> {
193        // TODO(https://fxbug.dev/42159791): Implement once Minstrel is in Rust.
194        error!("GetMinstrelStats is not supported.");
195        let resp = fidl_mlme::MinstrelStatsResponse { peer: None };
196        responder.respond(resp);
197        Ok(())
198    }
199}
200
201impl<D: DeviceOps> ClientMlme<D> {
202    pub async fn new(
203        config: ClientConfig,
204        mut device: D,
205        timer: Timer<TimedEvent>,
206    ) -> Result<Self, Error> {
207        let iface_mac = device::try_query_iface_mac(&mut device).await?;
208        Ok(Self {
209            sta: None,
210            ctx: Context { _config: config, device, timer, seq_mgr: SequenceManager::new() },
211            scanner: Scanner::new(iface_mac.into()),
212            channel_state: Default::default(),
213        })
214    }
215
216    pub async fn set_main_channel(
217        &mut self,
218        channel: fidl_common::WlanChannel,
219    ) -> Result<(), zx::Status> {
220        self.channel_state.bind(&mut self.ctx, &mut self.scanner).set_main_channel(channel).await
221    }
222
223    pub async fn on_mac_frame_rx(
224        &mut self,
225        frame: &[u8],
226        rx_info: fidl_softmac::WlanRxInfo,
227        async_id: trace::Id,
228    ) {
229        wtrace::duration!(c"ClientMlme::on_mac_frame_rx");
230        // TODO(https://fxbug.dev/42120906): Send the entire frame to scanner.
231        if let Some(mgmt_frame) = mac::MgmtFrame::parse(frame, false) {
232            let bssid = Bssid::from(mgmt_frame.mgmt_hdr.addr3);
233            match mgmt_frame.try_into_mgmt_body().1 {
234                Some(mac::MgmtBody::Beacon { bcn_hdr, elements }) => {
235                    wtrace::duration!(c"MgmtBody::Beacon");
236                    self.scanner.bind(&mut self.ctx).handle_ap_advertisement(
237                        bssid,
238                        bcn_hdr.beacon_interval,
239                        bcn_hdr.capabilities,
240                        elements,
241                        rx_info.clone(),
242                    );
243                }
244                Some(mac::MgmtBody::ProbeResp { probe_resp_hdr, elements }) => {
245                    wtrace::duration!(c"MgmtBody::ProbeResp");
246                    self.scanner.bind(&mut self.ctx).handle_ap_advertisement(
247                        bssid,
248                        probe_resp_hdr.beacon_interval,
249                        probe_resp_hdr.capabilities,
250                        elements,
251                        rx_info.clone(),
252                    )
253                }
254                _ => (),
255            }
256        }
257
258        if let Some(sta) = self.sta.as_mut() {
259            // Only pass the frame to a BoundClient under the following conditions:
260            //   - ChannelState currently has a main channel.
261            //   - ClientMlme received the frame on the main channel.
262            match self.channel_state.get_main_channel() {
263                Some(main_channel) if main_channel.primary == rx_info.channel.primary => {
264                    sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
265                        .on_mac_frame(frame, rx_info, async_id)
266                        .await
267                }
268                Some(_) => {
269                    wtrace::async_end_wlansoftmac_rx(async_id, "off main channel");
270                }
271                // TODO(https://fxbug.dev/42075118): This is only reachable because the Client state machine
272                // returns to the Joined state and clears the main channel upon deauthentication.
273                None => {
274                    error!(
275                        "Received MAC frame on channel {:?} while main channel is not set.",
276                        rx_info.channel
277                    );
278                    wtrace::async_end_wlansoftmac_rx(async_id, "main channel not set");
279                }
280            }
281        } else {
282            wtrace::async_end_wlansoftmac_rx(async_id, "no bound client");
283        }
284    }
285
286    pub async fn handle_mlme_req(&mut self, req: wlan_sme::MlmeRequest) -> Result<(), Error> {
287        use wlan_sme::MlmeRequest as Req;
288
289        match req {
290            // Handle non station specific MLME messages first (Join, Scan, etc.)
291            Req::Scan(req) => Ok(self.on_sme_scan(req).await),
292            Req::Connect(req) => self.on_sme_connect(req).await,
293            Req::GetIfaceStats(responder) => self.on_sme_get_iface_stats(responder),
294            Req::GetIfaceHistogramStats(responder) => {
295                self.on_sme_get_iface_histogram_stats(responder)
296            }
297            Req::QueryDeviceInfo(responder) => self.on_sme_query_device_info(responder).await,
298            Req::QueryDiscoverySupport(responder) => {
299                self.on_sme_query_discovery_support(responder).await
300            }
301            Req::QueryMacSublayerSupport(responder) => {
302                self.on_sme_query_mac_sublayer_support(responder).await
303            }
304            Req::QuerySecuritySupport(responder) => {
305                self.on_sme_query_security_support(responder).await
306            }
307            Req::QuerySpectrumManagementSupport(responder) => {
308                self.on_sme_query_spectrum_management_support(responder).await
309            }
310            Req::ListMinstrelPeers(responder) => self.on_sme_list_minstrel_peers(responder),
311            Req::GetMinstrelStats(req, responder) => {
312                self.on_sme_get_minstrel_stats(responder, &req.peer_addr.into())
313            }
314            other_message => match &mut self.sta {
315                None => {
316                    if let Req::Reconnect(req) = other_message {
317                        self.ctx.device.send_mlme_event(fidl_mlme::MlmeEvent::ConnectConf {
318                            resp: fidl_mlme::ConnectConfirm {
319                                peer_sta_address: req.peer_sta_address,
320                                result_code: fidl_ieee80211::StatusCode::DeniedNoAssociationExists,
321                                association_id: 0,
322                                association_ies: vec![],
323                            },
324                        })?;
325                    }
326                    Err(Error::Status(
327                        format!(
328                            "Failed to handle {} MLME request when this ClientMlme has no sta.",
329                            other_message.name()
330                        ),
331                        zx::Status::BAD_STATE,
332                    ))
333                }
334                Some(sta) => Ok(sta
335                    .bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
336                    .handle_mlme_req(other_message)
337                    .await),
338            },
339        }
340    }
341
342    async fn on_sme_scan(&mut self, req: fidl_mlme::ScanRequest) {
343        let txn_id = req.txn_id;
344        let _ = self.scanner.bind(&mut self.ctx).on_sme_scan(req).await.map_err(|e| {
345            error!("Scan failed in MLME: {:?}", e);
346            let code = match e {
347                Error::ScanError(scan_error) => scan_error.into(),
348                _ => fidl_mlme::ScanResultCode::InternalError,
349            };
350            self.ctx
351                .device
352                .send_mlme_event(fidl_mlme::MlmeEvent::OnScanEnd {
353                    end: fidl_mlme::ScanEnd { txn_id, code },
354                })
355                .unwrap_or_else(|e| error!("error sending MLME ScanEnd: {}", e));
356        });
357    }
358
359    pub async fn handle_scan_complete(&mut self, status: zx::Status, scan_id: u64) {
360        self.scanner.bind(&mut self.ctx).handle_scan_complete(status, scan_id).await;
361    }
362
363    async fn on_sme_connect(&mut self, req: fidl_mlme::ConnectRequest) -> Result<(), Error> {
364        // Cancel any ongoing scan so that it doesn't conflict with the connect request
365        // TODO(b/254290448): Use enable/disable scanning for better guarantees.
366        if let Err(e) = self.scanner.bind(&mut self.ctx).cancel_ongoing_scan().await {
367            warn!("Failed to cancel ongoing scan before connect: {}.", e);
368        }
369
370        let bssid = req.selected_bss.bssid;
371        let result = match req.selected_bss.try_into() {
372            Ok(bss) => {
373                let req = ParsedConnectRequest {
374                    selected_bss: bss,
375                    connect_failure_timeout: req.connect_failure_timeout,
376                    auth_type: req.auth_type,
377                    sae_password: req.sae_password,
378                    wep_key: req.wep_key.map(|k| *k),
379                    security_ie: req.security_ie,
380                };
381                self.join_device(&req.selected_bss).await.map(|cap| (req, cap))
382            }
383            Err(e) => Err(Error::Status(
384                format!("Error parsing BssDescription: {:?}", e),
385                zx::Status::IO_INVALID,
386            )),
387        };
388
389        match result {
390            Ok((req, client_capabilities)) => {
391                self.sta.replace(Client::new(
392                    req,
393                    device::try_query_iface_mac(&mut self.ctx.device).await?,
394                    client_capabilities,
395                ));
396                if let Some(sta) = &mut self.sta {
397                    sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
398                        .start_connecting()
399                        .await;
400                }
401                Ok(())
402            }
403            Err(e) => {
404                error!("Error setting up device for join: {}", e);
405                // TODO(https://fxbug.dev/42120718): Only one failure code defined in IEEE 802.11-2016 6.3.4.3
406                // Can we do better?
407                self.ctx.device.send_mlme_event(fidl_mlme::MlmeEvent::ConnectConf {
408                    resp: fidl_mlme::ConnectConfirm {
409                        peer_sta_address: bssid,
410                        result_code: fidl_ieee80211::StatusCode::JoinFailure,
411                        association_id: 0,
412                        association_ies: vec![],
413                    },
414                })?;
415                Err(e)
416            }
417        }
418    }
419
420    async fn join_device(&mut self, bss: &BssDescription) -> Result<ClientCapabilities, Error> {
421        let info = ddk_converter::mlme_device_info_from_softmac(
422            device::try_query(&mut self.ctx.device).await?,
423        )?;
424        let join_caps = derive_join_capabilities(Channel::from(bss.channel), bss.rates(), &info)
425            .map_err(|e| {
426                Error::Status(
427                    format!("Failed to derive join capabilities: {:?}", e),
428                    zx::Status::NOT_SUPPORTED,
429                )
430            })?;
431
432        self.set_main_channel(bss.channel.into())
433            .await
434            .map_err(|status| Error::Status(format!("Error setting device channel"), status))?;
435
436        let join_bss_request = fidl_common::JoinBssRequest {
437            bssid: Some(bss.bssid.to_array()),
438            bss_type: Some(fidl_common::BssType::Infrastructure),
439            remote: Some(true),
440            beacon_period: Some(bss.beacon_period),
441            ..Default::default()
442        };
443
444        // Configure driver to pass frames from this BSS to MLME. Otherwise they will be dropped.
445        self.ctx
446            .device
447            .join_bss(&join_bss_request)
448            .await
449            .map(|()| join_caps)
450            .map_err(|status| Error::Status(format!("Error setting BSS in driver"), status))
451    }
452
453    async fn on_sme_query_device_info(
454        &mut self,
455        responder: wlan_sme::responder::Responder<fidl_mlme::DeviceInfo>,
456    ) -> Result<(), Error> {
457        let info = ddk_converter::mlme_device_info_from_softmac(
458            device::try_query(&mut self.ctx.device).await?,
459        )?;
460        responder.respond(info);
461        Ok(())
462    }
463
464    async fn on_sme_query_discovery_support(
465        &mut self,
466        responder: wlan_sme::responder::Responder<fidl_common::DiscoverySupport>,
467    ) -> Result<(), Error> {
468        let support = device::try_query_discovery_support(&mut self.ctx.device).await?;
469        responder.respond(support);
470        Ok(())
471    }
472
473    async fn on_sme_query_mac_sublayer_support(
474        &mut self,
475        responder: wlan_sme::responder::Responder<fidl_common::MacSublayerSupport>,
476    ) -> Result<(), Error> {
477        let support = device::try_query_mac_sublayer_support(&mut self.ctx.device).await?;
478        responder.respond(support);
479        Ok(())
480    }
481
482    async fn on_sme_query_security_support(
483        &mut self,
484        responder: wlan_sme::responder::Responder<fidl_common::SecuritySupport>,
485    ) -> Result<(), Error> {
486        let support = device::try_query_security_support(&mut self.ctx.device).await?;
487        responder.respond(support);
488        Ok(())
489    }
490
491    async fn on_sme_query_spectrum_management_support(
492        &mut self,
493        responder: wlan_sme::responder::Responder<fidl_common::SpectrumManagementSupport>,
494    ) -> Result<(), Error> {
495        let support = device::try_query_spectrum_management_support(&mut self.ctx.device).await?;
496        responder.respond(support);
497        Ok(())
498    }
499
500    pub fn on_eth_frame_tx<B: SplitByteSlice>(
501        &mut self,
502        bytes: B,
503        async_id: trace::Id,
504    ) -> Result<(), Error> {
505        wtrace::duration!(c"ClientMlme::on_eth_frame_tx");
506        match self.sta.as_mut() {
507            None => Err(Error::Status(
508                format!("Ethernet frame dropped (Client does not exist)."),
509                zx::Status::BAD_STATE,
510            )),
511            Some(sta) => sta
512                .bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
513                .on_eth_frame_tx(bytes, async_id),
514        }
515    }
516
517    /// Called when a previously scheduled `TimedEvent` fired.
518    /// Return true if auto-deauth has triggered. Return false otherwise.
519    pub async fn handle_timed_event(&mut self, event: TimedEvent) {
520        if let Some(sta) = self.sta.as_mut() {
521            return sta
522                .bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state)
523                .handle_timed_event(event)
524                .await;
525        }
526    }
527}
528
529/// A STA running in Client mode.
530/// The Client STA is in its early development process and does not yet manage its internal state
531/// machine or track negotiated capabilities.
532pub struct Client {
533    state: Option<States>,
534    pub connect_req: ParsedConnectRequest,
535    pub iface_mac: MacAddr,
536    pub client_capabilities: ClientCapabilities,
537    pub connect_timeout: Option<EventHandle>,
538}
539
540impl Client {
541    pub fn new(
542        connect_req: ParsedConnectRequest,
543        iface_mac: MacAddr,
544        client_capabilities: ClientCapabilities,
545    ) -> Self {
546        Self {
547            state: Some(States::new_initial()),
548            connect_req,
549            iface_mac,
550            client_capabilities,
551            connect_timeout: None,
552        }
553    }
554
555    pub fn ssid(&self) -> &Ssid {
556        &self.connect_req.selected_bss.ssid
557    }
558
559    pub fn bssid(&self) -> Bssid {
560        self.connect_req.selected_bss.bssid
561    }
562
563    pub fn beacon_period(&self) -> zx::MonotonicDuration {
564        zx::MonotonicDuration::from(TimeUnit(self.connect_req.selected_bss.beacon_period))
565    }
566
567    pub fn eapol_required(&self) -> bool {
568        self.connect_req.selected_bss.rsne().is_some()
569        // TODO(https://fxbug.dev/42139266): Add detection of WPA1 in softmac for testing
570        // purposes only. In particular, connect-to-wpa1-network relies
571        // on this half of the OR statement.
572            || self.connect_req.selected_bss.find_wpa_ie().is_some()
573    }
574
575    pub fn bind<'a, D>(
576        &'a mut self,
577        ctx: &'a mut Context<D>,
578        scanner: &'a mut Scanner,
579        channel_state: &'a mut ChannelState,
580    ) -> BoundClient<'a, D> {
581        BoundClient { sta: self, ctx, scanner, channel_state }
582    }
583
584    /// Only management and data frames should be processed. Furthermore, the source address should
585    /// be the BSSID the client associated to and the receiver address should either be non-unicast
586    /// or the client's MAC address.
587    fn should_handle_frame<B: SplitByteSlice>(&self, mac_frame: &mac::MacFrame<B>) -> bool {
588        wtrace::duration!(c"Client::should_handle_frame");
589
590        // Technically, |transmitter_addr| and |receiver_addr| would be more accurate but using src
591        // src and dst to be consistent with |data_dst_addr()|.
592        let (src_addr, dst_addr) = match mac_frame {
593            mac::MacFrame::Mgmt(mac::MgmtFrame { mgmt_hdr, .. }) => {
594                (Some(mgmt_hdr.addr3), mgmt_hdr.addr1)
595            }
596            mac::MacFrame::Data(mac::DataFrame { fixed_fields, .. }) => {
597                (mac::data_bssid(&fixed_fields), mac::data_dst_addr(&fixed_fields))
598            }
599            // Control frames are not supported. Drop them.
600            _ => return false,
601        };
602        src_addr.is_some_and(|src_addr| src_addr == self.bssid().into())
603            && (!dst_addr.is_unicast() || dst_addr == self.iface_mac)
604    }
605}
606
607pub struct BoundClient<'a, D> {
608    sta: &'a mut Client,
609    // TODO(https://fxbug.dev/42120453): pull everything out of Context and plop them here.
610    ctx: &'a mut Context<D>,
611    scanner: &'a mut Scanner,
612    channel_state: &'a mut ChannelState,
613}
614
615impl<'a, D: DeviceOps> akm_algorithm::AkmAction for BoundClient<'a, D> {
616    fn send_auth_frame(
617        &mut self,
618        auth_type: mac::AuthAlgorithmNumber,
619        seq_num: u16,
620        result_code: mac::StatusCode,
621        auth_content: &[u8],
622    ) -> Result<(), anyhow::Error> {
623        self.send_auth_frame(auth_type, seq_num, result_code, auth_content).map_err(|e| e.into())
624    }
625
626    fn forward_sme_sae_rx(
627        &mut self,
628        seq_num: u16,
629        status_code: fidl_ieee80211::StatusCode,
630        sae_fields: Vec<u8>,
631    ) {
632        self.forward_sae_frame_rx(seq_num, status_code, sae_fields)
633    }
634
635    fn forward_sae_handshake_ind(&mut self) {
636        self.forward_sae_handshake_ind()
637    }
638}
639
640impl<'a, D: DeviceOps> BoundClient<'a, D> {
641    /// Delivers a single MSDU to the STA's underlying device. The MSDU is delivered as an
642    /// Ethernet II frame.
643    /// Returns Err(_) if writing or delivering the Ethernet II frame failed.
644    fn deliver_msdu<B: SplitByteSlice>(&mut self, msdu: mac::Msdu<B>) -> Result<(), Error> {
645        let mac::Msdu { dst_addr, src_addr, llc_frame } = msdu;
646
647        let mut packet = [0u8; mac::MAX_ETH_FRAME_LEN];
648        let (frame_start, frame_end) = write_frame_with_fixed_slice!(&mut packet[..], {
649            headers: {
650                mac::EthernetIIHdr: &mac::EthernetIIHdr {
651                    da: dst_addr,
652                    sa: src_addr,
653                    ether_type: llc_frame.hdr.protocol_id,
654                },
655            },
656            payload: &llc_frame.body,
657        })?;
658        self.ctx
659            .device
660            .deliver_eth_frame(&packet[frame_start..frame_end])
661            .map_err(|s| Error::Status(format!("could not deliver Ethernet II frame"), s))
662    }
663
664    pub fn send_auth_frame(
665        &mut self,
666        auth_type: mac::AuthAlgorithmNumber,
667        seq_num: u16,
668        result_code: mac::StatusCode,
669        auth_content: &[u8],
670    ) -> Result<(), Error> {
671        let buffer = write_frame!({
672            headers: {
673                mac::MgmtHdr: &mgmt_writer::mgmt_hdr_to_ap(
674                    mac::FrameControl(0)
675                        .with_frame_type(mac::FrameType::MGMT)
676                        .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
677                    self.sta.bssid(),
678                    self.sta.iface_mac,
679                    mac::SequenceControl(0)
680                        .with_seq_num(self.ctx.seq_mgr.next_sns1(&self.sta.bssid().into()) as u16)
681                ),
682                mac::AuthHdr: &mac::AuthHdr {
683                    auth_alg_num: auth_type,
684                    auth_txn_seq_num: seq_num,
685                    status_code: result_code,
686                },
687            },
688            body: auth_content,
689        })?;
690        self.send_mgmt_or_ctrl_frame(buffer)
691            .map_err(|s| Error::Status(format!("error sending open auth frame"), s))
692    }
693
694    /// Sends an authentication frame using Open System authentication.
695    pub fn send_open_auth_frame(&mut self) -> Result<(), Error> {
696        self.send_auth_frame(
697            mac::AuthAlgorithmNumber::OPEN,
698            1,
699            fidl_ieee80211::StatusCode::Success.into(),
700            &[],
701        )
702    }
703
704    /// Sends an association request frame based on device capability.
705    pub fn send_assoc_req_frame(&mut self) -> Result<(), Error> {
706        let ssid = self.sta.ssid().clone();
707        let cap = &self.sta.client_capabilities.0;
708        let capability_info = cap.capability_info.0;
709        let rates: Vec<u8> = cap.rates.iter().map(|r| r.rate()).collect();
710        let ht_cap = cap.ht_cap;
711        let vht_cap = cap.vht_cap;
712        let security_ie = self.sta.connect_req.security_ie.clone();
713
714        let rsne = (!security_ie.is_empty() && security_ie[0] == ie::Id::RSNE.0)
715            .then(|| match rsne::from_bytes(&security_ie[..]) {
716                Ok((_, x)) => Ok(x),
717                Err(e) => Err(format_err!("error parsing rsne {:?} : {:?}", security_ie, e)),
718            })
719            .transpose()?;
720        let buffer = write_frame!({
721            headers: {
722                mac::MgmtHdr: &mgmt_writer::mgmt_hdr_to_ap(
723                    mac::FrameControl(0)
724                        .with_frame_type(mac::FrameType::MGMT)
725                        .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_REQ),
726                    self.sta.bssid(),
727                    self.sta.iface_mac,
728                    mac::SequenceControl(0)
729                        .with_seq_num(self.ctx.seq_mgr.next_sns1(&self.sta.bssid().into()) as u16)
730                ),
731                mac::AssocReqHdr: &mac::AssocReqHdr {
732                    capabilities: mac::CapabilityInfo(capability_info),
733                    listen_interval: 0,
734                },
735            },
736            ies: {
737                ssid: ssid,
738                supported_rates: rates,
739                extended_supported_rates: {/* continue rates */},
740                rsne?: rsne,
741                ht_cap?: ht_cap,
742                vht_cap?: vht_cap,
743            },
744        })?;
745        self.send_mgmt_or_ctrl_frame(buffer)
746            .map_err(|s| Error::Status(format!("error sending assoc req frame"), s))
747    }
748
749    /// Sends a "keep alive" response to the BSS. A keep alive response is a NULL data frame sent as
750    /// a response to the AP transmitting NULL data frames to the client.
751    // Note: This function was introduced to meet C++ MLME feature parity. However, there needs to
752    // be some investigation, whether these "keep alive" frames are the right way of keeping a
753    // client associated to legacy APs.
754    fn send_keep_alive_resp_frame(&mut self) -> Result<(), Error> {
755        let buffer = write_frame!({
756            headers: {
757                mac::FixedDataHdrFields: &data_writer::data_hdr_client_to_ap(
758                    mac::FrameControl(0)
759                        .with_frame_type(mac::FrameType::DATA)
760                        .with_data_subtype(mac::DataSubtype(0).with_null(true)),
761                    self.sta.bssid(),
762                    self.sta.iface_mac,
763                    mac::SequenceControl(0)
764                        .with_seq_num(self.ctx.seq_mgr.next_sns1(&self.sta.bssid().into()) as u16)
765                ),
766            },
767        })?;
768        self.ctx
769            .device
770            .send_wlan_frame(buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
771            .map_err(|s| Error::Status(format!("error sending keep alive frame"), s))
772    }
773
774    pub fn send_deauth_frame(&mut self, reason_code: mac::ReasonCode) -> Result<(), Error> {
775        let buffer = write_frame!({
776            headers: {
777                mac::MgmtHdr: &mgmt_writer::mgmt_hdr_to_ap(
778                    mac::FrameControl(0)
779                        .with_frame_type(mac::FrameType::MGMT)
780                        .with_mgmt_subtype(mac::MgmtSubtype::DEAUTH),
781                    self.sta.bssid(),
782                    self.sta.iface_mac,
783                    mac::SequenceControl(0)
784                        .with_seq_num(self.ctx.seq_mgr.next_sns1(&self.sta.bssid().into()) as u16)
785                ),
786                mac::DeauthHdr: &mac::DeauthHdr {
787                    reason_code,
788                },
789            },
790        })?;
791        let result = self
792            .send_mgmt_or_ctrl_frame(buffer)
793            .map_err(|s| Error::Status(format!("error sending deauthenticate frame"), s));
794        // Clear main_channel since there is no "main channel" after deauthenticating
795        self.channel_state.bind(&mut self.ctx, &mut self.scanner).clear_main_channel();
796
797        result
798    }
799
800    /// Sends the given |payload| as a data frame over the air. If the caller does not pass an |async_id| to
801    /// this function, then this function will generate its own |async_id| and end the trace if an error
802    /// occurs.
803    pub fn send_data_frame(
804        &mut self,
805        src: MacAddr,
806        dst: MacAddr,
807        is_protected: bool,
808        qos_ctrl: bool,
809        ether_type: u16,
810        payload: &[u8],
811        async_id: Option<trace::Id>,
812    ) -> Result<(), Error> {
813        let async_id_provided = async_id.is_some();
814        let async_id = async_id.unwrap_or_else(|| {
815            let async_id = trace::Id::new();
816            wtrace::async_begin_wlansoftmac_tx(async_id, "mlme");
817            async_id
818        });
819        wtrace::duration!(c"BoundClient::send_data_frame");
820
821        let qos_ctrl = if qos_ctrl {
822            Some(
823                wmm::derive_tid(ether_type, payload)
824                    .map_or(mac::QosControl(0), |tid| mac::QosControl(0).with_tid(tid as u16)),
825            )
826        } else {
827            None
828        };
829
830        // IEEE Std 802.11-2016, Table 9-26 specifies address field contents and their relation
831        // to the addr fields.
832        // TODO(https://fxbug.dev/42128470): Support A-MSDU address field contents.
833
834        // We do not currently support RA other than the BSS.
835        // TODO(https://fxbug.dev/42122401): Support to_ds = false and alternative RA for TDLS.
836        let to_ds = true;
837        let from_ds = src != self.sta.iface_mac;
838        // Detect when SA != TA, in which case we use addr4.
839        let addr1 = self.sta.bssid().into();
840        let addr2 = self.sta.iface_mac;
841        let addr3 = match (to_ds, from_ds) {
842            (false, false) => self.sta.bssid().into(),
843            (false, true) => src,
844            (true, _) => dst,
845        };
846        let addr4 = if from_ds && to_ds { Some(src) } else { None };
847
848        let tx_flags = match ether_type {
849            mac::ETHER_TYPE_EAPOL => fidl_softmac::WlanTxInfoFlags::FAVOR_RELIABILITY,
850            _ => fidl_softmac::WlanTxInfoFlags::empty(),
851        };
852
853        // TODO(https://fxbug.dev/353987692): Replace `header_room` with actual amount of space
854        // for the header in a network device buffer. The MAX_HEADER_SIZE is arbitrarily extended
855        // to emulate the extra room the network device buffer will likely always provide.
856        const MAX_HEADER_SIZE: usize = mem::size_of::<mac::FixedDataHdrFields>()
857            + mem::size_of::<MacAddr>()
858            + mem::size_of::<mac::QosControl>()
859            + mem::size_of::<mac::LlcHdr>();
860        let header_room = MAX_HEADER_SIZE + 100;
861        let arena = Arena::new();
862        let mut buffer = arena.insert_default_slice(header_room + payload.len());
863
864        // TODO(https://fxbug.dev/353987692): Remove this clone once we migrate to network device where
865        // the buffer can be reused.
866        let payload_start = buffer.len() - payload.len();
867        buffer[payload_start..].clone_from_slice(&payload[..]);
868
869        let (frame_start, _frame_end) =
870            write_frame_with_fixed_slice!(&mut buffer[..payload_start], {
871                fill_zeroes: (),
872                headers: {
873                    mac::FixedDataHdrFields: &mac::FixedDataHdrFields {
874                        frame_ctrl: mac::FrameControl(0)
875                            .with_frame_type(mac::FrameType::DATA)
876                            .with_data_subtype(mac::DataSubtype(0).with_qos(qos_ctrl.is_some()))
877                            .with_protected(is_protected)
878                            .with_to_ds(to_ds)
879                            .with_from_ds(from_ds),
880                        duration: 0,
881                        addr1,
882                        addr2,
883                        addr3,
884                        seq_ctrl: mac::SequenceControl(0).with_seq_num(
885                            match qos_ctrl.as_ref() {
886                                None => self.ctx.seq_mgr.next_sns1(&dst),
887                                Some(qos_ctrl) => self.ctx.seq_mgr.next_sns2(&dst, qos_ctrl.tid()),
888                            } as u16
889                        )
890                    },
891                    mac::Addr4?: addr4,
892                    mac::QosControl?: qos_ctrl,
893                    mac::LlcHdr: &data_writer::make_snap_llc_hdr(ether_type),
894                },
895            })
896            .map_err(|e| {
897                if !async_id_provided {
898                    wtrace::async_end_wlansoftmac_tx(async_id, zx::Status::INTERNAL);
899                }
900                e
901            })?;
902
903        // Adjust the start of the slice stored in the ArenaBox.
904        //
905        // Safety: buffer is a valid pointer to a slice allocated in arena.
906        let buffer = unsafe {
907            arena.assume_unchecked(NonNull::new_unchecked(
908                &mut ArenaBox::into_ptr(buffer).as_mut()[frame_start..],
909            ))
910        };
911        let buffer = arena.make_static(buffer);
912        self.ctx.device.send_wlan_frame(buffer, tx_flags, Some(async_id)).map_err(|s| {
913            if !async_id_provided {
914                wtrace::async_end_wlansoftmac_tx(async_id, s);
915            }
916            Error::Status(format!("error sending data frame"), s)
917        })
918    }
919
920    /// Sends an MLME-EAPOL.indication to MLME's SME peer.
921    /// Note: MLME-EAPOL.indication is a custom Fuchsia primitive and not defined in IEEE 802.11.
922    fn send_eapol_indication(
923        &mut self,
924        src_addr: MacAddr,
925        dst_addr: MacAddr,
926        eapol_frame: &[u8],
927    ) -> Result<(), Error> {
928        self.ctx
929            .device
930            .send_mlme_event(fidl_mlme::MlmeEvent::EapolInd {
931                ind: fidl_mlme::EapolIndication {
932                    src_addr: src_addr.to_array(),
933                    dst_addr: dst_addr.to_array(),
934                    data: eapol_frame.to_vec(),
935                },
936            })
937            .map_err(|e| e.into())
938    }
939
940    /// Sends an EAPoL frame over the air and reports transmission status to SME via an
941    /// MLME-EAPOL.confirm message.
942    pub fn send_eapol_frame(
943        &mut self,
944        src: MacAddr,
945        dst: MacAddr,
946        is_protected: bool,
947        eapol_frame: &[u8],
948    ) {
949        // TODO(https://fxbug.dev/42110270): EAPoL frames can be send in QoS data frames. However, Fuchsia's old C++
950        // MLME never sent EAPoL frames in QoS data frames. For feature parity do the same.
951        let result = self.send_data_frame(
952            src,
953            dst,
954            is_protected,
955            false, /* don't use QoS */
956            mac::ETHER_TYPE_EAPOL,
957            eapol_frame,
958            None,
959        );
960        let result_code = match result {
961            Ok(()) => fidl_mlme::EapolResultCode::Success,
962            Err(e) => {
963                error!("error sending EAPoL frame: {}", e);
964                fidl_mlme::EapolResultCode::TransmissionFailure
965            }
966        };
967
968        // Report transmission result to SME.
969        self.ctx
970            .device
971            .send_mlme_event(fidl_mlme::MlmeEvent::EapolConf {
972                resp: fidl_mlme::EapolConfirm { result_code, dst_addr: dst.to_array() },
973            })
974            .unwrap_or_else(|e| error!("error sending MLME-EAPOL.confirm message: {}", e));
975    }
976
977    pub fn send_ps_poll_frame(&mut self, aid: Aid) -> Result<(), Error> {
978        const PS_POLL_ID_MASK: u16 = 0b11000000_00000000;
979
980        let buffer = write_frame!({
981            headers: {
982                mac::FrameControl: &mac::FrameControl(0)
983                    .with_frame_type(mac::FrameType::CTRL)
984                    .with_ctrl_subtype(mac::CtrlSubtype::PS_POLL),
985                mac::PsPoll: &mac::PsPoll {
986                    // IEEE 802.11-2016 9.3.1.5 states the ID in the PS-Poll frame is the
987                    // association ID with the 2 MSBs set to 1.
988                    masked_aid: aid | PS_POLL_ID_MASK,
989                    bssid: self.sta.bssid(),
990                    ta: self.sta.iface_mac,
991                },
992            },
993        })?;
994        self.send_mgmt_or_ctrl_frame(buffer)
995            .map_err(|s| Error::Status(format!("error sending PS-Poll frame"), s))
996    }
997
998    /// Called when a previously scheduled `TimedEvent` fired.
999    pub async fn handle_timed_event(&mut self, event: TimedEvent) {
1000        self.sta.state = Some(self.sta.state.take().unwrap().on_timed_event(self, event).await)
1001    }
1002
1003    /// Called when an arbitrary frame was received over the air.
1004    pub async fn on_mac_frame<B: SplitByteSlice>(
1005        &mut self,
1006        bytes: B,
1007        rx_info: fidl_softmac::WlanRxInfo,
1008        async_id: trace::Id,
1009    ) {
1010        wtrace::duration!(c"BoundClient::on_mac_frame");
1011        // Safe: |state| is never None and always replaced with Some(..).
1012        self.sta.state =
1013            Some(self.sta.state.take().unwrap().on_mac_frame(self, bytes, rx_info, async_id).await);
1014    }
1015
1016    pub fn on_eth_frame_tx<B: SplitByteSlice>(
1017        &mut self,
1018        frame: B,
1019        async_id: trace::Id,
1020    ) -> Result<(), Error> {
1021        wtrace::duration!(c"BoundClient::on_eth_frame_tx");
1022        // Safe: |state| is never None and always replaced with Some(..).
1023        let state = self.sta.state.take().unwrap();
1024        let result = state.on_eth_frame(self, frame, async_id);
1025        self.sta.state.replace(state);
1026        result
1027    }
1028
1029    pub async fn start_connecting(&mut self) {
1030        // Safe: |state| is never None and always replaced with Some(..).
1031        let next_state = self.sta.state.take().unwrap().start_connecting(self).await;
1032        self.sta.state.replace(next_state);
1033    }
1034
1035    pub async fn handle_mlme_req(&mut self, msg: wlan_sme::MlmeRequest) {
1036        // Safe: |state| is never None and always replaced with Some(..).
1037        let next_state = self.sta.state.take().unwrap().handle_mlme_req(self, msg).await;
1038        self.sta.state.replace(next_state);
1039    }
1040
1041    fn send_connect_conf_failure(&mut self, result_code: fidl_ieee80211::StatusCode) {
1042        self.sta.connect_timeout.take();
1043        let bssid = self.sta.connect_req.selected_bss.bssid;
1044        self.send_connect_conf_failure_with_bssid(bssid, result_code);
1045    }
1046
1047    /// Send ConnectConf failure with BSSID specified.
1048    /// The connect timeout is not cleared as this method may be called with a foreign BSSID.
1049    fn send_connect_conf_failure_with_bssid(
1050        &mut self,
1051        bssid: Bssid,
1052        result_code: fidl_ieee80211::StatusCode,
1053    ) {
1054        let connect_conf = fidl_mlme::ConnectConfirm {
1055            peer_sta_address: bssid.to_array(),
1056            result_code,
1057            association_id: 0,
1058            association_ies: vec![],
1059        };
1060        self.ctx
1061            .device
1062            .send_mlme_event(fidl_mlme::MlmeEvent::ConnectConf { resp: connect_conf })
1063            .unwrap_or_else(|e| error!("error sending MLME-CONNECT.confirm: {}", e));
1064    }
1065
1066    fn send_connect_conf_success<B: SplitByteSlice>(
1067        &mut self,
1068        association_id: mac::Aid,
1069        association_ies: B,
1070    ) {
1071        self.sta.connect_timeout.take();
1072        let connect_conf = fidl_mlme::ConnectConfirm {
1073            peer_sta_address: self.sta.connect_req.selected_bss.bssid.to_array(),
1074            result_code: fidl_ieee80211::StatusCode::Success,
1075            association_id,
1076            association_ies: association_ies.to_vec(),
1077        };
1078        self.ctx
1079            .device
1080            .send_mlme_event(fidl_mlme::MlmeEvent::ConnectConf { resp: connect_conf })
1081            .unwrap_or_else(|e| error!("error sending MLME-CONNECT.confirm: {}", e));
1082    }
1083
1084    /// Sends an MLME-DEAUTHENTICATE.indication message to the joined BSS.
1085    fn send_deauthenticate_ind(
1086        &mut self,
1087        reason_code: fidl_ieee80211::ReasonCode,
1088        locally_initiated: LocallyInitiated,
1089    ) {
1090        // Clear main_channel since there is no "main channel" after deauthenticating
1091        self.channel_state.bind(&mut self.ctx, &mut self.scanner).clear_main_channel();
1092
1093        self.ctx
1094            .device
1095            .send_mlme_event(fidl_mlme::MlmeEvent::DeauthenticateInd {
1096                ind: fidl_mlme::DeauthenticateIndication {
1097                    peer_sta_address: self.sta.bssid().to_array(),
1098                    reason_code,
1099                    locally_initiated: locally_initiated.0,
1100                },
1101            })
1102            .unwrap_or_else(|e| error!("error sending MLME-DEAUTHENTICATE.indication: {}", e));
1103    }
1104
1105    /// Sends an MLME-DISASSOCIATE.indication message to the joined BSS.
1106    fn send_disassoc_ind(
1107        &mut self,
1108        reason_code: fidl_ieee80211::ReasonCode,
1109        locally_initiated: LocallyInitiated,
1110    ) {
1111        self.ctx
1112            .device
1113            .send_mlme_event(fidl_mlme::MlmeEvent::DisassociateInd {
1114                ind: fidl_mlme::DisassociateIndication {
1115                    peer_sta_address: self.sta.bssid().to_array(),
1116                    reason_code,
1117                    locally_initiated: locally_initiated.0,
1118                },
1119            })
1120            .unwrap_or_else(|e| error!("error sending MLME-DISASSOCIATE.indication: {}", e));
1121    }
1122
1123    async fn clear_association(&mut self) -> Result<(), zx::Status> {
1124        self.ctx
1125            .device
1126            .clear_association(&fidl_softmac::WlanSoftmacBaseClearAssociationRequest {
1127                peer_addr: Some(self.sta.bssid().to_array()),
1128                ..Default::default()
1129            })
1130            .await
1131    }
1132
1133    /// Sends an sae frame rx message to the SME.
1134    fn forward_sae_frame_rx(
1135        &mut self,
1136        seq_num: u16,
1137        status_code: fidl_ieee80211::StatusCode,
1138        sae_fields: Vec<u8>,
1139    ) {
1140        self.ctx
1141            .device
1142            .send_mlme_event(fidl_mlme::MlmeEvent::OnSaeFrameRx {
1143                frame: fidl_mlme::SaeFrame {
1144                    peer_sta_address: self.sta.bssid().to_array(),
1145                    seq_num,
1146                    status_code,
1147                    sae_fields,
1148                },
1149            })
1150            .unwrap_or_else(|e| error!("error sending OnSaeFrameRx: {}", e));
1151    }
1152
1153    fn forward_sae_handshake_ind(&mut self) {
1154        self.ctx
1155            .device
1156            .send_mlme_event(fidl_mlme::MlmeEvent::OnSaeHandshakeInd {
1157                ind: fidl_mlme::SaeHandshakeIndication {
1158                    peer_sta_address: self.sta.bssid().to_array(),
1159                },
1160            })
1161            .unwrap_or_else(|e| error!("error sending OnSaeHandshakeInd: {}", e));
1162    }
1163
1164    fn send_mgmt_or_ctrl_frame(&mut self, buffer: ArenaStaticBox<[u8]>) -> Result<(), zx::Status> {
1165        self.ctx.device.send_wlan_frame(buffer, fidl_softmac::WlanTxInfoFlags::empty(), None)
1166    }
1167}
1168
1169pub struct ParsedConnectRequest {
1170    pub selected_bss: BssDescription,
1171    pub connect_failure_timeout: u32,
1172    pub auth_type: fidl_mlme::AuthenticationTypes,
1173    pub sae_password: Vec<u8>,
1174    pub wep_key: Option<fidl_mlme::SetKeyDescriptor>,
1175    pub security_ie: Vec<u8>,
1176}
1177
1178pub struct ParsedAssociateResp {
1179    pub association_id: u16,
1180    pub capabilities: CapabilityInfo,
1181    pub rates: Vec<ie::SupportedRate>,
1182    pub ht_cap: Option<ie::HtCapabilities>,
1183    pub vht_cap: Option<ie::VhtCapabilities>,
1184}
1185
1186impl ParsedAssociateResp {
1187    pub fn parse<B: SplitByteSlice>(assoc_resp_frame: &mac::AssocRespFrame<B>) -> Self {
1188        let mut parsed = ParsedAssociateResp {
1189            association_id: assoc_resp_frame.assoc_resp_hdr.aid,
1190            capabilities: assoc_resp_frame.assoc_resp_hdr.capabilities,
1191            rates: vec![],
1192            ht_cap: None,
1193            vht_cap: None,
1194        };
1195        for (id, body) in assoc_resp_frame.ies() {
1196            match id {
1197                Id::SUPPORTED_RATES => match ie::parse_supported_rates(body) {
1198                    Err(e) => warn!("invalid Supported Rates: {}", e),
1199                    Ok(supported_rates) => {
1200                        // safe to unwrap because supported rate is 1-byte long thus always aligned
1201                        parsed.rates.extend(supported_rates.iter());
1202                    }
1203                },
1204                Id::EXTENDED_SUPPORTED_RATES => match ie::parse_extended_supported_rates(body) {
1205                    Err(e) => warn!("invalid Extended Supported Rates: {}", e),
1206                    Ok(supported_rates) => {
1207                        // safe to unwrap because supported rate is 1-byte long thus always aligned
1208                        parsed.rates.extend(supported_rates.iter());
1209                    }
1210                },
1211                Id::HT_CAPABILITIES => match ie::parse_ht_capabilities(body) {
1212                    Err(e) => warn!("invalid HT Capabilities: {}", e),
1213                    Ok(ht_cap) => {
1214                        parsed.ht_cap = Some(*ht_cap);
1215                    }
1216                },
1217                Id::VHT_CAPABILITIES => match ie::parse_vht_capabilities(body) {
1218                    Err(e) => warn!("invalid VHT Capabilities: {}", e),
1219                    Ok(vht_cap) => {
1220                        parsed.vht_cap = Some(*vht_cap);
1221                    }
1222                },
1223                // TODO(https://fxbug.dev/42120297): parse vendor ID and include WMM param if exists
1224                _ => {}
1225            }
1226        }
1227        parsed
1228    }
1229}
1230
1231impl<'a, D: DeviceOps> BlockAckTx for BoundClient<'a, D> {
1232    /// Sends a BlockAck frame to the associated AP.
1233    ///
1234    /// BlockAck frames are described by 802.11-2016, section 9.6.5.2, 9.6.5.3, and 9.6.5.4.
1235    fn send_block_ack_frame(&mut self, n: usize, body: &[u8]) -> Result<(), Error> {
1236        let arena = Arena::new();
1237        let buffer = arena.insert_default_slice::<u8>(n);
1238        let mut buffer = arena.make_static(buffer);
1239        let mut writer = BufferWriter::new(&mut buffer[..]);
1240        write_block_ack_hdr(
1241            &mut writer,
1242            self.sta.bssid(),
1243            self.sta.iface_mac,
1244            &mut self.ctx.seq_mgr,
1245        )
1246        .and_then(|_| writer.append_bytes(body).map_err(Into::into))?;
1247        self.send_mgmt_or_ctrl_frame(buffer)
1248            .map_err(|status| Error::Status(format!("error sending BlockAck frame"), status))
1249    }
1250}
1251
1252/// Writes the header of the management frame for BlockAck frames to the given buffer.
1253///
1254/// The address may be that of the originator or recipient. The frame formats are described by IEEE
1255/// Std 802.11-2016, 9.6.5.
1256fn write_block_ack_hdr<B: Append>(
1257    buffer: &mut B,
1258    bssid: Bssid,
1259    addr: MacAddr,
1260    seq_mgr: &mut SequenceManager,
1261) -> Result<(), Error> {
1262    // The management header differs for APs and clients. The frame control and management header
1263    // are constructed here, but AP and client STAs share the code that constructs the body. See
1264    // the `block_ack` module.
1265    Ok(append_frame_to!(
1266        buffer,
1267        {
1268            headers: {
1269                mac::MgmtHdr: &mgmt_writer::mgmt_hdr_to_ap(
1270                    mac::FrameControl(0)
1271                        .with_frame_type(mac::FrameType::MGMT)
1272                        .with_mgmt_subtype(mac::MgmtSubtype::ACTION),
1273                    bssid,
1274                    addr,
1275                    mac::SequenceControl(0)
1276                        .with_seq_num(seq_mgr.next_sns1(&bssid.into()) as u16),
1277                ),
1278            },
1279        }
1280    )
1281    .map(|_buffer| {})?)
1282}
1283
1284#[cfg(test)]
1285mod tests {
1286    use super::state::DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT;
1287    use super::*;
1288    use crate::block_ack::{
1289        self, BlockAckState, Closed, ADDBA_REQ_FRAME_LEN, ADDBA_RESP_FRAME_LEN,
1290    };
1291    use crate::client::lost_bss::LostBssCounter;
1292    use crate::client::test_utils::drain_timeouts;
1293    use crate::device::{test_utils, FakeDevice, FakeDeviceConfig, FakeDeviceState, LinkStatus};
1294    use crate::test_utils::{fake_wlan_channel, MockWlanRxInfo};
1295    use fuchsia_sync::Mutex;
1296    use lazy_static::lazy_static;
1297    use std::sync::Arc;
1298    use wlan_common::capabilities::StaCapabilities;
1299    use wlan_common::channel::Cbw;
1300    use wlan_common::stats::SignalStrengthAverage;
1301    use wlan_common::test_utils::fake_capabilities::fake_client_capabilities;
1302    use wlan_common::test_utils::fake_frames::*;
1303    use wlan_common::timer::{self, create_timer};
1304    use wlan_common::{assert_variant, fake_bss_description, fake_fidl_bss_description};
1305    use wlan_sme::responder::Responder;
1306    use wlan_statemachine::*;
1307    use {fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_internal as fidl_internal};
1308    lazy_static! {
1309        static ref BSSID: Bssid = [6u8; 6].into();
1310        static ref IFACE_MAC: MacAddr = [7u8; 6].into();
1311    }
1312    const RSNE: &[u8] = &[
1313        0x30, 0x14, //  ID and len
1314        1, 0, //  version
1315        0x00, 0x0f, 0xac, 0x04, //  group data cipher suite
1316        0x01, 0x00, //  pairwise cipher suite count
1317        0x00, 0x0f, 0xac, 0x04, //  pairwise cipher suite list
1318        0x01, 0x00, //  akm suite count
1319        0x00, 0x0f, 0xac, 0x02, //  akm suite list
1320        0xa8, 0x04, //  rsn capabilities
1321    ];
1322    const SCAN_CHANNEL_PRIMARY: u8 = 6;
1323    // Note: not necessarily valid beacon frame.
1324    #[rustfmt::skip]
1325    const BEACON_FRAME: &'static [u8] = &[
1326        // Mgmt header
1327        0b10000000, 0, // Frame Control
1328        0, 0, // Duration
1329        255, 255, 255, 255, 255, 255, // addr1
1330        6, 6, 6, 6, 6, 6, // addr2
1331        6, 6, 6, 6, 6, 6, // addr3
1332        0, 0, // Sequence Control
1333        // Beacon header:
1334        0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
1335        10, 0, // Beacon interval
1336        33, 0, // Capabilities
1337        // IEs:
1338        0, 4, 0x73, 0x73, 0x69, 0x64, // SSID - "ssid"
1339        1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Supported rates
1340        3, 1, 11, // DSSS parameter set - channel 11
1341        5, 4, 0, 0, 0, 0, // TIM
1342    ];
1343
1344    struct MockObjects {
1345        fake_device: FakeDevice,
1346        fake_device_state: Arc<Mutex<FakeDeviceState>>,
1347        timer: Option<Timer<super::TimedEvent>>,
1348        time_stream: timer::EventStream<super::TimedEvent>,
1349    }
1350
1351    impl MockObjects {
1352        // TODO(https://fxbug.dev/327499461): This function is async to ensure MLME functions will
1353        // run in an async context and not call `wlan_common::timer::Timer::now` without an
1354        // executor.
1355        async fn new() -> Self {
1356            let (timer, time_stream) = create_timer();
1357            let (fake_device, fake_device_state) = FakeDevice::new_with_config(
1358                FakeDeviceConfig::default()
1359                    .with_mock_mac_role(fidl_common::WlanMacRole::Client)
1360                    .with_mock_sta_addr((*IFACE_MAC).to_array()),
1361            )
1362            .await;
1363            Self { fake_device, fake_device_state, timer: Some(timer), time_stream }
1364        }
1365
1366        async fn make_mlme(&mut self) -> ClientMlme<FakeDevice> {
1367            let mut mlme = ClientMlme::new(
1368                Default::default(),
1369                self.fake_device.clone(),
1370                self.timer.take().unwrap(),
1371            )
1372            .await
1373            .expect("Failed to create client MLME.");
1374            mlme.set_main_channel(fake_wlan_channel().into())
1375                .await
1376                .expect("unable to set main channel");
1377            mlme
1378        }
1379    }
1380
1381    fn scan_req() -> fidl_mlme::ScanRequest {
1382        fidl_mlme::ScanRequest {
1383            txn_id: 1337,
1384            scan_type: fidl_mlme::ScanTypes::Passive,
1385            channel_list: vec![SCAN_CHANNEL_PRIMARY],
1386            ssid_list: vec![Ssid::try_from("ssid").unwrap().into()],
1387            probe_delay: 0,
1388            min_channel_time: 100,
1389            max_channel_time: 300,
1390        }
1391    }
1392
1393    fn make_client_station() -> Client {
1394        let connect_req = ParsedConnectRequest {
1395            selected_bss: fake_bss_description!(Open, bssid: BSSID.to_array()),
1396            connect_failure_timeout: 100,
1397            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1398            sae_password: vec![],
1399            wep_key: None,
1400            security_ie: vec![],
1401        };
1402        Client::new(connect_req, *IFACE_MAC, fake_client_capabilities())
1403    }
1404
1405    fn make_client_station_protected() -> Client {
1406        let connect_req = ParsedConnectRequest {
1407            selected_bss: fake_bss_description!(Wpa2, bssid: BSSID.to_array()),
1408            connect_failure_timeout: 100,
1409            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1410            sae_password: vec![],
1411            wep_key: None,
1412            security_ie: RSNE.to_vec(),
1413        };
1414        Client::new(connect_req, *IFACE_MAC, fake_client_capabilities())
1415    }
1416
1417    impl ClientMlme<FakeDevice> {
1418        fn make_client_station(&mut self) {
1419            self.sta.replace(make_client_station());
1420        }
1421
1422        fn make_client_station_protected(&mut self) {
1423            self.sta.replace(make_client_station_protected());
1424        }
1425
1426        fn get_bound_client(&mut self) -> Option<BoundClient<'_, FakeDevice>> {
1427            match self.sta.as_mut() {
1428                None => None,
1429                Some(sta) => {
1430                    Some(sta.bind(&mut self.ctx, &mut self.scanner, &mut self.channel_state))
1431                }
1432            }
1433        }
1434    }
1435
1436    impl BoundClient<'_, FakeDevice> {
1437        fn move_to_associated_state(&mut self) {
1438            use super::state::*;
1439            let status_check_timeout =
1440                schedule_association_status_timeout(self.sta.beacon_period(), &mut self.ctx.timer);
1441            let state =
1442                States::from(wlan_statemachine::testing::new_state(Associated(Association {
1443                    aid: 42,
1444                    assoc_resp_ies: vec![],
1445                    controlled_port_open: true,
1446                    ap_ht_op: None,
1447                    ap_vht_op: None,
1448                    qos: Qos::Disabled,
1449                    lost_bss_counter: LostBssCounter::start(
1450                        self.sta.beacon_period(),
1451                        DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
1452                    ),
1453                    status_check_timeout,
1454                    signal_strength_average: SignalStrengthAverage::new(),
1455                    block_ack_state: StateMachine::new(BlockAckState::from(State::new(Closed))),
1456                })));
1457            self.sta.state.replace(state);
1458        }
1459
1460        async fn close_controlled_port(&mut self) {
1461            self.handle_mlme_req(wlan_sme::MlmeRequest::SetCtrlPort(
1462                fidl_mlme::SetControlledPortRequest {
1463                    peer_sta_address: BSSID.to_array(),
1464                    state: fidl_mlme::ControlledPortState::Closed,
1465                },
1466            ))
1467            .await;
1468        }
1469    }
1470
1471    #[fuchsia::test(allow_stalls = false)]
1472    async fn spawns_new_sta_on_connect_request_from_sme() {
1473        let mut m = MockObjects::new().await;
1474        let mut me = m.make_mlme().await;
1475        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
1476        me.on_sme_connect(fidl_mlme::ConnectRequest {
1477            selected_bss: fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
1478            connect_failure_timeout: 100,
1479            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1480            sae_password: vec![],
1481            wep_key: None,
1482            security_ie: vec![],
1483        })
1484        .await
1485        .expect("valid ConnectRequest should be handled successfully");
1486        me.get_bound_client().expect("client sta should have been created by now.");
1487    }
1488
1489    #[fuchsia::test(allow_stalls = false)]
1490    async fn fails_to_connect_if_channel_unknown() {
1491        let mut m = MockObjects::new().await;
1492        let mut me = m.make_mlme().await;
1493        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
1494        let mut req = fidl_mlme::ConnectRequest {
1495            selected_bss: fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
1496            connect_failure_timeout: 100,
1497            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1498            sae_password: vec![],
1499            wep_key: None,
1500            security_ie: vec![],
1501        };
1502
1503        req.selected_bss.channel.cbw = fidl_fuchsia_wlan_common::ChannelBandwidth::unknown();
1504        me.on_sme_connect(req)
1505            .await
1506            .expect_err("ConnectRequest with unknown channel should be rejected");
1507        assert!(me.get_bound_client().is_none());
1508    }
1509
1510    #[fuchsia::test(allow_stalls = false)]
1511    async fn rsn_ie_implies_sta_eapol_required() {
1512        let mut m = MockObjects::new().await;
1513        let mut me = m.make_mlme().await;
1514        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
1515        me.on_sme_connect(fidl_mlme::ConnectRequest {
1516            selected_bss: fake_fidl_bss_description!(Wpa2, ssid: Ssid::try_from("foo").unwrap()),
1517            connect_failure_timeout: 100,
1518            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1519            sae_password: vec![],
1520            wep_key: None,
1521            security_ie: vec![],
1522        })
1523        .await
1524        .expect("valid ConnectRequest should be handled successfully");
1525        let client = me.get_bound_client().expect("client sta should have been created by now.");
1526        assert!(client.sta.eapol_required());
1527    }
1528
1529    #[fuchsia::test(allow_stalls = false)]
1530    async fn wpa1_implies_sta_eapol_required() {
1531        let mut m = MockObjects::new().await;
1532        let mut me = m.make_mlme().await;
1533        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
1534        me.on_sme_connect(fidl_mlme::ConnectRequest {
1535            selected_bss: fake_fidl_bss_description!(Wpa1, ssid: Ssid::try_from("foo").unwrap()),
1536            connect_failure_timeout: 100,
1537            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1538            sae_password: vec![],
1539            wep_key: None,
1540            security_ie: vec![],
1541        })
1542        .await
1543        .expect("valid ConnectRequest should be handled successfully");
1544        let client = me.get_bound_client().expect("client sta should have been created by now.");
1545        assert!(client.sta.eapol_required());
1546    }
1547
1548    #[fuchsia::test(allow_stalls = false)]
1549    async fn no_wpa_or_rsn_ie_implies_sta_eapol_not_required() {
1550        let mut m = MockObjects::new().await;
1551        let mut me = m.make_mlme().await;
1552        assert!(me.get_bound_client().is_none(), "MLME should not contain client, yet");
1553        me.on_sme_connect(fidl_mlme::ConnectRequest {
1554            selected_bss: fake_fidl_bss_description!(Open, ssid: Ssid::try_from("foo").unwrap()),
1555            connect_failure_timeout: 100,
1556            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1557            sae_password: vec![],
1558            wep_key: None,
1559            security_ie: vec![],
1560        })
1561        .await
1562        .expect("valid ConnectRequest should be handled successfully");
1563        let client = me.get_bound_client().expect("client sta should have been created by now.");
1564        assert!(!client.sta.eapol_required());
1565    }
1566
1567    /// Consumes `TimedEvent` values from the `timer::EventStream` held by `mock_objects` and
1568    /// handles each `TimedEvent` value with `mlme`. This function makes the following assertions:
1569    ///
1570    ///   - The `timer::EventStream` held by `mock_objects` starts with one `StatusCheckTimeout`
1571    ///     pending.
1572    ///   - For the `beacon_count` specified, `mlme` will consume the current `StatusCheckTimeout`
1573    ///     and schedule the next.
1574    ///   - `mlme` produces a `fidl_mlme::SignalReportIndication` for each StatusCheckTimeout
1575    ///     consumed.
1576    async fn handle_association_status_checks_and_signal_reports(
1577        mock_objects: &mut MockObjects,
1578        mlme: &mut ClientMlme<FakeDevice>,
1579        beacon_count: u32,
1580    ) {
1581        for _ in 0..beacon_count / super::state::ASSOCIATION_STATUS_TIMEOUT_BEACON_COUNT {
1582            let (_, timed_event, _) = mock_objects
1583                .time_stream
1584                .try_next()
1585                .unwrap()
1586                .expect("Should have scheduled a timed event");
1587            mlme.handle_timed_event(timed_event.event).await;
1588            assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 0);
1589            mock_objects
1590                .fake_device_state
1591                .lock()
1592                .next_mlme_msg::<fidl_internal::SignalReportIndication>()
1593                .expect("error reading SignalReport.indication");
1594        }
1595    }
1596
1597    #[fuchsia::test(allow_stalls = false)]
1598    async fn test_auto_deauth_uninterrupted_interval() {
1599        let mut mock_objects = MockObjects::new().await;
1600        let mut mlme = mock_objects.make_mlme().await;
1601        mlme.make_client_station();
1602        let mut client = mlme.get_bound_client().expect("client should be present");
1603
1604        client.move_to_associated_state();
1605
1606        // Verify timer is scheduled and move the time to immediately before auto deauth is triggered.
1607        handle_association_status_checks_and_signal_reports(
1608            &mut mock_objects,
1609            &mut mlme,
1610            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
1611        )
1612        .await;
1613
1614        // One more timeout to trigger the auto deauth
1615        let (_, timed_event, _) = mock_objects
1616            .time_stream
1617            .try_next()
1618            .unwrap()
1619            .expect("Should have scheduled a timed event");
1620
1621        // Verify that triggering event at deadline causes deauth
1622        mlme.handle_timed_event(timed_event.event).await;
1623        mock_objects
1624            .fake_device_state
1625            .lock()
1626            .next_mlme_msg::<fidl_internal::SignalReportIndication>()
1627            .expect("error reading SignalReport.indication");
1628        assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 1);
1629        #[rustfmt::skip]
1630        assert_eq!(&mock_objects.fake_device_state.lock().wlan_queue[0].0[..], &[
1631            // Mgmt header:
1632            0b1100_00_00, 0b00000000, // FC
1633            0, 0, // Duration
1634            6, 6, 6, 6, 6, 6, // addr1
1635            7, 7, 7, 7, 7, 7, // addr2
1636            6, 6, 6, 6, 6, 6, // addr3
1637            0x10, 0, // Sequence Control
1638            3, 0, // reason code
1639        ][..]);
1640        let deauth_ind = mock_objects
1641            .fake_device_state
1642            .lock()
1643            .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
1644            .expect("error reading DEAUTHENTICATE.indication");
1645        assert_eq!(
1646            deauth_ind,
1647            fidl_mlme::DeauthenticateIndication {
1648                peer_sta_address: BSSID.to_array(),
1649                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
1650                locally_initiated: true,
1651            }
1652        );
1653    }
1654
1655    #[fuchsia::test(allow_stalls = false)]
1656    async fn test_auto_deauth_received_beacon() {
1657        let mut mock_objects = MockObjects::new().await;
1658        let mut mlme = mock_objects.make_mlme().await;
1659        mlme.make_client_station();
1660        let mut client = mlme.get_bound_client().expect("client should be present");
1661
1662        client.move_to_associated_state();
1663
1664        // Move the countdown to just about to cause auto deauth.
1665        handle_association_status_checks_and_signal_reports(
1666            &mut mock_objects,
1667            &mut mlme,
1668            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
1669        )
1670        .await;
1671
1672        // Receive beacon midway, so lost bss countdown is reset.
1673        // If this beacon is not received, the next timeout will trigger auto deauth.
1674        mlme.on_mac_frame_rx(
1675            BEACON_FRAME,
1676            fidl_softmac::WlanRxInfo {
1677                rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
1678                valid_fields: fidl_softmac::WlanRxInfoValid::empty(),
1679                phy: fidl_common::WlanPhyType::Dsss,
1680                data_rate: 0,
1681                channel: mlme.channel_state.get_main_channel().unwrap(),
1682                mcs: 0,
1683                rssi_dbm: 0,
1684                snr_dbh: 0,
1685            },
1686            0.into(),
1687        )
1688        .await;
1689
1690        // Verify auto deauth is not triggered for the entire duration.
1691        handle_association_status_checks_and_signal_reports(
1692            &mut mock_objects,
1693            &mut mlme,
1694            DEFAULT_AUTO_DEAUTH_TIMEOUT_BEACON_COUNT,
1695        )
1696        .await;
1697
1698        // Verify more timer is scheduled
1699        let (_, timed_event2, _) = mock_objects
1700            .time_stream
1701            .try_next()
1702            .unwrap()
1703            .expect("Should have scheduled a timed event");
1704
1705        // Verify that triggering event at new deadline causes deauth
1706        mlme.handle_timed_event(timed_event2.event).await;
1707        mock_objects
1708            .fake_device_state
1709            .lock()
1710            .next_mlme_msg::<fidl_internal::SignalReportIndication>()
1711            .expect("error reading SignalReport.indication");
1712        assert_eq!(mock_objects.fake_device_state.lock().wlan_queue.len(), 1);
1713        #[rustfmt::skip]
1714        assert_eq!(&mock_objects.fake_device_state.lock().wlan_queue[0].0[..], &[
1715            // Mgmt header:
1716            0b1100_00_00, 0b00000000, // FC
1717            0, 0, // Duration
1718            6, 6, 6, 6, 6, 6, // addr1
1719            7, 7, 7, 7, 7, 7, // addr2
1720            6, 6, 6, 6, 6, 6, // addr3
1721            0x10, 0, // Sequence Control
1722            3, 0, // reason code
1723        ][..]);
1724        let deauth_ind = mock_objects
1725            .fake_device_state
1726            .lock()
1727            .next_mlme_msg::<fidl_mlme::DeauthenticateIndication>()
1728            .expect("error reading DEAUTHENTICATE.indication");
1729        assert_eq!(
1730            deauth_ind,
1731            fidl_mlme::DeauthenticateIndication {
1732                peer_sta_address: BSSID.to_array(),
1733                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
1734                locally_initiated: true,
1735            }
1736        );
1737    }
1738
1739    #[fuchsia::test(allow_stalls = false)]
1740    async fn client_send_open_auth_frame() {
1741        let mut m = MockObjects::new().await;
1742        let mut me = m.make_mlme().await;
1743        me.make_client_station();
1744        let mut client = me.get_bound_client().expect("client should be present");
1745        client.send_open_auth_frame().expect("error delivering WLAN frame");
1746        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1747        #[rustfmt::skip]
1748        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
1749            // Mgmt header:
1750            0b1011_00_00, 0b00000000, // FC
1751            0, 0, // Duration
1752            6, 6, 6, 6, 6, 6, // addr1
1753            7, 7, 7, 7, 7, 7, // addr2
1754            6, 6, 6, 6, 6, 6, // addr3
1755            0x10, 0, // Sequence Control
1756            // Auth header:
1757            0, 0, // auth algorithm
1758            1, 0, // auth txn seq num
1759            0, 0, // status code
1760        ][..]);
1761    }
1762
1763    #[fuchsia::test(allow_stalls = false)]
1764    async fn client_send_assoc_req_frame() {
1765        let mut m = MockObjects::new().await;
1766        let mut me = m.make_mlme().await;
1767        let connect_req = ParsedConnectRequest {
1768            selected_bss: fake_bss_description!(Wpa2,
1769                ssid: Ssid::try_from([11, 22, 33, 44]).unwrap(),
1770                bssid: BSSID.to_array(),
1771            ),
1772            connect_failure_timeout: 100,
1773            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1774            sae_password: vec![],
1775            wep_key: None,
1776            security_ie: RSNE.to_vec(),
1777        };
1778        let client_capabilities = ClientCapabilities(StaCapabilities {
1779            capability_info: CapabilityInfo(0x1234),
1780            rates: vec![8u8, 7, 6, 5, 4, 3, 2, 1, 0].into_iter().map(ie::SupportedRate).collect(),
1781            ht_cap: ie::parse_ht_capabilities(&(0..26).collect::<Vec<u8>>()[..]).map(|h| *h).ok(),
1782            vht_cap: ie::parse_vht_capabilities(&(100..112).collect::<Vec<u8>>()[..])
1783                .map(|v| *v)
1784                .ok(),
1785        });
1786        me.sta.replace(Client::new(connect_req, *IFACE_MAC, client_capabilities));
1787        let mut client = me.get_bound_client().expect("client should be present");
1788        client.send_assoc_req_frame().expect("error delivering WLAN frame");
1789        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1790        assert_eq!(
1791            &m.fake_device_state.lock().wlan_queue[0].0[..],
1792            &[
1793                // Mgmt header:
1794                0, 0, // FC
1795                0, 0, // Duration
1796                6, 6, 6, 6, 6, 6, // addr1
1797                7, 7, 7, 7, 7, 7, // addr2
1798                6, 6, 6, 6, 6, 6, // addr3
1799                0x10, 0, // Sequence Control
1800                // Association Request header:
1801                0x34, 0x12, // capability info
1802                0, 0, // listen interval
1803                // IEs
1804                0, 4, // SSID id and length
1805                11, 22, 33, 44, // SSID
1806                1, 8, // supp rates id and length
1807                8, 7, 6, 5, 4, 3, 2, 1, // supp rates
1808                50, 1, // ext supp rates and length
1809                0, // ext supp rates
1810                0x30, 0x14, // RSNE ID and len
1811                1, 0, // RSNE version
1812                0x00, 0x0f, 0xac, 0x04, // RSNE group data cipher suite
1813                0x01, 0x00, // RSNE pairwise cipher suite count
1814                0x00, 0x0f, 0xac, 0x04, // RSNE pairwise cipher suite list
1815                0x01, 0x00, // RSNE akm suite count
1816                0x00, 0x0f, 0xac, 0x02, // RSNE akm suite list
1817                0xa8, 0x04, // RSNE rsn capabilities
1818                45, 26, // HT Cap id and length
1819                0, 1, 2, 3, 4, 5, 6, 7, // HT Cap \
1820                8, 9, 10, 11, 12, 13, 14, 15, // HT Cap \
1821                16, 17, 18, 19, 20, 21, 22, 23, // HT Cap \
1822                24, 25, // HT Cap (26 bytes)
1823                191, 12, // VHT Cap id and length
1824                100, 101, 102, 103, 104, 105, 106, 107, // VHT Cap \
1825                108, 109, 110, 111, // VHT Cap (12 bytes)
1826            ][..]
1827        );
1828    }
1829
1830    #[fuchsia::test(allow_stalls = false)]
1831    async fn client_send_keep_alive_resp_frame() {
1832        let mut m = MockObjects::new().await;
1833        let mut me = m.make_mlme().await;
1834        me.make_client_station();
1835        let mut client = me.get_bound_client().expect("client should be present");
1836        client.send_keep_alive_resp_frame().expect("error delivering WLAN frame");
1837        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1838        #[rustfmt::skip]
1839        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
1840            // Data header:
1841            0b0100_10_00, 0b0000000_1, // FC
1842            0, 0, // Duration
1843            6, 6, 6, 6, 6, 6, // addr1
1844            7, 7, 7, 7, 7, 7, // addr2
1845            6, 6, 6, 6, 6, 6, // addr3
1846            0x10, 0, // Sequence Control
1847        ][..]);
1848    }
1849
1850    #[fuchsia::test(allow_stalls = false)]
1851    async fn client_send_data_frame() {
1852        let payload = vec![5; 8];
1853        let mut m = MockObjects::new().await;
1854        let mut me = m.make_mlme().await;
1855        me.make_client_station();
1856        let mut client = me.get_bound_client().expect("client should be present");
1857        client
1858            .send_data_frame(*IFACE_MAC, [4; 6].into(), false, false, 0x1234, &payload[..], None)
1859            .expect("error delivering WLAN frame");
1860        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1861        #[rustfmt::skip]
1862        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
1863            // Data header:
1864            0b0000_10_00, 0b0000000_1, // FC
1865            0, 0, // Duration
1866            6, 6, 6, 6, 6, 6, // addr1
1867            7, 7, 7, 7, 7, 7, // addr2
1868            4, 4, 4, 4, 4, 4, // addr3
1869            0x10, 0, // Sequence Control
1870            // LLC header:
1871            0xAA, 0xAA, 0x03, // DSAP, SSAP, Control
1872            0, 0, 0, // OUI
1873            0x12, 0x34, // Protocol ID
1874            // Payload
1875            5, 5, 5, 5, 5, 5, 5, 5,
1876        ][..]);
1877    }
1878
1879    #[fuchsia::test(allow_stalls = false)]
1880    async fn client_send_data_frame_ipv4_qos() {
1881        let mut m = MockObjects::new().await;
1882        let mut me = m.make_mlme().await;
1883        let mut client = make_client_station();
1884        client
1885            .bind(&mut me.ctx, &mut me.scanner, &mut me.channel_state)
1886            .send_data_frame(
1887                *IFACE_MAC,
1888                [4; 6].into(),
1889                false,
1890                true,
1891                0x0800,              // IPv4
1892                &[1, 0xB0, 3, 4, 5], // DSCP = 0b101100 (i.e. VOICE-ADMIT)
1893                None,
1894            )
1895            .expect("error delivering WLAN frame");
1896        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1897        #[rustfmt::skip]
1898        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
1899            // Data header:
1900            0b1000_10_00, 0b0000000_1, // FC
1901            0, 0, // Duration
1902            6, 6, 6, 6, 6, 6, // addr1
1903            7, 7, 7, 7, 7, 7, // addr2
1904            4, 4, 4, 4, 4, 4, // addr3
1905            0x10, 0, // Sequence Control
1906            0x06, 0, // QoS Control - TID = 6
1907            // LLC header:
1908            0xAA, 0xAA, 0x03, // DSAP, SSAP, Control
1909            0, 0, 0, // OUI
1910            0x08, 0x00, // Protocol ID
1911            // Payload
1912            1, 0xB0, 3, 4, 5,
1913        ][..]);
1914    }
1915
1916    #[fuchsia::test(allow_stalls = false)]
1917    async fn client_send_data_frame_ipv6_qos() {
1918        let mut m = MockObjects::new().await;
1919        let mut me = m.make_mlme().await;
1920        let mut client = make_client_station();
1921        client
1922            .bind(&mut me.ctx, &mut me.scanner, &mut me.channel_state)
1923            .send_data_frame(
1924                *IFACE_MAC,
1925                [4; 6].into(),
1926                false,
1927                true,
1928                0x86DD,                         // IPv6
1929                &[0b0101, 0b10000000, 3, 4, 5], // DSCP = 0b010110 (i.e. AF23)
1930                None,
1931            )
1932            .expect("error delivering WLAN frame");
1933        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1934        #[rustfmt::skip]
1935        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
1936            // Data header:
1937            0b1000_10_00, 0b0000000_1, // FC
1938            0, 0, // Duration
1939            6, 6, 6, 6, 6, 6, // addr1
1940            7, 7, 7, 7, 7, 7, // addr2
1941            4, 4, 4, 4, 4, 4, // addr3
1942            0x10, 0, // Sequence Control
1943            0x03, 0, // QoS Control - TID = 3
1944            // LLC header:
1945            0xAA, 0xAA, 0x03, // DSAP, SSAP, Control
1946            0, 0, 0, // OUI
1947            0x86, 0xDD, // Protocol ID
1948            // Payload
1949            0b0101, 0b10000000, 3, 4, 5,
1950        ][..]);
1951    }
1952
1953    #[fuchsia::test(allow_stalls = false)]
1954    async fn client_send_data_frame_from_ds() {
1955        let payload = vec![5; 8];
1956        let mut m = MockObjects::new().await;
1957        let mut me = m.make_mlme().await;
1958        me.make_client_station();
1959        let mut client = me.get_bound_client().expect("client should be present");
1960        client
1961            .send_data_frame([3; 6].into(), [4; 6].into(), false, false, 0x1234, &payload[..], None)
1962            .expect("error delivering WLAN frame");
1963        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1964        #[rustfmt::skip]
1965        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
1966            // Data header:
1967            0b0000_10_00, 0b000000_11, // FC (ToDS=1, FromDS=1)
1968            0, 0, // Duration
1969            6, 6, 6, 6, 6, 6, // addr1
1970            7, 7, 7, 7, 7, 7, // addr2 = IFACE_MAC
1971            4, 4, 4, 4, 4, 4, // addr3
1972            0x10, 0, // Sequence Control
1973            3, 3, 3, 3, 3, 3, // addr4
1974            // LLC header:
1975            0xAA, 0xAA, 0x03, // DSAP, SSAP, Control
1976            0, 0, 0, // OUI
1977            0x12, 0x34, // Protocol ID
1978            // Payload
1979            5, 5, 5, 5, 5, 5, 5, 5,
1980        ][..]);
1981    }
1982
1983    #[fuchsia::test(allow_stalls = false)]
1984    async fn client_send_deauthentication_notification() {
1985        let mut m = MockObjects::new().await;
1986        let mut me = m.make_mlme().await;
1987        me.make_client_station();
1988        let mut client = me.get_bound_client().expect("client should be present");
1989
1990        client
1991            .send_deauth_frame(fidl_ieee80211::ReasonCode::ApInitiated.into())
1992            .expect("error delivering WLAN frame");
1993        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
1994        #[rustfmt::skip]
1995        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
1996            // Mgmt header:
1997            0b1100_00_00, 0b00000000, // FC
1998            0, 0, // Duration
1999            6, 6, 6, 6, 6, 6, // addr1
2000            7, 7, 7, 7, 7, 7, // addr2
2001            6, 6, 6, 6, 6, 6, // addr3
2002            0x10, 0, // Sequence Control
2003            47, 0, // reason code
2004        ][..]);
2005    }
2006
2007    fn mock_rx_info<'a>(client: &BoundClient<'a, FakeDevice>) -> fidl_softmac::WlanRxInfo {
2008        let channel = client.channel_state.get_main_channel().unwrap();
2009        MockWlanRxInfo::with_channel(channel).into()
2010    }
2011
2012    #[fuchsia::test(allow_stalls = false)]
2013    async fn respond_to_keep_alive_request() {
2014        #[rustfmt::skip]
2015        let data_frame = vec![
2016            // Data header:
2017            0b0100_10_00, 0b000000_1_0, // FC
2018            0, 0, // Duration
2019            7, 7, 7, 7, 7, 7, // addr1
2020            6, 6, 6, 6, 6, 6, // addr2
2021            42, 42, 42, 42, 42, 42, // addr3
2022            0x10, 0, // Sequence Control
2023        ];
2024        let mut m = MockObjects::new().await;
2025        let mut me = m.make_mlme().await;
2026        me.make_client_station();
2027        let mut client = me.get_bound_client().expect("client should be present");
2028        client.move_to_associated_state();
2029
2030        client.on_mac_frame(&data_frame[..], mock_rx_info(&client), 0.into()).await;
2031
2032        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
2033        #[rustfmt::skip]
2034        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
2035            // Data header:
2036            0b0100_10_00, 0b0000000_1, // FC
2037            0, 0, // Duration
2038            6, 6, 6, 6, 6, 6, // addr1
2039            7, 7, 7, 7, 7, 7, // addr2
2040            6, 6, 6, 6, 6, 6, // addr3
2041            0x10, 0, // Sequence Control
2042        ][..]);
2043    }
2044
2045    #[fuchsia::test(allow_stalls = false)]
2046    async fn data_frame_to_ethernet_single_llc() {
2047        let mut data_frame = make_data_frame_single_llc(None, None);
2048        data_frame[1] = 0b00000010; // from_ds = 1, to_ds = 0 when AP sends to client (us)
2049        data_frame[4..10].copy_from_slice(IFACE_MAC.as_array()); // addr1 - receiver - client (us)
2050        data_frame[10..16].copy_from_slice(BSSID.as_array()); // addr2 - bssid
2051
2052        let mut m = MockObjects::new().await;
2053        let mut me = m.make_mlme().await;
2054        me.make_client_station();
2055        let mut client = me.get_bound_client().expect("client should be present");
2056        client.move_to_associated_state();
2057
2058        client.on_mac_frame(&data_frame[..], mock_rx_info(&client), 0.into()).await;
2059
2060        assert_eq!(m.fake_device_state.lock().eth_queue.len(), 1);
2061        #[rustfmt::skip]
2062        assert_eq!(m.fake_device_state.lock().eth_queue[0], [
2063            7, 7, 7, 7, 7, 7, // dst_addr
2064            5, 5, 5, 5, 5, 5, // src_addr
2065            9, 10, // ether_type
2066            11, 11, 11, // payload
2067        ]);
2068    }
2069
2070    #[fuchsia::test(allow_stalls = false)]
2071    async fn data_frame_to_ethernet_amsdu() {
2072        let mut data_frame = make_data_frame_amsdu();
2073        data_frame[1] = 0b00000010; // from_ds = 1, to_ds = 0 when AP sends to client (us)
2074        data_frame[4..10].copy_from_slice(IFACE_MAC.as_array()); // addr1 - receiver - client (us)
2075        data_frame[10..16].copy_from_slice(BSSID.as_array()); // addr2 - bssid
2076
2077        let mut m = MockObjects::new().await;
2078        let mut me = m.make_mlme().await;
2079        me.make_client_station();
2080        let mut client = me.get_bound_client().expect("client should be present");
2081        client.move_to_associated_state();
2082
2083        client.on_mac_frame(&data_frame[..], mock_rx_info(&client), 0.into()).await;
2084
2085        let queue = &m.fake_device_state.lock().eth_queue;
2086        assert_eq!(queue.len(), 2);
2087        #[rustfmt::skip]
2088        let mut expected_first_eth_frame = vec![
2089            0x78, 0x8a, 0x20, 0x0d, 0x67, 0x03, // dst_addr
2090            0xb4, 0xf7, 0xa1, 0xbe, 0xb9, 0xab, // src_addr
2091            0x08, 0x00, // ether_type
2092        ];
2093        expected_first_eth_frame.extend_from_slice(MSDU_1_PAYLOAD);
2094        assert_eq!(queue[0], &expected_first_eth_frame[..]);
2095        #[rustfmt::skip]
2096        let mut expected_second_eth_frame = vec![
2097            0x78, 0x8a, 0x20, 0x0d, 0x67, 0x04, // dst_addr
2098            0xb4, 0xf7, 0xa1, 0xbe, 0xb9, 0xac, // src_addr
2099            0x08, 0x01, // ether_type
2100        ];
2101        expected_second_eth_frame.extend_from_slice(MSDU_2_PAYLOAD);
2102        assert_eq!(queue[1], &expected_second_eth_frame[..]);
2103    }
2104
2105    #[fuchsia::test(allow_stalls = false)]
2106    async fn data_frame_to_ethernet_amsdu_padding_too_short() {
2107        let mut data_frame = make_data_frame_amsdu_padding_too_short();
2108        data_frame[1] = 0b00000010; // from_ds = 1, to_ds = 0 when AP sends to client (us)
2109        data_frame[4..10].copy_from_slice(IFACE_MAC.as_array()); // addr1 - receiver - client (us)
2110        data_frame[10..16].copy_from_slice(BSSID.as_array()); // addr2 - bssid
2111
2112        let mut m = MockObjects::new().await;
2113        let mut me = m.make_mlme().await;
2114        me.make_client_station();
2115        let mut client = me.get_bound_client().expect("client should be present");
2116        client.move_to_associated_state();
2117
2118        client.on_mac_frame(&data_frame[..], mock_rx_info(&client), 0.into()).await;
2119
2120        let queue = &m.fake_device_state.lock().eth_queue;
2121        assert_eq!(queue.len(), 1);
2122        #[rustfmt::skip]
2123            let mut expected_first_eth_frame = vec![
2124            0x78, 0x8a, 0x20, 0x0d, 0x67, 0x03, // dst_addr
2125            0xb4, 0xf7, 0xa1, 0xbe, 0xb9, 0xab, // src_addr
2126            0x08, 0x00, // ether_type
2127        ];
2128        expected_first_eth_frame.extend_from_slice(MSDU_1_PAYLOAD);
2129        assert_eq!(queue[0], &expected_first_eth_frame[..]);
2130    }
2131
2132    #[fuchsia::test(allow_stalls = false)]
2133    async fn data_frame_controlled_port_closed() {
2134        let mut data_frame = make_data_frame_single_llc(None, None);
2135        data_frame[1] = 0b00000010; // from_ds = 1, to_ds = 0 when AP sends to client (us)
2136        data_frame[4..10].copy_from_slice(IFACE_MAC.as_array()); // addr1 - receiver - client (us)
2137        data_frame[10..16].copy_from_slice(BSSID.as_array()); // addr2 - bssid
2138
2139        let mut m = MockObjects::new().await;
2140        let mut me = m.make_mlme().await;
2141        me.make_client_station_protected();
2142        let mut client = me.get_bound_client().expect("client should be present");
2143        client.move_to_associated_state();
2144        client.close_controlled_port().await;
2145
2146        client.on_mac_frame(&data_frame[..], mock_rx_info(&client), 0.into()).await;
2147
2148        // Verify frame was not sent to netstack.
2149        assert_eq!(m.fake_device_state.lock().eth_queue.len(), 0);
2150    }
2151
2152    #[fuchsia::test(allow_stalls = false)]
2153    async fn eapol_frame_controlled_port_closed() {
2154        let (src_addr, dst_addr, mut eapol_frame) = make_eapol_frame(*IFACE_MAC);
2155        eapol_frame[1] = 0b00000010; // from_ds = 1, to_ds = 0 when AP sends to client (us)
2156        eapol_frame[4..10].copy_from_slice(IFACE_MAC.as_array()); // addr1 - receiver - client (us)
2157        eapol_frame[10..16].copy_from_slice(BSSID.as_array()); // addr2 - bssid
2158
2159        let mut m = MockObjects::new().await;
2160        let mut me = m.make_mlme().await;
2161        me.make_client_station_protected();
2162        let mut client = me.get_bound_client().expect("client should be present");
2163        client.move_to_associated_state();
2164        client.close_controlled_port().await;
2165
2166        client.on_mac_frame(&eapol_frame[..], mock_rx_info(&client), 0.into()).await;
2167
2168        // Verify EAPoL frame was not sent to netstack.
2169        assert_eq!(m.fake_device_state.lock().eth_queue.len(), 0);
2170
2171        // Verify EAPoL frame was sent to SME.
2172        let eapol_ind = m
2173            .fake_device_state
2174            .lock()
2175            .next_mlme_msg::<fidl_mlme::EapolIndication>()
2176            .expect("error reading EAPOL.indication");
2177        assert_eq!(
2178            eapol_ind,
2179            fidl_mlme::EapolIndication {
2180                src_addr: src_addr.to_array(),
2181                dst_addr: dst_addr.to_array(),
2182                data: EAPOL_PDU.to_vec()
2183            }
2184        );
2185    }
2186
2187    #[fuchsia::test(allow_stalls = false)]
2188    async fn eapol_frame_is_controlled_port_open() {
2189        let (src_addr, dst_addr, mut eapol_frame) = make_eapol_frame(*IFACE_MAC);
2190        eapol_frame[1] = 0b00000010; // from_ds = 1, to_ds = 0 when AP sends to client (us)
2191        eapol_frame[4..10].copy_from_slice(IFACE_MAC.as_array()); // addr1 - receiver - client (us)
2192        eapol_frame[10..16].copy_from_slice(BSSID.as_array()); // addr2 - bssid
2193
2194        let mut m = MockObjects::new().await;
2195        let mut me = m.make_mlme().await;
2196        me.make_client_station();
2197        let mut client = me.get_bound_client().expect("client should be present");
2198        client.move_to_associated_state();
2199
2200        client.on_mac_frame(&eapol_frame[..], mock_rx_info(&client), 0.into()).await;
2201
2202        // Verify EAPoL frame was not sent to netstack.
2203        assert_eq!(m.fake_device_state.lock().eth_queue.len(), 0);
2204
2205        // Verify EAPoL frame was sent to SME.
2206        let eapol_ind = m
2207            .fake_device_state
2208            .lock()
2209            .next_mlme_msg::<fidl_mlme::EapolIndication>()
2210            .expect("error reading EAPOL.indication");
2211        assert_eq!(
2212            eapol_ind,
2213            fidl_mlme::EapolIndication {
2214                src_addr: src_addr.to_array(),
2215                dst_addr: dst_addr.to_array(),
2216                data: EAPOL_PDU.to_vec()
2217            }
2218        );
2219    }
2220
2221    #[fuchsia::test(allow_stalls = false)]
2222    async fn send_eapol_ind_success() {
2223        let mut m = MockObjects::new().await;
2224        let mut me = m.make_mlme().await;
2225        me.make_client_station();
2226        let mut client = me.get_bound_client().expect("client should be present");
2227        client
2228            .send_eapol_indication([1; 6].into(), [2; 6].into(), &[5; 200])
2229            .expect("expected EAPOL.indication to be sent");
2230        let eapol_ind = m
2231            .fake_device_state
2232            .lock()
2233            .next_mlme_msg::<fidl_mlme::EapolIndication>()
2234            .expect("error reading EAPOL.indication");
2235        assert_eq!(
2236            eapol_ind,
2237            fidl_mlme::EapolIndication {
2238                src_addr: [1; 6].into(),
2239                dst_addr: [2; 6].into(),
2240                data: vec![5; 200]
2241            }
2242        );
2243    }
2244
2245    #[fuchsia::test(allow_stalls = false)]
2246    async fn send_eapol_frame_success() {
2247        let mut m = MockObjects::new().await;
2248        let mut me = m.make_mlme().await;
2249        me.make_client_station();
2250        let mut client = me.get_bound_client().expect("client should be present");
2251        client.send_eapol_frame(*IFACE_MAC, (*BSSID).into(), false, &[5; 8]);
2252
2253        // Verify EAPOL.confirm message was sent to SME.
2254        let eapol_confirm = m
2255            .fake_device_state
2256            .lock()
2257            .next_mlme_msg::<fidl_mlme::EapolConfirm>()
2258            .expect("error reading EAPOL.confirm");
2259        assert_eq!(
2260            eapol_confirm,
2261            fidl_mlme::EapolConfirm {
2262                result_code: fidl_mlme::EapolResultCode::Success,
2263                dst_addr: BSSID.to_array(),
2264            }
2265        );
2266
2267        // Verify EAPoL frame was sent over the air.
2268        #[rustfmt::skip]
2269        assert_eq!(&m.fake_device_state.lock().wlan_queue[0].0[..], &[
2270            // Data header:
2271            0b0000_10_00, 0b0000000_1, // FC
2272            0, 0, // Duration
2273            6, 6, 6, 6, 6, 6, // addr1
2274            7, 7, 7, 7, 7, 7, // addr2
2275            6, 6, 6, 6, 6, 6, // addr3
2276            0x10, 0, // Sequence Control
2277            // LLC header:
2278            0xaa, 0xaa, 0x03, // dsap ssap ctrl
2279            0x00, 0x00, 0x00, // oui
2280            0x88, 0x8E, // protocol id (EAPOL)
2281            // EAPoL PDU:
2282            5, 5, 5, 5, 5, 5, 5, 5,
2283        ][..]);
2284    }
2285
2286    #[fuchsia::test(allow_stalls = false)]
2287    async fn send_eapol_frame_failure() {
2288        let mut m = MockObjects::new().await;
2289        m.fake_device_state.lock().config.send_wlan_frame_fails = true;
2290        let mut me = m.make_mlme().await;
2291        me.make_client_station();
2292        let mut client = me.get_bound_client().expect("client should be present");
2293        client.send_eapol_frame([1; 6].into(), [2; 6].into(), false, &[5; 200]);
2294
2295        // Verify EAPOL.confirm message was sent to SME.
2296        let eapol_confirm = m
2297            .fake_device_state
2298            .lock()
2299            .next_mlme_msg::<fidl_mlme::EapolConfirm>()
2300            .expect("error reading EAPOL.confirm");
2301        assert_eq!(
2302            eapol_confirm,
2303            fidl_mlme::EapolConfirm {
2304                result_code: fidl_mlme::EapolResultCode::TransmissionFailure,
2305                dst_addr: [2; 6].into(),
2306            }
2307        );
2308
2309        // Verify EAPoL frame was not sent over the air.
2310        assert!(m.fake_device_state.lock().wlan_queue.is_empty());
2311    }
2312
2313    #[fuchsia::test(allow_stalls = false)]
2314    async fn send_keys() {
2315        let mut m = MockObjects::new().await;
2316        let mut me = m.make_mlme().await;
2317        me.make_client_station_protected();
2318        let mut client = me.get_bound_client().expect("client should be present");
2319        client.move_to_associated_state();
2320
2321        assert!(m.fake_device_state.lock().keys.is_empty());
2322        client.handle_mlme_req(crate::test_utils::fake_set_keys_req((*BSSID).into())).await;
2323        assert_eq!(m.fake_device_state.lock().keys.len(), 1);
2324
2325        let sent_key = crate::test_utils::fake_key((*BSSID).into());
2326        let received_key = &m.fake_device_state.lock().keys[0];
2327        assert_eq!(received_key.key, Some(sent_key.key));
2328        assert_eq!(received_key.key_idx, Some(sent_key.key_id as u8));
2329        assert_eq!(received_key.key_type, Some(fidl_ieee80211::KeyType::Pairwise));
2330    }
2331
2332    #[fuchsia::test(allow_stalls = false)]
2333    async fn send_addba_req_frame() {
2334        let mut mock = MockObjects::new().await;
2335        let mut mlme = mock.make_mlme().await;
2336        mlme.make_client_station();
2337        let mut client = mlme.get_bound_client().expect("client should be present");
2338
2339        let mut body = [0u8; 16];
2340        let mut writer = BufferWriter::new(&mut body[..]);
2341        block_ack::write_addba_req_body(&mut writer, 1).expect("failed writing addba frame");
2342        client
2343            .send_block_ack_frame(ADDBA_REQ_FRAME_LEN, writer.into_written())
2344            .expect("failed sending addba frame");
2345        assert_eq!(
2346            &mock.fake_device_state.lock().wlan_queue[0].0[..],
2347            &[
2348                // Mgmt header 1101 for action frame
2349                0b11010000, 0b00000000, // frame control
2350                0, 0, // duration
2351                6, 6, 6, 6, 6, 6, // addr1
2352                7, 7, 7, 7, 7, 7, // addr2
2353                6, 6, 6, 6, 6, 6, // addr3
2354                0x10, 0, // sequence control
2355                // Action frame header (Also part of ADDBA request frame)
2356                0x03, // Action Category: block ack (0x03)
2357                0x00, // block ack action: ADDBA request (0x00)
2358                1,    // block ack dialog token
2359                0b00000011, 0b00010000, // block ack parameters (u16)
2360                0, 0, // block ack timeout (u16) (0: disabled)
2361                0b00010000, 0, // block ack starting sequence number: fragment 0, sequence 1
2362            ][..]
2363        );
2364    }
2365
2366    #[fuchsia::test(allow_stalls = false)]
2367    async fn send_addba_resp_frame() {
2368        let mut mock = MockObjects::new().await;
2369        let mut mlme = mock.make_mlme().await;
2370        mlme.make_client_station();
2371        let mut client = mlme.get_bound_client().expect("client should be present");
2372
2373        let mut body = [0u8; 16];
2374        let mut writer = BufferWriter::new(&mut body[..]);
2375        block_ack::write_addba_resp_body(&mut writer, 1).expect("failed writing addba frame");
2376        client
2377            .send_block_ack_frame(ADDBA_RESP_FRAME_LEN, writer.into_written())
2378            .expect("failed sending addba frame");
2379        assert_eq!(
2380            &mock.fake_device_state.lock().wlan_queue[0].0[..],
2381            &[
2382                // Mgmt header 1101 for action frame
2383                0b11010000, 0b00000000, // frame control
2384                0, 0, // duration
2385                6, 6, 6, 6, 6, 6, // addr1
2386                7, 7, 7, 7, 7, 7, // addr2
2387                6, 6, 6, 6, 6, 6, // addr3
2388                0x10, 0, // sequence control
2389                // Action frame header (Also part of ADDBA response frame)
2390                0x03, // Action Category: block ack (0x03)
2391                0x01, // block ack action: ADDBA response (0x01)
2392                1,    // block ack dialog token
2393                0, 0, // status
2394                0b00000011, 0b00010000, // block ack parameters (u16)
2395                0, 0, // block ack timeout (u16) (0: disabled)
2396            ][..]
2397        );
2398    }
2399
2400    #[fuchsia::test(allow_stalls = false)]
2401    async fn client_send_successful_connect_conf() {
2402        let mut m = MockObjects::new().await;
2403        let mut me = m.make_mlme().await;
2404        me.make_client_station();
2405        let mut client = me.get_bound_client().expect("client should be present");
2406
2407        client.send_connect_conf_success(42, &[0, 5, 3, 4, 5, 6, 7][..]);
2408        let connect_conf = m
2409            .fake_device_state
2410            .lock()
2411            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
2412            .expect("error reading Connect.confirm");
2413        assert_eq!(
2414            connect_conf,
2415            fidl_mlme::ConnectConfirm {
2416                peer_sta_address: BSSID.to_array(),
2417                result_code: fidl_ieee80211::StatusCode::Success,
2418                association_id: 42,
2419                association_ies: vec![0, 5, 3, 4, 5, 6, 7],
2420            }
2421        );
2422    }
2423
2424    #[fuchsia::test(allow_stalls = false)]
2425    async fn client_send_failed_connect_conf() {
2426        let mut m = MockObjects::new().await;
2427        let mut me = m.make_mlme().await;
2428        me.make_client_station();
2429        let mut client = me.get_bound_client().expect("client should be present");
2430        client.send_connect_conf_failure(fidl_ieee80211::StatusCode::DeniedNoMoreStas);
2431        let connect_conf = m
2432            .fake_device_state
2433            .lock()
2434            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
2435            .expect("error reading Connect.confirm");
2436        assert_eq!(
2437            connect_conf,
2438            fidl_mlme::ConnectConfirm {
2439                peer_sta_address: BSSID.to_array(),
2440                result_code: fidl_ieee80211::StatusCode::DeniedNoMoreStas,
2441                association_id: 0,
2442                association_ies: vec![],
2443            }
2444        );
2445    }
2446
2447    #[fuchsia::test(allow_stalls = false)]
2448    async fn client_send_scan_end_on_mlme_scan_busy() {
2449        let mut m = MockObjects::new().await;
2450        let mut me = m.make_mlme().await;
2451        me.make_client_station();
2452
2453        // Issue a second scan before the first finishes
2454        me.on_sme_scan(scan_req()).await;
2455        me.on_sme_scan(fidl_mlme::ScanRequest { txn_id: 1338, ..scan_req() }).await;
2456
2457        let scan_end = m
2458            .fake_device_state
2459            .lock()
2460            .next_mlme_msg::<fidl_mlme::ScanEnd>()
2461            .expect("error reading MLME ScanEnd");
2462        assert_eq!(
2463            scan_end,
2464            fidl_mlme::ScanEnd { txn_id: 1338, code: fidl_mlme::ScanResultCode::NotSupported }
2465        );
2466    }
2467
2468    #[fuchsia::test(allow_stalls = false)]
2469    async fn client_send_scan_end_on_scan_busy() {
2470        let mut m = MockObjects::new().await;
2471        let mut me = m.make_mlme().await;
2472        me.make_client_station();
2473
2474        // Issue a second scan before the first finishes
2475        me.on_sme_scan(scan_req()).await;
2476        me.on_sme_scan(fidl_mlme::ScanRequest { txn_id: 1338, ..scan_req() }).await;
2477
2478        let scan_end = m
2479            .fake_device_state
2480            .lock()
2481            .next_mlme_msg::<fidl_mlme::ScanEnd>()
2482            .expect("error reading MLME ScanEnd");
2483        assert_eq!(
2484            scan_end,
2485            fidl_mlme::ScanEnd { txn_id: 1338, code: fidl_mlme::ScanResultCode::NotSupported }
2486        );
2487    }
2488
2489    #[fuchsia::test(allow_stalls = false)]
2490    async fn client_send_scan_end_on_mlme_scan_invalid_args() {
2491        let mut m = MockObjects::new().await;
2492        let mut me = m.make_mlme().await;
2493
2494        me.make_client_station();
2495        me.on_sme_scan(fidl_mlme::ScanRequest {
2496            txn_id: 1337,
2497            scan_type: fidl_mlme::ScanTypes::Passive,
2498            channel_list: vec![], // empty channel list
2499            ssid_list: vec![Ssid::try_from("ssid").unwrap().into()],
2500            probe_delay: 0,
2501            min_channel_time: 100,
2502            max_channel_time: 300,
2503        })
2504        .await;
2505        let scan_end = m
2506            .fake_device_state
2507            .lock()
2508            .next_mlme_msg::<fidl_mlme::ScanEnd>()
2509            .expect("error reading MLME ScanEnd");
2510        assert_eq!(
2511            scan_end,
2512            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::InvalidArgs }
2513        );
2514    }
2515
2516    #[fuchsia::test(allow_stalls = false)]
2517    async fn client_send_scan_end_on_scan_invalid_args() {
2518        let mut m = MockObjects::new().await;
2519        let mut me = m.make_mlme().await;
2520
2521        me.make_client_station();
2522        me.on_sme_scan(fidl_mlme::ScanRequest {
2523            txn_id: 1337,
2524            scan_type: fidl_mlme::ScanTypes::Passive,
2525            channel_list: vec![6],
2526            ssid_list: vec![Ssid::try_from("ssid").unwrap().into()],
2527            probe_delay: 0,
2528            min_channel_time: 300, // min > max
2529            max_channel_time: 100,
2530        })
2531        .await;
2532        let scan_end = m
2533            .fake_device_state
2534            .lock()
2535            .next_mlme_msg::<fidl_mlme::ScanEnd>()
2536            .expect("error reading MLME ScanEnd");
2537        assert_eq!(
2538            scan_end,
2539            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::InvalidArgs }
2540        );
2541    }
2542
2543    #[fuchsia::test(allow_stalls = false)]
2544    async fn client_send_scan_end_on_passive_scan_fails() {
2545        let mut m = MockObjects::new().await;
2546        m.fake_device_state.lock().config.start_passive_scan_fails = true;
2547        let mut me = m.make_mlme().await;
2548
2549        me.make_client_station();
2550        me.on_sme_scan(scan_req()).await;
2551        let scan_end = m
2552            .fake_device_state
2553            .lock()
2554            .next_mlme_msg::<fidl_mlme::ScanEnd>()
2555            .expect("error reading MLME ScanEnd");
2556        assert_eq!(
2557            scan_end,
2558            fidl_mlme::ScanEnd { txn_id: 1337, code: fidl_mlme::ScanResultCode::NotSupported }
2559        );
2560    }
2561
2562    #[fuchsia::test(allow_stalls = false)]
2563    async fn mlme_respond_to_query_device_info() {
2564        let mut mock_objects = MockObjects::new().await;
2565        let mut mlme = mock_objects.make_mlme().await;
2566
2567        let (responder, receiver) = Responder::new();
2568        mlme.handle_mlme_req(wlan_sme::MlmeRequest::QueryDeviceInfo(responder))
2569            .await
2570            .expect("Failed to send MlmeRequest::Connect");
2571        assert_eq!(
2572            receiver.await.unwrap(),
2573            fidl_mlme::DeviceInfo {
2574                sta_addr: IFACE_MAC.to_array(),
2575                role: fidl_common::WlanMacRole::Client,
2576                bands: test_utils::fake_mlme_band_caps(),
2577                softmac_hardware_capability: 0,
2578                qos_capable: false,
2579            }
2580        );
2581    }
2582
2583    #[fuchsia::test(allow_stalls = false)]
2584    async fn mlme_respond_to_query_discovery_support() {
2585        let mut m = MockObjects::new().await;
2586        let mut me = m.make_mlme().await;
2587
2588        let (responder, receiver) = Responder::new();
2589        me.handle_mlme_req(wlan_sme::MlmeRequest::QueryDiscoverySupport(responder))
2590            .await
2591            .expect("Failed to send MlmeRequest::Connect");
2592        let resp = receiver.await.unwrap();
2593        assert_eq!(resp.scan_offload.supported, true);
2594        assert_eq!(resp.probe_response_offload.supported, false);
2595    }
2596
2597    #[fuchsia::test(allow_stalls = false)]
2598    async fn mlme_respond_to_query_mac_sublayer_support() {
2599        let mut m = MockObjects::new().await;
2600        let mut me = m.make_mlme().await;
2601
2602        let (responder, receiver) = Responder::new();
2603        me.handle_mlme_req(wlan_sme::MlmeRequest::QueryMacSublayerSupport(responder))
2604            .await
2605            .expect("Failed to send MlmeRequest::Connect");
2606        let resp = receiver.await.unwrap();
2607        assert_eq!(resp.rate_selection_offload.supported, false);
2608        assert_eq!(resp.data_plane.data_plane_type, fidl_common::DataPlaneType::EthernetDevice);
2609        assert_eq!(resp.device.is_synthetic, true);
2610        assert_eq!(
2611            resp.device.mac_implementation_type,
2612            fidl_common::MacImplementationType::Softmac
2613        );
2614        assert_eq!(resp.device.tx_status_report_supported, true);
2615    }
2616
2617    #[fuchsia::test(allow_stalls = false)]
2618    async fn mlme_respond_to_query_security_support() {
2619        let mut m = MockObjects::new().await;
2620        let mut me = m.make_mlme().await;
2621
2622        let (responder, receiver) = Responder::new();
2623        assert_variant!(
2624            me.handle_mlme_req(wlan_sme::MlmeRequest::QuerySecuritySupport(responder)).await,
2625            Ok(())
2626        );
2627        let resp = receiver.await.unwrap();
2628        assert_eq!(resp.mfp.supported, false);
2629        assert_eq!(resp.sae.driver_handler_supported, false);
2630        assert_eq!(resp.sae.sme_handler_supported, false);
2631    }
2632
2633    #[fuchsia::test(allow_stalls = false)]
2634    async fn mlme_respond_to_query_spectrum_management_support() {
2635        let mut m = MockObjects::new().await;
2636        let mut me = m.make_mlme().await;
2637
2638        let (responder, receiver) = Responder::new();
2639        me.handle_mlme_req(wlan_sme::MlmeRequest::QuerySpectrumManagementSupport(responder))
2640            .await
2641            .expect("Failed to send MlmeRequest::QuerySpectrumManagementSupport");
2642        assert_eq!(receiver.await.unwrap().dfs.supported, true);
2643    }
2644
2645    #[fuchsia::test(allow_stalls = false)]
2646    async fn mlme_connect_unprotected_happy_path() {
2647        let mut m = MockObjects::new().await;
2648        let mut me = m.make_mlme().await;
2649        let channel = Channel::new(6, Cbw::Cbw40);
2650        let connect_req = fidl_mlme::ConnectRequest {
2651            selected_bss: fake_fidl_bss_description!(Open,
2652                ssid: Ssid::try_from("ssid").unwrap().into(),
2653                bssid: BSSID.to_array(),
2654                channel: channel.clone(),
2655            ),
2656            connect_failure_timeout: 100,
2657            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
2658            sae_password: vec![],
2659            wep_key: None,
2660            security_ie: vec![],
2661        };
2662        me.handle_mlme_req(wlan_sme::MlmeRequest::Connect(connect_req))
2663            .await
2664            .expect("Failed to send MlmeRequest::Connect");
2665
2666        // Verify an event was queued up in the timer.
2667        assert_variant!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
2668            assert_eq!(ids.len(), 1);
2669        });
2670
2671        // Verify authentication frame was sent to AP.
2672        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
2673        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
2674        #[rustfmt::skip]
2675        let expected = vec![
2676            // Mgmt Header:
2677            0b1011_00_00, 0b00000000, // Frame Control
2678            0, 0, // Duration
2679            6, 6, 6, 6, 6, 6, // Addr1
2680            7, 7, 7, 7, 7, 7, // Addr2
2681            6, 6, 6, 6, 6, 6, // Addr3
2682            0x10, 0, // Sequence Control
2683            // Auth Header:
2684            0, 0, // Algorithm Number (Open)
2685            1, 0, // Txn Sequence Number
2686            0, 0, // Status Code
2687        ];
2688        assert_eq!(&frame[..], &expected[..]);
2689
2690        // Mock auth frame response from the AP
2691        #[rustfmt::skip]
2692        let auth_resp_success = vec![
2693            // Mgmt Header:
2694            0b1011_00_00, 0b00000000, // Frame Control
2695            0, 0, // Duration
2696            7, 7, 7, 7, 7, 7, // Addr1
2697            7, 7, 7, 7, 7, 7, // Addr2
2698            6, 6, 6, 6, 6, 6, // Addr3
2699            0x10, 0, // Sequence Control
2700            // Auth Header:
2701            0, 0, // Algorithm Number (Open)
2702            2, 0, // Txn Sequence Number
2703            0, 0, // Status Code
2704        ];
2705        me.on_mac_frame_rx(
2706            &auth_resp_success[..],
2707            MockWlanRxInfo::with_channel(channel.into()).into(),
2708            0.into(),
2709        )
2710        .await;
2711
2712        // Verify association request frame was went to AP
2713        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
2714        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
2715        #[rustfmt::skip]
2716        let expected = vec![
2717            // Mgmt header:
2718            0, 0, // FC
2719            0, 0, // Duration
2720            6, 6, 6, 6, 6, 6, // addr1
2721            7, 7, 7, 7, 7, 7, // addr2
2722            6, 6, 6, 6, 6, 6, // addr3
2723            0x20, 0, // Sequence Control
2724            // Association Request header:
2725            0x01, 0x00, // capability info
2726            0, 0, // listen interval
2727            // IEs
2728            0, 4, // SSID id and length
2729            0x73, 0x73, 0x69, 0x64, // SSID
2730            1, 8, // supp rates id and length
2731            2, 4, 11, 22, 12, 18, 24, 36, // supp rates
2732            50, 4, // ext supp rates and length
2733            48, 72, 96, 108, // ext supp rates
2734            45, 26, // HT Cap id and length
2735            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
2736            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
2737            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
2738        ];
2739        assert_eq!(&frame[..], &expected[..]);
2740
2741        // Mock assoc resp frame from the AP
2742        #[rustfmt::skip]
2743        let assoc_resp_success = vec![
2744            // Mgmt Header:
2745            0b0001_00_00, 0b00000000, // Frame Control
2746            0, 0, // Duration
2747            7, 7, 7, 7, 7, 7, // Addr1 == IFACE_MAC
2748            7, 7, 7, 7, 7, 7, // Addr2
2749            6, 6, 6, 6, 6, 6, // Addr3
2750            0x20, 0, // Sequence Control
2751            // Assoc Resp Header:
2752            0, 0, // Capabilities
2753            0, 0, // Status Code
2754            42, 0, // AID
2755            // IEs
2756            // Basic Rates
2757            0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
2758            // HT Capabilities
2759            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
2760            0x17, // A-MPDU parameters
2761            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2762            // VHT Capabilities
2763            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
2764            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
2765        ];
2766        me.on_mac_frame_rx(
2767            &assoc_resp_success[..],
2768            MockWlanRxInfo::with_channel(channel.into()).into(),
2769            0.into(),
2770        )
2771        .await;
2772
2773        // Verify a successful connect conf is sent
2774        let msg = m
2775            .fake_device_state
2776            .lock()
2777            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
2778            .expect("expect ConnectConf");
2779        assert_eq!(
2780            msg,
2781            fidl_mlme::ConnectConfirm {
2782                peer_sta_address: BSSID.to_array(),
2783                result_code: fidl_ieee80211::StatusCode::Success,
2784                association_id: 42,
2785                association_ies: vec![
2786                    // IEs
2787                    // Basic Rates
2788                    0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
2789                    // HT Capabilities
2790                    0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
2791                    0x17, // A-MPDU parameters
2792                    0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2793                    0x00, 0x00, // VHT Capabilities
2794                    0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
2795                    0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
2796                ],
2797            }
2798        );
2799
2800        // Verify eth link is up
2801        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::UP);
2802    }
2803
2804    #[fuchsia::test(allow_stalls = false)]
2805    async fn mlme_connect_protected_happy_path() {
2806        let mut m = MockObjects::new().await;
2807        let mut me = m.make_mlme().await;
2808        let channel = Channel::new(6, Cbw::Cbw40);
2809        let connect_req = fidl_mlme::ConnectRequest {
2810            selected_bss: fake_fidl_bss_description!(Wpa2,
2811                ssid: Ssid::try_from("ssid").unwrap().into(),
2812                bssid: BSSID.to_array(),
2813                channel: channel.clone(),
2814            ),
2815            connect_failure_timeout: 100,
2816            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
2817            sae_password: vec![],
2818            wep_key: None,
2819            security_ie: vec![
2820                48, 18, // RSNE header
2821                1, 0, // Version
2822                0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
2823                1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
2824                1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
2825            ],
2826        };
2827        me.handle_mlme_req(wlan_sme::MlmeRequest::Connect(connect_req))
2828            .await
2829            .expect("Failed to send MlmeRequest::Connect");
2830
2831        // Verify an event was queued up in the timer.
2832        assert_variant!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
2833            assert_eq!(ids.len(), 1);
2834        });
2835
2836        // Verify authentication frame was sent to AP.
2837        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
2838        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
2839        #[rustfmt::skip]
2840        let expected = vec![
2841            // Mgmt Header:
2842            0b1011_00_00, 0b00000000, // Frame Control
2843            0, 0, // Duration
2844            6, 6, 6, 6, 6, 6, // Addr1
2845            7, 7, 7, 7, 7, 7, // Addr2
2846            6, 6, 6, 6, 6, 6, // Addr3
2847            0x10, 0, // Sequence Control
2848            // Auth Header:
2849            0, 0, // Algorithm Number (Open)
2850            1, 0, // Txn Sequence Number
2851            0, 0, // Status Code
2852        ];
2853        assert_eq!(&frame[..], &expected[..]);
2854
2855        // Mock auth frame response from the AP
2856        #[rustfmt::skip]
2857        let auth_resp_success = vec![
2858            // Mgmt Header:
2859            0b1011_00_00, 0b00000000, // Frame Control
2860            0, 0, // Duration
2861            7, 7, 7, 7, 7, 7, // Addr1
2862            7, 7, 7, 7, 7, 7, // Addr2
2863            6, 6, 6, 6, 6, 6, // Addr3
2864            0x10, 0, // Sequence Control
2865            // Auth Header:
2866            0, 0, // Algorithm Number (Open)
2867            2, 0, // Txn Sequence Number
2868            0, 0, // Status Code
2869        ];
2870        me.on_mac_frame_rx(
2871            &auth_resp_success[..],
2872            MockWlanRxInfo::with_channel(channel.into()).into(),
2873            0.into(),
2874        )
2875        .await;
2876
2877        // Verify association request frame was went to AP
2878        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
2879        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
2880        #[rustfmt::skip]
2881        let expected = vec![
2882            // Mgmt header:
2883            0, 0, // FC
2884            0, 0, // Duration
2885            6, 6, 6, 6, 6, 6, // addr1
2886            7, 7, 7, 7, 7, 7, // addr2
2887            6, 6, 6, 6, 6, 6, // addr3
2888            0x20, 0, // Sequence Control
2889            // Association Request header:
2890            0x01, 0x00, // capability info
2891            0, 0, // listen interval
2892            // IEs
2893            0, 4, // SSID id and length
2894            0x73, 0x73, 0x69, 0x64, // SSID
2895            1, 8, // supp rates id and length
2896            2, 4, 11, 22, 12, 18, 24, 36, // supp rates
2897            50, 4, // ext supp rates and length
2898            48, 72, 96, 108, // ext supp rates
2899            48, 18, // RSNE id and length
2900            1, 0, // RSN \
2901            0x00, 0x0F, 0xAC, 4, // RSN \
2902            1, 0, 0x00, 0x0F, 0xAC, 4, // RSN \
2903            1, 0, 0x00, 0x0F, 0xAC, 2, // RSN
2904            45, 26, // HT Cap id and length
2905            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
2906            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
2907            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
2908        ];
2909        assert_eq!(&frame[..], &expected[..]);
2910
2911        // Mock assoc resp frame from the AP
2912        #[rustfmt::skip]
2913        let assoc_resp_success = vec![
2914            // Mgmt Header:
2915            0b0001_00_00, 0b00000000, // Frame Control
2916            0, 0, // Duration
2917            7, 7, 7, 7, 7, 7, // Addr1 == IFACE_MAC
2918            7, 7, 7, 7, 7, 7, // Addr2
2919            6, 6, 6, 6, 6, 6, // Addr3
2920            0x20, 0, // Sequence Control
2921            // Assoc Resp Header:
2922            0, 0, // Capabilities
2923            0, 0, // Status Code
2924            42, 0, // AID
2925            // IEs
2926            // Basic Rates
2927            0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24,
2928            // RSN
2929            0x30, 18, 1, 0, // RSN header and version
2930            0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
2931            1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
2932            1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
2933            // HT Capabilities
2934            0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
2935            0x17, // A-MPDU parameters
2936            0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Other HT Cap fields
2937            // VHT Capabilities
2938            0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
2939            0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
2940        ];
2941        me.on_mac_frame_rx(
2942            &assoc_resp_success[..],
2943            MockWlanRxInfo::with_channel(channel.into()).into(),
2944            0.into(),
2945        )
2946        .await;
2947
2948        // Verify a successful connect conf is sent
2949        let msg = m
2950            .fake_device_state
2951            .lock()
2952            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
2953            .expect("expect ConnectConf");
2954        assert_eq!(
2955            msg,
2956            fidl_mlme::ConnectConfirm {
2957                peer_sta_address: BSSID.to_array(),
2958                result_code: fidl_ieee80211::StatusCode::Success,
2959                association_id: 42,
2960                association_ies: vec![
2961                    // IEs
2962                    // Basic Rates
2963                    0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, // RSN
2964                    0x30, 18, 1, 0, // RSN header and version
2965                    0x00, 0x0F, 0xAC, 4, // Group Cipher: CCMP-128
2966                    1, 0, 0x00, 0x0F, 0xAC, 4, // 1 Pairwise Cipher: CCMP-128
2967                    1, 0, 0x00, 0x0F, 0xAC, 2, // 1 AKM: PSK
2968                    // HT Capabilities
2969                    0x2d, 0x1a, 0xef, 0x09, // HT capabilities info
2970                    0x17, // A-MPDU parameters
2971                    0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2972                    0x00, 0x00, // Other HT Cap fields
2973                    // VHT Capabilities
2974                    0xbf, 0x0c, 0x91, 0x59, 0x82, 0x0f, // VHT capabilities info
2975                    0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, // VHT supported MCS set
2976                ],
2977            }
2978        );
2979
2980        // Verify that link is still down
2981        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::DOWN);
2982
2983        // Send a request to open controlled port
2984        me.handle_mlme_req(wlan_sme::MlmeRequest::SetCtrlPort(
2985            fidl_mlme::SetControlledPortRequest {
2986                peer_sta_address: BSSID.to_array(),
2987                state: fidl_mlme::ControlledPortState::Open,
2988            },
2989        ))
2990        .await
2991        .expect("expect sending msg to succeed");
2992
2993        // Verify that link is now up
2994        assert_eq!(m.fake_device_state.lock().link_status, LinkStatus::UP);
2995    }
2996
2997    #[fuchsia::test(allow_stalls = false)]
2998    async fn mlme_connect_vht() {
2999        let mut m = MockObjects::new().await;
3000        let mut me = m.make_mlme().await;
3001        let channel = Channel::new(36, Cbw::Cbw40);
3002        let connect_req = fidl_mlme::ConnectRequest {
3003            selected_bss: fake_fidl_bss_description!(Open,
3004                ssid: Ssid::try_from("ssid").unwrap().into(),
3005                bssid: BSSID.to_array(),
3006                channel: channel.clone(),
3007            ),
3008            connect_failure_timeout: 100,
3009            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
3010            sae_password: vec![],
3011            wep_key: None,
3012            security_ie: vec![],
3013        };
3014        me.handle_mlme_req(wlan_sme::MlmeRequest::Connect(connect_req))
3015            .await
3016            .expect("Failed to send MlmeRequest::Connect.");
3017
3018        // Verify an event was queued up in the timer.
3019        assert_variant!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(ids) => {
3020            assert_eq!(ids.len(), 1);
3021        });
3022
3023        // Auth frame
3024        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
3025        let (_frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
3026
3027        // Mock auth frame response from the AP
3028        #[rustfmt::skip]
3029        let auth_resp_success = vec![
3030            // Mgmt Header:
3031            0b1011_00_00, 0b00000000, // Frame Control
3032            0, 0, // Duration
3033            7, 7, 7, 7, 7, 7, // Addr1
3034            7, 7, 7, 7, 7, 7, // Addr2
3035            6, 6, 6, 6, 6, 6, // Addr3
3036            0x10, 0, // Sequence Control
3037            // Auth Header:
3038            0, 0, // Algorithm Number (Open)
3039            2, 0, // Txn Sequence Number
3040            0, 0, // Status Code
3041        ];
3042        me.on_mac_frame_rx(
3043            &auth_resp_success[..],
3044            MockWlanRxInfo::with_channel(channel.into()).into(),
3045            0.into(),
3046        )
3047        .await;
3048
3049        // Verify association request frame was went to AP
3050        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
3051        let (frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
3052        #[rustfmt::skip]
3053        let expected = vec![
3054            // Mgmt header:
3055            0, 0, // FC
3056            0, 0, // Duration
3057            6, 6, 6, 6, 6, 6, // addr1
3058            7, 7, 7, 7, 7, 7, // addr2
3059            6, 6, 6, 6, 6, 6, // addr3
3060            0x20, 0, // Sequence Control
3061            // Association Request header:
3062            0x01, 0x00, // capability info
3063            0, 0, // listen interval
3064            // IEs
3065            0, 4, // SSID id and length
3066            0x73, 0x73, 0x69, 0x64, // SSID
3067            1, 6, // supp rates id and length
3068            2, 4, 11, 22, 48, 96, // supp rates
3069            45, 26, // HT Cap id and length
3070            0x63, 0, 0x17, 0xff, 0, 0, 0, // HT Cap \
3071            0, 0, 0, 0, 0, 0, 0, 0, 1, // HT Cap \
3072            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // HT Cap
3073            191, 12, // VHT Cap id and length
3074            50, 80, 128, 15, 254, 255, 0, 0, 254, 255, 0, 0, // VHT Cap
3075        ];
3076        assert_eq!(&frame[..], &expected[..]);
3077    }
3078
3079    #[fuchsia::test(allow_stalls = false)]
3080    async fn mlme_connect_timeout() {
3081        let mut m = MockObjects::new().await;
3082        let mut me = m.make_mlme().await;
3083        let connect_req = fidl_mlme::ConnectRequest {
3084            selected_bss: fake_fidl_bss_description!(Open, bssid: BSSID.to_array()),
3085            connect_failure_timeout: 100,
3086            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
3087            sae_password: vec![],
3088            wep_key: None,
3089            security_ie: vec![],
3090        };
3091        me.handle_mlme_req(wlan_sme::MlmeRequest::Connect(connect_req))
3092            .await
3093            .expect("Failed to send MlmeRequest::Connect.");
3094
3095        // Verify an event was queued up in the timer.
3096        let (event, _id) = assert_variant!(drain_timeouts(&mut m.time_stream).get(&TimedEventClass::Connecting), Some(events) => {
3097            assert_eq!(events.len(), 1);
3098            events[0].clone()
3099        });
3100
3101        // Quick check that a frame was sent (this is authentication frame).
3102        assert_eq!(m.fake_device_state.lock().wlan_queue.len(), 1);
3103        let (_frame, _txflags) = m.fake_device_state.lock().wlan_queue.remove(0);
3104
3105        // Send connect timeout
3106        me.handle_timed_event(event).await;
3107
3108        // Verify a connect confirm message was sent
3109        let msg = m
3110            .fake_device_state
3111            .lock()
3112            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
3113            .expect("expect msg");
3114        assert_eq!(
3115            msg,
3116            fidl_mlme::ConnectConfirm {
3117                peer_sta_address: BSSID.to_array(),
3118                result_code: fidl_ieee80211::StatusCode::RejectedSequenceTimeout,
3119                association_id: 0,
3120                association_ies: vec![],
3121            },
3122        );
3123    }
3124
3125    #[fuchsia::test(allow_stalls = false)]
3126    async fn mlme_reconnect_no_sta() {
3127        let mut m = MockObjects::new().await;
3128        let mut me = m.make_mlme().await;
3129
3130        let reconnect_req = fidl_mlme::ReconnectRequest { peer_sta_address: [1, 2, 3, 4, 5, 6] };
3131        let result = me.handle_mlme_req(wlan_sme::MlmeRequest::Reconnect(reconnect_req)).await;
3132        assert_variant!(result, Err(Error::Status(_, zx::Status::BAD_STATE)));
3133
3134        // Verify a connect confirm message was sent
3135        let msg = m
3136            .fake_device_state
3137            .lock()
3138            .next_mlme_msg::<fidl_mlme::ConnectConfirm>()
3139            .expect("expect msg");
3140        assert_eq!(
3141            msg,
3142            fidl_mlme::ConnectConfirm {
3143                peer_sta_address: [1, 2, 3, 4, 5, 6],
3144                result_code: fidl_ieee80211::StatusCode::DeniedNoAssociationExists,
3145                association_id: 0,
3146                association_ies: vec![],
3147            },
3148        );
3149    }
3150
3151    #[fuchsia::test(allow_stalls = false)]
3152    async fn mlme_respond_to_get_iface_stats_with_error_status() {
3153        let mut m = MockObjects::new().await;
3154        let mut me = m.make_mlme().await;
3155
3156        let (responder, receiver) = Responder::new();
3157        me.handle_mlme_req(wlan_sme::MlmeRequest::GetIfaceStats(responder))
3158            .await
3159            .expect("Failed to send MlmeRequest::GetIfaceStats.");
3160        assert_eq!(
3161            receiver.await,
3162            Ok(fidl_mlme::GetIfaceStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED))
3163        );
3164    }
3165
3166    #[fuchsia::test(allow_stalls = false)]
3167    async fn mlme_respond_to_get_iface_histogram_stats_with_error_status() {
3168        let mut m = MockObjects::new().await;
3169        let mut me = m.make_mlme().await;
3170
3171        let (responder, receiver) = Responder::new();
3172        me.handle_mlme_req(wlan_sme::MlmeRequest::GetIfaceHistogramStats(responder))
3173            .await
3174            .expect("Failed to send MlmeRequest::GetIfaceHistogramStats");
3175        assert_eq!(
3176            receiver.await,
3177            Ok(fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(
3178                zx::sys::ZX_ERR_NOT_SUPPORTED
3179            ))
3180        );
3181    }
3182
3183    #[test]
3184    fn drop_mgmt_frame_wrong_bssid() {
3185        let frame = [
3186            // Mgmt header 1101 for action frame
3187            0b11010000, 0b00000000, // frame control
3188            0, 0, // duration
3189            7, 7, 7, 7, 7, 7, // addr1
3190            6, 6, 6, 6, 6, 6, // addr2
3191            0, 0, 0, 0, 0, 0, // addr3 (bssid should have been [6; 6])
3192            0x10, 0, // sequence control
3193        ];
3194        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3195        assert_eq!(false, make_client_station().should_handle_frame(&frame));
3196    }
3197
3198    #[test]
3199    fn drop_mgmt_frame_wrong_dst_addr() {
3200        let frame = [
3201            // Mgmt header 1101 for action frame
3202            0b11010000, 0b00000000, // frame control
3203            0, 0, // duration
3204            0, 0, 0, 0, 0, 0, // addr1 (dst_addr should have been [7; 6])
3205            6, 6, 6, 6, 6, 6, // addr2
3206            6, 6, 6, 6, 6, 6, // addr3
3207            0x10, 0, // sequence control
3208        ];
3209        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3210        assert_eq!(false, make_client_station().should_handle_frame(&frame));
3211    }
3212
3213    #[test]
3214    fn mgmt_frame_ok_broadcast() {
3215        let frame = [
3216            // Mgmt header 1101 for action frame
3217            0b11010000, 0b00000000, // frame control
3218            0, 0, // duration
3219            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // addr1 (dst_addr is broadcast)
3220            6, 6, 6, 6, 6, 6, // addr2
3221            6, 6, 6, 6, 6, 6, // addr3
3222            0x10, 0, // sequence control
3223        ];
3224        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3225        assert_eq!(true, make_client_station().should_handle_frame(&frame));
3226    }
3227
3228    #[test]
3229    fn mgmt_frame_ok_client_addr() {
3230        let frame = [
3231            // Mgmt header 1101 for action frame
3232            0b11010000, 0b00000000, // frame control
3233            0, 0, // duration
3234            7, 7, 7, 7, 7, 7, // addr1 (dst_addr should have been [7; 6])
3235            6, 6, 6, 6, 6, 6, // addr2
3236            6, 6, 6, 6, 6, 6, // addr3
3237            0x10, 0, // sequence control
3238        ];
3239        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3240        assert_eq!(true, make_client_station().should_handle_frame(&frame));
3241    }
3242
3243    #[test]
3244    fn drop_data_frame_wrong_bssid() {
3245        let frame = [
3246            // Data header 0100
3247            0b01001000,
3248            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
3249            0, 0, // duration
3250            7, 7, 7, 7, 7, 7, // addr1 (dst_addr)
3251            0, 0, 0, 0, 0, 0, // addr2 (bssid should have been [6; 6])
3252            6, 6, 6, 6, 6, 6, // addr3
3253            0x10, 0, // sequence control
3254        ];
3255        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3256        assert_eq!(false, make_client_station().should_handle_frame(&frame));
3257    }
3258
3259    #[test]
3260    fn drop_data_frame_wrong_dst_addr() {
3261        let frame = [
3262            // Data header 0100
3263            0b01001000,
3264            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
3265            0, 0, // duration
3266            0, 0, 0, 0, 0, 0, // addr1 (dst_addr should have been [7; 6])
3267            6, 6, 6, 6, 6, 6, // addr2 (bssid)
3268            6, 6, 6, 6, 6, 6, // addr3
3269            0x10, 0, // sequence control
3270        ];
3271        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3272        assert_eq!(false, make_client_station().should_handle_frame(&frame));
3273    }
3274
3275    #[test]
3276    fn data_frame_ok_broadcast() {
3277        let frame = [
3278            // Data header 0100
3279            0b01001000,
3280            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
3281            0, 0, // duration
3282            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // addr1 (dst_addr is broadcast)
3283            6, 6, 6, 6, 6, 6, // addr2 (bssid)
3284            6, 6, 6, 6, 6, 6, // addr3
3285            0x10, 0, // sequence control
3286        ];
3287        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3288        assert_eq!(true, make_client_station().should_handle_frame(&frame));
3289    }
3290
3291    #[test]
3292    fn data_frame_ok_client_addr() {
3293        let frame = [
3294            // Data header 0100
3295            0b01001000,
3296            0b00000010, // frame control. right 2 bits of octet 2: from_ds(1), to_ds(0)
3297            0, 0, // duration
3298            7, 7, 7, 7, 7, 7, // addr1 (dst_addr)
3299            6, 6, 6, 6, 6, 6, // addr2 (bssid)
3300            6, 6, 6, 6, 6, 6, // addr3
3301            0x10, 0, // sequence control
3302        ];
3303        let frame = mac::MacFrame::parse(&frame[..], false).unwrap();
3304        assert_eq!(true, make_client_station().should_handle_frame(&frame));
3305    }
3306}