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