Skip to main content

wlan_mlme/client/
mod.rs

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