wlan_mlme/client/
mod.rs

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