wlan_mlme/ap/
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 context;
6mod frame_writer;
7mod infra_bss;
8mod remote_client;
9
10use crate::ddk_converter;
11use crate::device::{self, DeviceOps};
12use crate::error::Error;
13use fdf::ArenaStaticBox;
14use ieee80211::{Bssid, MacAddr, Ssid};
15use log::{debug, error, info, trace, warn};
16use std::fmt;
17use wlan_common::mac::{self, CapabilityInfo};
18use wlan_common::timer::Timer;
19use wlan_common::TimeUnit;
20use zerocopy::SplitByteSlice;
21use {
22    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_minstrel as fidl_minstrel,
23    fidl_fuchsia_wlan_mlme as fidl_mlme, fidl_fuchsia_wlan_softmac as fidl_softmac,
24    fuchsia_trace as trace, wlan_trace as wtrace,
25};
26
27use context::*;
28use infra_bss::*;
29use remote_client::*;
30
31#[derive(Debug)]
32struct BufferedFrame {
33    buffer: ArenaStaticBox<[u8]>,
34    tx_flags: fidl_softmac::WlanTxInfoFlags,
35    async_id: trace::Id,
36}
37
38/// Rejection reasons for why a frame was not proceessed.
39#[derive(Debug)]
40pub enum Rejection {
41    /// The frame was for another BSS.
42    OtherBss,
43
44    /// For data frames: The To DS bit was false, or the From DS bit was true.
45    /// For management frames: The To DS bit was set and the frame was not a QMF (QoS Management
46    /// frame) management frame, or the reserved From DS bit was set.
47    BadDsBits,
48
49    /// For ethernet frames
50
51    /// Frame is malformed (For example, a minimum Ethernet frame must contain a header(14 bytes).
52    FrameMalformed,
53
54    /// No source address was found.
55    NoSrcAddr,
56
57    /// No client with the given address was found.
58    NoSuchClient(MacAddr),
59
60    /// Some error specific to a client occurred.
61    Client(MacAddr, ClientRejection),
62
63    /// Some general error occurred.
64    Error(anyhow::Error),
65}
66
67impl Rejection {
68    fn log_level(&self) -> log::Level {
69        match self {
70            Self::NoSrcAddr | Self::FrameMalformed => log::Level::Error,
71            Self::Client(_, e) => e.log_level(),
72            _ => log::Level::Trace,
73        }
74    }
75    fn log(&self, msg: &str) {
76        match self.log_level() {
77            log::Level::Trace => trace!("{}: {}", msg, self),
78            log::Level::Debug => debug!("{}: {}", msg, self),
79            log::Level::Info => info!("{}: {}", msg, self),
80            log::Level::Warn => warn!("{}: {}", msg, self),
81            log::Level::Error => error!("{}: {}", msg, self),
82        }
83    }
84}
85
86impl fmt::Display for Rejection {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        match self {
89            Self::Client(addr, e) => write!(f, "client {:02X?}: {:?}", addr, e),
90            _ => fmt::Debug::fmt(self, f),
91        }
92    }
93}
94
95impl From<anyhow::Error> for Rejection {
96    fn from(e: anyhow::Error) -> Rejection {
97        Self::Error(e)
98    }
99}
100
101#[derive(Debug)]
102pub enum TimedEvent {
103    /// Events that are destined for a client to handle.
104    ClientEvent(MacAddr, ClientEvent),
105}
106
107pub struct Ap<D> {
108    ctx: Context<D>,
109    bss: Option<InfraBss>,
110}
111
112/// This trait adds an ok_or_bss_err for Option<&Bss> and Option<&mut Bss>, which returns an error
113/// with ZX_ERR_BAD_STATE if the Option is uninhabited.
114trait BssOptionExt<T: std::borrow::Borrow<InfraBss>> {
115    fn ok_or_bss_err(self) -> Result<T, Error>;
116}
117
118impl<T: std::borrow::Borrow<InfraBss>> BssOptionExt<T> for Option<T> {
119    fn ok_or_bss_err(self) -> Result<T, Error> {
120        self.ok_or(Error::Status(format!("BSS not started"), zx::Status::BAD_STATE))
121    }
122}
123
124impl<D: DeviceOps> crate::MlmeImpl for Ap<D> {
125    type Config = Bssid;
126    type Device = D;
127    type TimerEvent = TimedEvent;
128
129    async fn new(
130        config: Self::Config,
131        device: D,
132        timer: Timer<TimedEvent>,
133    ) -> Result<Self, anyhow::Error>
134    where
135        Self: Sized,
136    {
137        Ok(Self::new(device, timer, config))
138    }
139    async fn handle_mlme_request(
140        &mut self,
141        req: wlan_sme::MlmeRequest,
142    ) -> Result<(), anyhow::Error> {
143        Self::handle_mlme_req(self, req).await.map_err(|e| e.into())
144    }
145    async fn handle_mac_frame_rx(
146        &mut self,
147        frame: &[u8],
148        rx_info: fidl_softmac::WlanRxInfo,
149        async_id: trace::Id,
150    ) {
151        Self::handle_mac_frame_rx(self, frame, rx_info, async_id).await
152    }
153    fn handle_eth_frame_tx(
154        &mut self,
155        bytes: &[u8],
156        async_id: trace::Id,
157    ) -> Result<(), anyhow::Error> {
158        Self::handle_eth_frame_tx(self, bytes, async_id);
159        Ok(())
160    }
161    async fn handle_scan_complete(&mut self, _status: zx::Status, _scan_id: u64) {
162        warn!("Unexpected ScanComplete for AP MLME.");
163        return;
164    }
165    async fn handle_timeout(&mut self, event: TimedEvent) {
166        Self::handle_timed_event(self, event).await
167    }
168    fn access_device(&mut self) -> &mut Self::Device {
169        &mut self.ctx.device
170    }
171}
172
173impl<D> Ap<D> {
174    pub fn new(device: D, timer: Timer<TimedEvent>, bssid: Bssid) -> Self {
175        Self { ctx: Context::new(device, timer, bssid), bss: None }
176    }
177
178    fn handle_sme_list_minstrel_peers(
179        &self,
180        responder: wlan_sme::responder::Responder<fidl_mlme::MinstrelListResponse>,
181    ) -> Result<(), Error> {
182        // TODO(https://fxbug.dev/42159791): Implement once Minstrel is in Rust.
183        error!("ListMinstrelPeers is not supported.");
184        let peers = fidl_minstrel::Peers { addrs: vec![] };
185        let resp = fidl_mlme::MinstrelListResponse { peers };
186        responder.respond(resp);
187        Ok(())
188    }
189
190    fn handle_sme_get_minstrel_stats(
191        &self,
192        responder: wlan_sme::responder::Responder<fidl_mlme::MinstrelStatsResponse>,
193        _addr: &MacAddr,
194    ) -> Result<(), Error> {
195        // TODO(https://fxbug.dev/42159791): Implement once Minstrel is in Rust.
196        error!("GetMinstrelStats is not supported.");
197        let resp = fidl_mlme::MinstrelStatsResponse { peer: None };
198        responder.respond(resp);
199        Ok(())
200    }
201}
202
203impl<D: DeviceOps> Ap<D> {
204    // Timer handler functions.
205    pub async fn handle_timed_event(&mut self, event: TimedEvent) {
206        let bss = match self.bss.as_mut() {
207            Some(bss) => bss,
208            None => {
209                error!("received timed event but BSS was not started yet");
210                return;
211            }
212        };
213
214        if let Err(e) = bss.handle_timed_event(&mut self.ctx, event).await {
215            error!("failed to handle timed event frame: {}", e)
216        }
217    }
218
219    // MLME handler functions.
220
221    /// Handles MLME-START.request (IEEE Std 802.11-2016, 6.3.11.2) from the SME.
222    async fn handle_mlme_start_req(&mut self, req: fidl_mlme::StartRequest) -> Result<(), Error> {
223        if self.bss.is_some() {
224            info!("MLME-START.request: BSS already started");
225            self.ctx.send_mlme_start_conf(fidl_mlme::StartResultCode::BssAlreadyStartedOrJoined)?;
226            return Ok(());
227        }
228
229        if req.bss_type != fidl_common::BssType::Infrastructure {
230            info!("MLME-START.request: BSS type {:?} not supported", req.bss_type);
231            self.ctx.send_mlme_start_conf(fidl_mlme::StartResultCode::NotSupported)?;
232            return Ok(());
233        }
234
235        self.bss.replace(
236            InfraBss::new(
237                &mut self.ctx,
238                Ssid::from_bytes_unchecked(req.ssid),
239                TimeUnit(req.beacon_period),
240                req.dtim_period,
241                CapabilityInfo(req.capability_info),
242                req.rates,
243                req.channel,
244                req.rsne,
245            )
246            .await?,
247        );
248
249        self.ctx.send_mlme_start_conf(fidl_mlme::StartResultCode::Success)?;
250
251        info!("MLME-START.request: OK");
252        Ok(())
253    }
254
255    /// Handles MLME-STOP.request (IEEE Std 802.11-2016, 6.3.12.2) from the SME.
256    async fn handle_mlme_stop_req(&mut self, _req: fidl_mlme::StopRequest) -> Result<(), Error> {
257        match self.bss.take() {
258            Some(bss) => match bss.stop(&mut self.ctx).await {
259                Ok(_) => self.ctx.send_mlme_stop_conf(fidl_mlme::StopResultCode::Success)?,
260                Err(e) => {
261                    self.ctx.send_mlme_stop_conf(fidl_mlme::StopResultCode::InternalError)?;
262                    return Err(e);
263                }
264            },
265            None => {
266                info!("MLME-STOP.request: BSS not started");
267                self.ctx.send_mlme_stop_conf(fidl_mlme::StopResultCode::BssAlreadyStopped)?;
268            }
269        }
270        info!("MLME-STOP.request: OK");
271        Ok(())
272    }
273
274    /// Handles MLME-SETKEYS.request (IEEE Std 802.11-2016, 6.3.19.1) from the SME.
275    ///
276    /// The MLME should set the keys on the PHY.
277    pub async fn handle_mlme_setkeys_req(
278        &mut self,
279        req: fidl_mlme::SetKeysRequest,
280    ) -> Result<(), Error> {
281        if let Some(bss) = self.bss.as_mut() {
282            bss.handle_mlme_setkeys_req(&mut self.ctx, req.keylist).await
283        } else {
284            Err(Error::Status(format!("cannot set keys on unstarted BSS"), zx::Status::BAD_STATE))
285        }
286    }
287
288    pub async fn handle_mlme_query_device_info(
289        &mut self,
290        responder: wlan_sme::responder::Responder<fidl_mlme::DeviceInfo>,
291    ) -> Result<(), Error> {
292        let info = ddk_converter::mlme_device_info_from_softmac(
293            device::try_query(&mut self.ctx.device).await?,
294        )?;
295        responder.respond(info);
296        Ok(())
297    }
298
299    pub async fn handle_mlme_query_discovery_support(
300        &mut self,
301        responder: wlan_sme::responder::Responder<fidl_common::DiscoverySupport>,
302    ) -> Result<(), Error> {
303        let support = device::try_query_discovery_support(&mut self.ctx.device).await?;
304        responder.respond(support);
305        Ok(())
306    }
307
308    pub async fn handle_mlme_query_mac_sublayer_support(
309        &mut self,
310        responder: wlan_sme::responder::Responder<fidl_common::MacSublayerSupport>,
311    ) -> Result<(), Error> {
312        let support = device::try_query_mac_sublayer_support(&mut self.ctx.device).await?;
313        responder.respond(support);
314        Ok(())
315    }
316
317    pub async fn handle_mlme_query_security_support(
318        &mut self,
319        responder: wlan_sme::responder::Responder<fidl_common::SecuritySupport>,
320    ) -> Result<(), Error> {
321        let support = device::try_query_security_support(&mut self.ctx.device).await?;
322        responder.respond(support);
323        Ok(())
324    }
325
326    pub async fn handle_mlme_query_spectrum_management_support(
327        &mut self,
328        responder: wlan_sme::responder::Responder<fidl_common::SpectrumManagementSupport>,
329    ) -> Result<(), Error> {
330        let support = device::try_query_spectrum_management_support(&mut self.ctx.device).await?;
331        responder.respond(support);
332        Ok(())
333    }
334
335    pub async fn handle_mlme_req(&mut self, req: wlan_sme::MlmeRequest) -> Result<(), Error> {
336        use wlan_sme::MlmeRequest as Req;
337        match req {
338            Req::Start(req) => self.handle_mlme_start_req(req).await,
339            Req::Stop(req) => self.handle_mlme_stop_req(req).await,
340            Req::SetKeys(req) => self.handle_mlme_setkeys_req(req).await,
341            Req::QueryDeviceInfo(responder) => self.handle_mlme_query_device_info(responder).await,
342            Req::QueryDiscoverySupport(responder) => {
343                self.handle_mlme_query_discovery_support(responder).await
344            }
345            Req::QueryMacSublayerSupport(responder) => {
346                self.handle_mlme_query_mac_sublayer_support(responder).await
347            }
348            Req::QuerySecuritySupport(responder) => {
349                self.handle_mlme_query_security_support(responder).await
350            }
351            Req::QuerySpectrumManagementSupport(responder) => {
352                self.handle_mlme_query_spectrum_management_support(responder).await
353            }
354            Req::ListMinstrelPeers(responder) => self.handle_sme_list_minstrel_peers(responder),
355            Req::GetMinstrelStats(req, responder) => {
356                self.handle_sme_get_minstrel_stats(responder, &req.peer_addr.into())
357            }
358            Req::AuthResponse(resp) => {
359                // TODO(https://fxbug.dev/42172646) - Added to help investigate hw-sim test. Remove later
360                info!("Handling MLME auth resp. self.bss.is_some()?: {}", self.bss.is_some());
361                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_auth_resp(&mut self.ctx, resp).await
362            }
363            Req::Deauthenticate(req) => {
364                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_deauth_req(&mut self.ctx, req).await
365            }
366            Req::AssocResponse(resp) => {
367                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_assoc_resp(&mut self.ctx, resp).await
368            }
369            Req::Disassociate(req) => {
370                self.bss
371                    .as_mut()
372                    .ok_or_bss_err()?
373                    .handle_mlme_disassoc_req(&mut self.ctx, req)
374                    .await
375            }
376            Req::SetCtrlPort(req) => {
377                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_set_controlled_port_req(req)
378            }
379            Req::Eapol(req) => {
380                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_eapol_req(&mut self.ctx, req)
381            }
382            _ => Err(Error::Status(format!("not supported"), zx::Status::NOT_SUPPORTED)),
383        }
384        .map_err(|e| {
385            error!("error handling MLME message: {}", e);
386            e
387        })
388    }
389
390    pub fn handle_eth_frame_tx(&mut self, frame: &[u8], async_id: trace::Id) {
391        let bss = match self.bss.as_mut() {
392            Some(bss) => bss,
393            None => {
394                error!("received Ethernet frame but BSS was not started yet");
395                return;
396            }
397        };
398
399        let mac::EthernetFrame { hdr, body } =
400            match mac::EthernetFrame::parse(frame).ok_or_else(|| Rejection::FrameMalformed) {
401                Ok(eth_frame) => eth_frame,
402                Err(e) => {
403                    error!("failed to parse Ethernet frame: {}", e);
404                    return;
405                }
406            };
407
408        if let Err(e) = bss.handle_eth_frame(&mut self.ctx, *hdr, body, async_id) {
409            e.log("failed to handle Ethernet frame")
410        }
411    }
412
413    pub async fn handle_mac_frame_rx<B: SplitByteSlice>(
414        &mut self,
415        bytes: B,
416        rx_info: fidl_softmac::WlanRxInfo,
417        async_id: trace::Id,
418    ) {
419        let bss = match self.bss.as_mut() {
420            Some(bss) => bss,
421            None => {
422                error!("received WLAN frame but BSS was not started yet");
423                wtrace::async_end_wlansoftmac_rx(async_id, "BSS not started");
424                return;
425            }
426        };
427
428        // Rogue frames received from the wrong channel
429        if rx_info.channel.primary != bss.channel {
430            wtrace::async_end_wlansoftmac_rx(async_id, "frame from wrong channel");
431            return;
432        }
433
434        let body_aligned = (rx_info.rx_flags & fidl_softmac::WlanRxInfoFlags::FRAME_BODY_PADDING_4)
435            != fidl_softmac::WlanRxInfoFlags::empty();
436
437        let mac_frame = match mac::MacFrame::parse(bytes, body_aligned) {
438            Some(mac_frame) => mac_frame,
439            None => {
440                error!("failed to parse MAC frame");
441                wtrace::async_end_wlansoftmac_rx(async_id, "failed to parse frame");
442                return;
443            }
444        };
445
446        if let Err(e) = match mac_frame {
447            mac::MacFrame::Mgmt(mgmt) => bss.handle_mgmt_frame(&mut self.ctx, mgmt).await,
448            mac::MacFrame::Data(data_frame) => bss.handle_data_frame(&mut self.ctx, data_frame),
449            mac::MacFrame::Ctrl(ctrl_frame) => bss.handle_ctrl_frame(&mut self.ctx, ctrl_frame),
450            mac::MacFrame::Unsupported { frame_ctrl } => {
451                error!("received unsupported MAC frame: frame_ctrl = {:?}", frame_ctrl);
452                wtrace::async_end_wlansoftmac_rx(async_id, "received unsupported frame");
453                return;
454            }
455        } {
456            wtrace::async_end_wlansoftmac_rx(async_id, "failed to handle frame");
457            e.log("failed to handle MAC frame")
458        } else {
459            wtrace::async_end_wlansoftmac_rx(async_id, "successfully handled frame");
460        }
461    }
462}
463
464#[cfg(test)]
465mod tests {
466    use super::*;
467    use crate::device::{test_utils, FakeDevice, FakeDeviceConfig, FakeDeviceState, LinkStatus};
468    use crate::test_utils::MockWlanRxInfo;
469    use fuchsia_sync::Mutex;
470    use ieee80211::MacAddrBytes;
471    use lazy_static::lazy_static;
472    use std::sync::Arc;
473    use wlan_common::big_endian::BigEndianU16;
474    use wlan_common::test_utils::fake_frames::fake_wpa2_rsne;
475    use wlan_common::{assert_variant, timer};
476    use wlan_frame_writer::write_frame_to_vec;
477    use wlan_sme::responder::Responder;
478    use {
479        fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
480        fidl_fuchsia_wlan_softmac as fidl_softmac,
481    };
482
483    lazy_static! {
484        static ref CLIENT_ADDR: MacAddr = [4u8; 6].into();
485        static ref BSSID: Bssid = [2u8; 6].into();
486        static ref CLIENT_ADDR2: MacAddr = [6u8; 6].into();
487    }
488
489    fn make_eth_frame(
490        dst_addr: MacAddr,
491        src_addr: MacAddr,
492        protocol_id: u16,
493        body: &[u8],
494    ) -> Vec<u8> {
495        write_frame_to_vec!({
496            headers: {
497                mac::EthernetIIHdr: &mac::EthernetIIHdr {
498                    da: dst_addr,
499                    sa: src_addr,
500                    ether_type: BigEndianU16::from_native(protocol_id),
501                },
502            },
503            payload: body,
504        })
505        .unwrap() // This frame construction never fails
506    }
507
508    // TODO(https://fxbug.dev/327499461): This function is async to ensure MLME functions will
509    // run in an async context and not call `wlan_common::timer::Timer::now` without an
510    // executor.
511    async fn make_ap(
512    ) -> (Ap<FakeDevice>, Arc<Mutex<FakeDeviceState>>, timer::EventStream<TimedEvent>) {
513        let (timer, time_stream) = timer::create_timer();
514        let (fake_device, fake_device_state) = FakeDevice::new_with_config(
515            FakeDeviceConfig::default()
516                .with_mock_mac_role(fidl_common::WlanMacRole::Ap)
517                .with_mock_sta_addr((*BSSID).to_array()),
518        )
519        .await;
520        (Ap::new(fake_device, timer, *BSSID), fake_device_state, time_stream)
521    }
522
523    #[fuchsia::test(allow_stalls = false)]
524    async fn ap_handle_eth_frame() {
525        let (mut ap, fake_device_state, _) = make_ap().await;
526        ap.bss.replace(
527            InfraBss::new(
528                &mut ap.ctx,
529                Ssid::try_from("coolnet").unwrap(),
530                TimeUnit::DEFAULT_BEACON_INTERVAL,
531                2,
532                CapabilityInfo(0),
533                vec![0b11111000],
534                1,
535                None,
536            )
537            .await
538            .expect("expected InfraBss::new ok"),
539        );
540        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
541
542        let client = ap.bss.as_mut().unwrap().clients.get_mut(&CLIENT_ADDR).unwrap();
543        client
544            .handle_mlme_auth_resp(&mut ap.ctx, fidl_mlme::AuthenticateResultCode::Success)
545            .await
546            .expect("expected OK");
547        client
548            .handle_mlme_assoc_resp(
549                &mut ap.ctx,
550                false,
551                1,
552                mac::CapabilityInfo(0),
553                fidl_mlme::AssociateResultCode::Success,
554                1,
555                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
556            )
557            .await
558            .expect("expected OK");
559        fake_device_state.lock().wlan_queue.clear();
560
561        ap.handle_eth_frame_tx(
562            &make_eth_frame(*CLIENT_ADDR, *CLIENT_ADDR2, 0x1234, &[1, 2, 3, 4, 5][..]),
563            0.into(),
564        );
565
566        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
567        assert_eq!(
568            &fake_device_state.lock().wlan_queue[0].0[..],
569            &[
570                // Mgmt header
571                0b00001000, 0b00000010, // Frame Control
572                0, 0, // Duration
573                4, 4, 4, 4, 4, 4, // addr1
574                2, 2, 2, 2, 2, 2, // addr2
575                6, 6, 6, 6, 6, 6, // addr3
576                0x30, 0, // Sequence Control
577                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
578                0, 0, 0, // OUI
579                0x12, 0x34, // Protocol ID
580                // Data
581                1, 2, 3, 4, 5,
582            ][..]
583        );
584    }
585
586    #[fuchsia::test(allow_stalls = false)]
587    async fn ap_handle_eth_frame_no_such_client() {
588        let (mut ap, _, _) = make_ap().await;
589        ap.bss.replace(
590            InfraBss::new(
591                &mut ap.ctx,
592                Ssid::try_from("coolnet").unwrap(),
593                TimeUnit::DEFAULT_BEACON_INTERVAL,
594                2,
595                CapabilityInfo(0),
596                vec![0b11111000],
597                1,
598                None,
599            )
600            .await
601            .expect("expected InfraBss::new ok"),
602        );
603        ap.handle_eth_frame_tx(
604            &make_eth_frame(*CLIENT_ADDR2, *CLIENT_ADDR, 0x1234, &[1, 2, 3, 4, 5][..]),
605            0.into(),
606        );
607    }
608
609    fn mock_rx_info(ap: &Ap<FakeDevice>) -> fidl_softmac::WlanRxInfo {
610        let channel = fidl_common::WlanChannel {
611            primary: ap.bss.as_ref().unwrap().channel,
612            cbw: fidl_common::ChannelBandwidth::Cbw20,
613            secondary80: 0,
614        };
615        MockWlanRxInfo::with_channel(channel).into()
616    }
617
618    #[fuchsia::test(allow_stalls = false)]
619    async fn ap_handle_mac_frame() {
620        let (mut ap, fake_device_state, _) = make_ap().await;
621        ap.bss.replace(
622            InfraBss::new(
623                &mut ap.ctx,
624                Ssid::try_from("coolnet").unwrap(),
625                TimeUnit::DEFAULT_BEACON_INTERVAL,
626                2,
627                CapabilityInfo(0),
628                vec![0b11111000],
629                1,
630                None,
631            )
632            .await
633            .expect("expected InfraBss::new ok"),
634        );
635        ap.handle_mac_frame_rx(
636            &[
637                // Mgmt header
638                0b10110000, 0b00000000, // Frame Control
639                0, 0, // Duration
640                2, 2, 2, 2, 2, 2, // addr1
641                4, 4, 4, 4, 4, 4, // addr2
642                2, 2, 2, 2, 2, 2, // addr3
643                0x10, 0, // Sequence Control
644                // Auth body
645                0, 0, // Auth Algorithm Number
646                1, 0, // Auth Txn Seq Number
647                0, 0, // Status code
648            ][..],
649            mock_rx_info(&ap),
650            0.into(),
651        )
652        .await;
653
654        assert_eq!(ap.bss.as_mut().unwrap().clients.contains_key(&CLIENT_ADDR), true);
655
656        let msg = fake_device_state
657            .lock()
658            .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
659            .expect("expected MLME message");
660        assert_eq!(
661            msg,
662            fidl_mlme::AuthenticateIndication {
663                peer_sta_address: CLIENT_ADDR.to_array(),
664                auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
665            },
666        );
667    }
668
669    #[fuchsia::test(allow_stalls = false)]
670    async fn ap_handle_mac_frame_ps_poll() {
671        let (mut ap, fake_device_state, _) = make_ap().await;
672        ap.bss.replace(
673            InfraBss::new(
674                &mut ap.ctx,
675                Ssid::try_from("coolnet").unwrap(),
676                TimeUnit::DEFAULT_BEACON_INTERVAL,
677                2,
678                CapabilityInfo(0),
679                vec![0b11111000],
680                1,
681                None,
682            )
683            .await
684            .expect("expected InfraBss::new ok"),
685        );
686        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
687
688        let client = ap.bss.as_mut().unwrap().clients.get_mut(&CLIENT_ADDR).unwrap();
689        client
690            .handle_mlme_auth_resp(&mut ap.ctx, fidl_mlme::AuthenticateResultCode::Success)
691            .await
692            .expect("expected OK");
693        client
694            .handle_mlme_assoc_resp(
695                &mut ap.ctx,
696                false,
697                1,
698                mac::CapabilityInfo(0),
699                fidl_mlme::AssociateResultCode::Success,
700                1,
701                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
702            )
703            .await
704            .expect("expected OK");
705        fake_device_state.lock().wlan_queue.clear();
706
707        // Put the client into dozing.
708        ap.handle_mac_frame_rx(
709            &[
710                0b01001000, 0b00010001, // Frame control.
711                0, 0, // Duration.
712                2, 2, 2, 2, 2, 2, // BSSID.
713                4, 4, 4, 4, 4, 4, // MAC address.
714                2, 2, 2, 2, 2, 2, // BSSID.
715                0x10, 0, // Sequence control.
716            ][..],
717            mock_rx_info(&ap),
718            0.into(),
719        )
720        .await;
721
722        ap.handle_eth_frame_tx(
723            &make_eth_frame(*CLIENT_ADDR, *CLIENT_ADDR2, 0x1234, &[1, 2, 3, 4, 5][..]),
724            0.into(),
725        );
726        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
727
728        // Send a PS-Poll.
729        ap.handle_mac_frame_rx(
730            &[
731                // Ctrl header
732                0b10100100, 0b00000000, // Frame Control
733                0b00000001, 0b11000000, // Masked AID
734                2, 2, 2, 2, 2, 2, // addr1
735                4, 4, 4, 4, 4, 4, // addr2
736            ][..],
737            mock_rx_info(&ap),
738            0.into(),
739        )
740        .await;
741
742        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
743        assert_eq!(
744            &fake_device_state.lock().wlan_queue[0].0[..],
745            &[
746                // Mgmt header
747                0b00001000, 0b00000010, // Frame Control
748                0, 0, // Duration
749                4, 4, 4, 4, 4, 4, // addr1
750                2, 2, 2, 2, 2, 2, // addr2
751                6, 6, 6, 6, 6, 6, // addr3
752                0x30, 0, // Sequence Control
753                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
754                0, 0, 0, // OUI
755                0x12, 0x34, // Protocol ID
756                // Data
757                1, 2, 3, 4, 5,
758            ][..]
759        );
760    }
761
762    #[fuchsia::test(allow_stalls = false)]
763    async fn ap_handle_mac_frame_no_such_client() {
764        let (mut ap, _, _) = make_ap().await;
765        ap.bss.replace(
766            InfraBss::new(
767                &mut ap.ctx,
768                Ssid::try_from("coolnet").unwrap(),
769                TimeUnit::DEFAULT_BEACON_INTERVAL,
770                2,
771                CapabilityInfo(0),
772                vec![0b11111000],
773                1,
774                None,
775            )
776            .await
777            .expect("expected InfraBss::new ok"),
778        );
779        ap.handle_mac_frame_rx(
780            &[
781                // Mgmt header
782                0b10100000, 0b00000001, // Frame Control
783                0, 0, // Duration
784                2, 2, 2, 2, 2, 2, // addr1
785                4, 4, 4, 4, 4, 4, // addr2
786                2, 2, 2, 2, 2, 2, // addr3
787                0x10, 0, // Sequence Control
788                // Disassoc header:
789                8, 0, // reason code
790            ][..],
791            mock_rx_info(&ap),
792            0.into(),
793        )
794        .await;
795
796        assert_eq!(ap.bss.as_mut().unwrap().clients.contains_key(&CLIENT_ADDR), false);
797    }
798
799    #[fuchsia::test(allow_stalls = false)]
800    async fn ap_handle_mac_frame_bogus() {
801        let (mut ap, _, _) = make_ap().await;
802        ap.bss.replace(
803            InfraBss::new(
804                &mut ap.ctx,
805                Ssid::try_from("coolnet").unwrap(),
806                TimeUnit::DEFAULT_BEACON_INTERVAL,
807                2,
808                CapabilityInfo(0),
809                vec![0b11111000],
810                1,
811                None,
812            )
813            .await
814            .expect("expected InfraBss::new ok"),
815        );
816        ap.handle_mac_frame_rx(
817            &[0][..],
818            fidl_softmac::WlanRxInfo {
819                rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
820                valid_fields: fidl_softmac::WlanRxInfoValid::empty(),
821                phy: fidl_common::WlanPhyType::Dsss,
822                data_rate: 0,
823                channel: fidl_common::WlanChannel {
824                    primary: 0,
825                    cbw: fidl_common::ChannelBandwidth::Cbw20,
826                    secondary80: 0,
827                },
828                mcs: 0,
829                rssi_dbm: 0,
830                snr_dbh: 0,
831            },
832            0.into(),
833        )
834        .await;
835    }
836
837    #[fuchsia::test(allow_stalls = false)]
838    async fn ap_handle_mac_frame_wrong_channel_drop() {
839        let (mut ap, fake_device_state, _) = make_ap().await;
840        ap.bss.replace(
841            InfraBss::new(
842                &mut ap.ctx,
843                Ssid::try_from("coolnet").unwrap(),
844                TimeUnit::DEFAULT_BEACON_INTERVAL,
845                2,
846                CapabilityInfo(0),
847                vec![0b11111000],
848                1,
849                None,
850            )
851            .await
852            .expect("expected InfraBss::new ok"),
853        );
854        let probe_req = [
855            // Mgmt header
856            0b01000000, 0b00000000, // Frame Control
857            0, 0, // Duration
858            2, 2, 2, 2, 2, 2, // addr1
859            4, 4, 4, 4, 4, 4, // addr2
860            2, 2, 2, 2, 2, 2, // addr3
861            0x10, 0, // Sequence Control
862            // SSID
863            0, 7, 0x63, 0x6f, 0x6f, 0x6c, 0x6e, 0x65, 0x74, 0x0a,
864        ];
865        let rx_info_wrong_channel = fidl_softmac::WlanRxInfo {
866            rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
867            valid_fields: fidl_softmac::WlanRxInfoValid::empty(),
868            phy: fidl_common::WlanPhyType::Dsss,
869            data_rate: 0,
870            channel: fidl_common::WlanChannel {
871                primary: 0,
872                cbw: fidl_common::ChannelBandwidth::Cbw20,
873                secondary80: 0,
874            },
875            mcs: 0,
876            rssi_dbm: 0,
877            snr_dbh: 0,
878        };
879        ap.handle_mac_frame_rx(&probe_req[..], rx_info_wrong_channel.clone(), 0.into()).await;
880
881        // Probe Request from the wrong channel should be dropped and no probe response sent.
882        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
883
884        // Frame from the same channel must be processed and a probe response sent.
885        let rx_info_same_channel = fidl_softmac::WlanRxInfo {
886            channel: fidl_common::WlanChannel {
887                primary: 1,
888                cbw: fidl_common::ChannelBandwidth::Cbw20,
889                secondary80: 0,
890            },
891            ..rx_info_wrong_channel
892        };
893        fake_device_state.lock().wlan_queue.clear();
894        ap.handle_mac_frame_rx(&probe_req[..], rx_info_same_channel, 0.into()).await;
895        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
896    }
897
898    #[fuchsia::test(allow_stalls = false)]
899    async fn ap_handle_mlme_start_req() {
900        let (mut ap, fake_device_state, _) = make_ap().await;
901        ap.handle_mlme_start_req(fidl_mlme::StartRequest {
902            ssid: Ssid::try_from("coolnet").unwrap().into(),
903            bss_type: fidl_common::BssType::Infrastructure,
904            beacon_period: 5,
905            dtim_period: 1,
906            channel: 2,
907            capability_info: CapabilityInfo(0).raw(),
908            rates: vec![0b11111000],
909            country: fidl_mlme::Country { alpha2: *b"xx", suffix: fidl_mlme::COUNTRY_ENVIRON_ALL },
910            mesh_id: vec![],
911            rsne: None,
912            phy: fidl_common::WlanPhyType::Erp,
913            channel_bandwidth: fidl_common::ChannelBandwidth::Cbw20,
914        })
915        .await
916        .expect("expected Ap::handle_mlme_start_request OK");
917
918        assert!(ap.bss.is_some());
919        assert_eq!(
920            fake_device_state.lock().wlan_channel,
921            fidl_common::WlanChannel {
922                primary: 2,
923                // TODO(https://fxbug.dev/42116942): Correctly support this.
924                cbw: fidl_common::ChannelBandwidth::Cbw20,
925                secondary80: 0,
926            }
927        );
928        assert_eq!(fake_device_state.lock().link_status, LinkStatus::UP);
929
930        let msg = fake_device_state
931            .lock()
932            .next_mlme_msg::<fidl_mlme::StartConfirm>()
933            .expect("expected MLME message");
934        assert_eq!(
935            msg,
936            fidl_mlme::StartConfirm { result_code: fidl_mlme::StartResultCode::Success },
937        );
938    }
939
940    #[fuchsia::test(allow_stalls = false)]
941    async fn ap_handle_mlme_start_req_already_started() {
942        let (mut ap, fake_device_state, _) = make_ap().await;
943        ap.bss.replace(
944            InfraBss::new(
945                &mut ap.ctx,
946                Ssid::try_from("coolnet").unwrap(),
947                TimeUnit::DEFAULT_BEACON_INTERVAL,
948                2,
949                CapabilityInfo(0),
950                vec![0b11111000],
951                1,
952                None,
953            )
954            .await
955            .expect("expected InfraBss::new ok"),
956        );
957
958        ap.handle_mlme_start_req(fidl_mlme::StartRequest {
959            ssid: Ssid::try_from("coolnet").unwrap().into(),
960            bss_type: fidl_common::BssType::Infrastructure,
961            beacon_period: 5,
962            dtim_period: 1,
963            channel: 2,
964            capability_info: CapabilityInfo(0).raw(),
965            rates: vec![],
966            country: fidl_mlme::Country { alpha2: *b"xx", suffix: fidl_mlme::COUNTRY_ENVIRON_ALL },
967            mesh_id: vec![],
968            rsne: None,
969            phy: fidl_common::WlanPhyType::Erp,
970            channel_bandwidth: fidl_common::ChannelBandwidth::Cbw20,
971        })
972        .await
973        .expect("expected Ap::handle_mlme_start_request OK");
974
975        let msg = fake_device_state
976            .lock()
977            .next_mlme_msg::<fidl_mlme::StartConfirm>()
978            .expect("expected MLME message");
979        assert_eq!(
980            msg,
981            fidl_mlme::StartConfirm {
982                result_code: fidl_mlme::StartResultCode::BssAlreadyStartedOrJoined
983            },
984        );
985    }
986
987    #[fuchsia::test(allow_stalls = false)]
988    async fn ap_handle_mlme_stop_req() {
989        let (mut ap, fake_device_state, _) = make_ap().await;
990        ap.bss.replace(
991            InfraBss::new(
992                &mut ap.ctx,
993                Ssid::try_from("coolnet").unwrap(),
994                TimeUnit::DEFAULT_BEACON_INTERVAL,
995                2,
996                CapabilityInfo(0),
997                vec![0b11111000],
998                1,
999                None,
1000            )
1001            .await
1002            .expect("expected InfraBss::new ok"),
1003        );
1004
1005        ap.handle_mlme_stop_req(fidl_mlme::StopRequest {
1006            ssid: Ssid::try_from("coolnet").unwrap().into(),
1007        })
1008        .await
1009        .expect("expected Ap::handle_mlme_stop_request OK");
1010        assert!(ap.bss.is_none());
1011        assert_eq!(fake_device_state.lock().link_status, LinkStatus::DOWN);
1012
1013        let msg = fake_device_state
1014            .lock()
1015            .next_mlme_msg::<fidl_mlme::StopConfirm>()
1016            .expect("expected MLME message");
1017        assert_eq!(msg, fidl_mlme::StopConfirm { result_code: fidl_mlme::StopResultCode::Success },);
1018    }
1019
1020    #[fuchsia::test(allow_stalls = false)]
1021    async fn ap_handle_mlme_stop_req_already_stopped() {
1022        let (mut ap, fake_device_state, _) = make_ap().await;
1023
1024        ap.handle_mlme_stop_req(fidl_mlme::StopRequest {
1025            ssid: Ssid::try_from("coolnet").unwrap().into(),
1026        })
1027        .await
1028        .expect("expected Ap::handle_mlme_stop_request OK");
1029        assert!(ap.bss.is_none());
1030
1031        let msg = fake_device_state
1032            .lock()
1033            .next_mlme_msg::<fidl_mlme::StopConfirm>()
1034            .expect("expected MLME message");
1035        assert_eq!(
1036            msg,
1037            fidl_mlme::StopConfirm { result_code: fidl_mlme::StopResultCode::BssAlreadyStopped },
1038        );
1039    }
1040
1041    #[fuchsia::test(allow_stalls = false)]
1042    async fn ap_handle_mlme_setkeys_req() {
1043        let (mut ap, fake_device_state, _) = make_ap().await;
1044        ap.bss.replace(
1045            InfraBss::new(
1046                &mut ap.ctx,
1047                Ssid::try_from("coolnet").unwrap(),
1048                TimeUnit::DEFAULT_BEACON_INTERVAL,
1049                2,
1050                CapabilityInfo(0),
1051                vec![0b11111000],
1052                1,
1053                Some(fake_wpa2_rsne()),
1054            )
1055            .await
1056            .expect("expected InfraBss::new ok"),
1057        );
1058
1059        ap.handle_mlme_setkeys_req(fidl_mlme::SetKeysRequest {
1060            keylist: vec![fidl_mlme::SetKeyDescriptor {
1061                cipher_suite_oui: [1, 2, 3],
1062                cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
1063                key_type: fidl_mlme::KeyType::Pairwise,
1064                address: [5; 6],
1065                key_id: 6,
1066                key: vec![1, 2, 3, 4, 5, 6, 7],
1067                rsc: 8,
1068            }],
1069        })
1070        .await
1071        .expect("expected Ap::handle_mlme_setkeys_req OK");
1072        assert_eq!(
1073            fake_device_state.lock().keys,
1074            vec![fidl_softmac::WlanKeyConfiguration {
1075                protection: Some(fidl_softmac::WlanProtection::RxTx),
1076                cipher_oui: Some([1, 2, 3]),
1077                cipher_type: Some(4),
1078                key_type: Some(fidl_ieee80211::KeyType::Pairwise),
1079                peer_addr: Some([5; 6]),
1080                key_idx: Some(6),
1081                key: Some(vec![1, 2, 3, 4, 5, 6, 7]),
1082                rsc: Some(8),
1083                ..Default::default()
1084            }]
1085        );
1086    }
1087
1088    #[fuchsia::test(allow_stalls = false)]
1089    async fn ap_handle_mlme_setkeys_req_no_bss() {
1090        let (mut ap, _, _) = make_ap().await;
1091        assert_variant!(
1092            ap.handle_mlme_setkeys_req(fidl_mlme::SetKeysRequest {
1093                keylist: vec![fidl_mlme::SetKeyDescriptor {
1094                    cipher_suite_oui: [1, 2, 3],
1095                    cipher_suite_type:
1096                        fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
1097                    key_type: fidl_mlme::KeyType::Pairwise,
1098                    address: [5; 6],
1099                    key_id: 6,
1100                    key: vec![1, 2, 3, 4, 5, 6, 7],
1101                    rsc: 8,
1102                }],
1103            })
1104            .await
1105            .expect_err("expected Ap::handle_mlme_setkeys_req error"),
1106            Error::Status(_, zx::Status::BAD_STATE)
1107        );
1108    }
1109
1110    #[fuchsia::test(allow_stalls = false)]
1111    async fn ap_handle_mlme_setkeys_req_bss_no_rsne() {
1112        let (mut ap, _, _) = make_ap().await;
1113        ap.bss.replace(
1114            InfraBss::new(
1115                &mut ap.ctx,
1116                Ssid::try_from("coolnet").unwrap(),
1117                TimeUnit::DEFAULT_BEACON_INTERVAL,
1118                2,
1119                CapabilityInfo(0),
1120                vec![0b11111000],
1121                1,
1122                None,
1123            )
1124            .await
1125            .expect("expected InfraBss::new ok"),
1126        );
1127
1128        assert_variant!(
1129            ap.handle_mlme_setkeys_req(fidl_mlme::SetKeysRequest {
1130                keylist: vec![fidl_mlme::SetKeyDescriptor {
1131                    cipher_suite_oui: [1, 2, 3],
1132                    cipher_suite_type:
1133                        fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
1134                    key_type: fidl_mlme::KeyType::Pairwise,
1135                    address: [5; 6],
1136                    key_id: 6,
1137                    key: vec![1, 2, 3, 4, 5, 6, 7],
1138                    rsc: 8,
1139                }],
1140            })
1141            .await
1142            .expect_err("expected Ap::handle_mlme_setkeys_req error"),
1143            Error::Status(_, zx::Status::BAD_STATE)
1144        );
1145    }
1146
1147    #[fuchsia::test(allow_stalls = false)]
1148    async fn ap_handle_mlme_req_handle_mlme_auth_resp() {
1149        let (mut ap, fake_device_state, _) = make_ap().await;
1150        ap.bss.replace(
1151            InfraBss::new(
1152                &mut ap.ctx,
1153                Ssid::try_from("coolnet").unwrap(),
1154                TimeUnit::DEFAULT_BEACON_INTERVAL,
1155                2,
1156                CapabilityInfo(0),
1157                vec![0b11111000],
1158                1,
1159                None,
1160            )
1161            .await
1162            .expect("expected InfraBss::new ok"),
1163        );
1164        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1165
1166        ap.handle_mlme_req(wlan_sme::MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
1167            peer_sta_address: CLIENT_ADDR.to_array(),
1168            result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
1169        }))
1170        .await
1171        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AuthenticateResp) ok");
1172        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1173        assert_eq!(
1174            &fake_device_state.lock().wlan_queue[0].0[..],
1175            &[
1176                // Mgmt header
1177                0b10110000, 0, // Frame Control
1178                0, 0, // Duration
1179                4, 4, 4, 4, 4, 4, // addr1
1180                2, 2, 2, 2, 2, 2, // addr2
1181                2, 2, 2, 2, 2, 2, // addr3
1182                0x10, 0, // Sequence Control
1183                // Auth header:
1184                0, 0, // auth algorithm
1185                2, 0, // auth txn seq num
1186                76, 0, // status code
1187            ][..]
1188        );
1189    }
1190
1191    #[fuchsia::test(allow_stalls = false)]
1192    async fn ap_handle_mlme_req_handle_mlme_auth_resp_no_bss() {
1193        let (mut ap, _, _) = make_ap().await;
1194
1195        assert_eq!(
1196            zx::Status::from(
1197                ap.handle_mlme_req(wlan_sme::MlmeRequest::AuthResponse(
1198                    fidl_mlme::AuthenticateResponse {
1199                        peer_sta_address: CLIENT_ADDR.to_array(),
1200                        result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
1201                    }
1202                ))
1203                .await
1204                .expect_err(
1205                    "expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AuthenticateResp) error"
1206                )
1207            ),
1208            zx::Status::BAD_STATE
1209        );
1210    }
1211
1212    #[fuchsia::test(allow_stalls = false)]
1213    async fn ap_handle_mlme_req_handle_mlme_auth_resp_no_such_client() {
1214        let (mut ap, _, _) = make_ap().await;
1215        ap.bss.replace(
1216            InfraBss::new(
1217                &mut ap.ctx,
1218                Ssid::try_from("coolnet").unwrap(),
1219                TimeUnit::DEFAULT_BEACON_INTERVAL,
1220                2,
1221                CapabilityInfo(0),
1222                vec![0b11111000],
1223                1,
1224                None,
1225            )
1226            .await
1227            .expect("expected InfraBss::new ok"),
1228        );
1229
1230        assert_eq!(
1231            zx::Status::from(
1232                ap.handle_mlme_req(wlan_sme::MlmeRequest::AuthResponse(
1233                    fidl_mlme::AuthenticateResponse {
1234                        peer_sta_address: CLIENT_ADDR.to_array(),
1235                        result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
1236                    }
1237                ))
1238                .await
1239                .expect_err(
1240                    "expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AuthenticateResp) error"
1241                )
1242            ),
1243            zx::Status::NOT_FOUND
1244        );
1245    }
1246
1247    #[fuchsia::test(allow_stalls = false)]
1248    async fn ap_handle_mlme_req_handle_mlme_deauth_req() {
1249        let (mut ap, fake_device_state, _) = make_ap().await;
1250        ap.bss.replace(
1251            InfraBss::new(
1252                &mut ap.ctx,
1253                Ssid::try_from("coolnet").unwrap(),
1254                TimeUnit::DEFAULT_BEACON_INTERVAL,
1255                2,
1256                CapabilityInfo(0),
1257                vec![0b11111000],
1258                1,
1259                None,
1260            )
1261            .await
1262            .expect("expected InfraBss::new ok"),
1263        );
1264        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1265
1266        ap.handle_mlme_req(wlan_sme::MlmeRequest::Deauthenticate(
1267            fidl_mlme::DeauthenticateRequest {
1268                peer_sta_address: CLIENT_ADDR.to_array(),
1269                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
1270            },
1271        ))
1272        .await
1273        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::DeauthenticateReq) ok");
1274        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1275        assert_eq!(
1276            &fake_device_state.lock().wlan_queue[0].0[..],
1277            &[
1278                // Mgmt header
1279                0b11000000, 0, // Frame Control
1280                0, 0, // Duration
1281                4, 4, 4, 4, 4, 4, // addr1
1282                2, 2, 2, 2, 2, 2, // addr2
1283                2, 2, 2, 2, 2, 2, // addr3
1284                0x10, 0, // Sequence Control
1285                // Deauth header:
1286                3, 0, // reason code
1287            ][..]
1288        );
1289    }
1290
1291    #[fuchsia::test(allow_stalls = false)]
1292    async fn ap_handle_mlme_req_handle_mlme_assoc_resp() {
1293        let (mut ap, fake_device_state, _) = make_ap().await;
1294        ap.bss.replace(
1295            InfraBss::new(
1296                &mut ap.ctx,
1297                Ssid::try_from("coolnet").unwrap(),
1298                TimeUnit::DEFAULT_BEACON_INTERVAL,
1299                2,
1300                CapabilityInfo(0),
1301                vec![0b11111000],
1302                1,
1303                None,
1304            )
1305            .await
1306            .expect("expected InfraBss::new ok"),
1307        );
1308        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1309
1310        ap.handle_mlme_req(wlan_sme::MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1311            peer_sta_address: CLIENT_ADDR.to_array(),
1312            result_code: fidl_mlme::AssociateResultCode::Success,
1313            association_id: 1,
1314            capability_info: CapabilityInfo(0).raw(),
1315            rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
1316        }))
1317        .await
1318        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AssociateResp) ok");
1319        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1320        assert_eq!(
1321            &fake_device_state.lock().wlan_queue[0].0[..],
1322            &[
1323                // Mgmt header
1324                0b00010000, 0, // Frame Control
1325                0, 0, // Duration
1326                4, 4, 4, 4, 4, 4, // addr1
1327                2, 2, 2, 2, 2, 2, // addr2
1328                2, 2, 2, 2, 2, 2, // addr3
1329                0x10, 0, // Sequence Control
1330                // Association response header:
1331                0, 0, // Capabilities
1332                0, 0, // status code
1333                1, 0, // AID
1334                // IEs
1335                1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
1336                50, 2, 9, 10, // Extended rates
1337                90, 3, 90, 0, 0, // BSS max idle period
1338            ][..]
1339        );
1340    }
1341
1342    #[fuchsia::test(allow_stalls = false)]
1343    async fn ap_handle_mlme_req_handle_mlme_disassoc_req() {
1344        let (mut ap, fake_device_state, _) = make_ap().await;
1345        ap.bss.replace(
1346            InfraBss::new(
1347                &mut ap.ctx,
1348                Ssid::try_from("coolnet").unwrap(),
1349                TimeUnit::DEFAULT_BEACON_INTERVAL,
1350                2,
1351                CapabilityInfo(0),
1352                vec![0b11111000],
1353                1,
1354                None,
1355            )
1356            .await
1357            .expect("expected InfraBss::new ok"),
1358        );
1359        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1360
1361        ap.handle_mlme_req(wlan_sme::MlmeRequest::Disassociate(fidl_mlme::DisassociateRequest {
1362            peer_sta_address: CLIENT_ADDR.to_array(),
1363            reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
1364        }))
1365        .await
1366        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::DisassociateReq) ok");
1367        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1368        assert_eq!(
1369            &fake_device_state.lock().wlan_queue[0].0[..],
1370            &[
1371                // Mgmt header
1372                0b10100000, 0, // Frame Control
1373                0, 0, // Duration
1374                4, 4, 4, 4, 4, 4, // addr1
1375                2, 2, 2, 2, 2, 2, // addr2
1376                2, 2, 2, 2, 2, 2, // addr3
1377                0x10, 0, // Sequence Control
1378                // Disassoc header:
1379                8, 0, // reason code
1380            ][..]
1381        );
1382    }
1383
1384    #[fuchsia::test(allow_stalls = false)]
1385    async fn ap_handle_mlme_req_handle_mlme_set_controlled_port_req() {
1386        let (mut ap, _, _) = make_ap().await;
1387        ap.bss.replace(
1388            InfraBss::new(
1389                &mut ap.ctx,
1390                Ssid::try_from("coolnet").unwrap(),
1391                TimeUnit::DEFAULT_BEACON_INTERVAL,
1392                2,
1393                CapabilityInfo(0),
1394                vec![0b11111000],
1395                1,
1396                Some(fake_wpa2_rsne()),
1397            )
1398            .await
1399            .expect("expected InfraBss::new ok"),
1400        );
1401        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1402
1403        ap.handle_mlme_req(wlan_sme::MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1404            peer_sta_address: CLIENT_ADDR.to_array(),
1405            result_code: fidl_mlme::AssociateResultCode::Success,
1406            association_id: 1,
1407            capability_info: CapabilityInfo(0).raw(),
1408            rates: vec![1, 2, 3],
1409        }))
1410        .await
1411        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AssociateResp) ok");
1412
1413        ap.handle_mlme_req(wlan_sme::MlmeRequest::SetCtrlPort(
1414            fidl_mlme::SetControlledPortRequest {
1415                peer_sta_address: CLIENT_ADDR.to_array(),
1416                state: fidl_mlme::ControlledPortState::Open,
1417            },
1418        ))
1419        .await
1420        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::SetControlledPort) ok");
1421    }
1422
1423    #[fuchsia::test(allow_stalls = false)]
1424    async fn ap_handle_mlme_req_handle_mlme_eapol_req() {
1425        let (mut ap, fake_device_state, _) = make_ap().await;
1426        ap.bss.replace(
1427            InfraBss::new(
1428                &mut ap.ctx,
1429                Ssid::try_from("coolnet").unwrap(),
1430                TimeUnit::DEFAULT_BEACON_INTERVAL,
1431                2,
1432                CapabilityInfo(0),
1433                vec![0b11111000],
1434                1,
1435                None,
1436            )
1437            .await
1438            .expect("expected InfraBss::new ok"),
1439        );
1440        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1441
1442        ap.handle_mlme_req(wlan_sme::MlmeRequest::Eapol(fidl_mlme::EapolRequest {
1443            dst_addr: CLIENT_ADDR.to_array(),
1444            src_addr: BSSID.to_array(),
1445            data: vec![1, 2, 3],
1446        }))
1447        .await
1448        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::EapolReq) ok");
1449        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1450        assert_eq!(
1451            &fake_device_state.lock().wlan_queue[0].0[..],
1452            &[
1453                // Header
1454                0b00001000, 0b00000010, // Frame Control
1455                0, 0, // Duration
1456                4, 4, 4, 4, 4, 4, // addr1
1457                2, 2, 2, 2, 2, 2, // addr2
1458                2, 2, 2, 2, 2, 2, // addr3
1459                0x10, 0, // Sequence Control
1460                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1461                0, 0, 0, // OUI
1462                0x88, 0x8E, // EAPOL protocol ID
1463                // Data
1464                1, 2, 3,
1465            ][..]
1466        );
1467    }
1468
1469    #[fuchsia::test(allow_stalls = false)]
1470    async fn ap_mlme_respond_to_query_device_info() {
1471        let (mut ap, _, _) = make_ap().await;
1472
1473        let (responder, receiver) = Responder::new();
1474        assert_variant!(
1475            ap.handle_mlme_req(wlan_sme::MlmeRequest::QueryDeviceInfo(responder)).await,
1476            Ok(())
1477        );
1478        assert_eq!(
1479            receiver.await.unwrap(),
1480            fidl_mlme::DeviceInfo {
1481                sta_addr: BSSID.to_array(),
1482                role: fidl_common::WlanMacRole::Ap,
1483                bands: test_utils::fake_mlme_band_caps(),
1484                softmac_hardware_capability: 0,
1485                qos_capable: false,
1486            }
1487        );
1488    }
1489
1490    #[fuchsia::test(allow_stalls = false)]
1491    async fn ap_mlme_respond_to_query_discovery_support() {
1492        let (mut ap, _, _) = make_ap().await;
1493
1494        let (responder, receiver) = Responder::new();
1495        assert_variant!(
1496            ap.handle_mlme_req(wlan_sme::MlmeRequest::QueryDiscoverySupport(responder)).await,
1497            Ok(())
1498        );
1499        let resp = receiver.await.unwrap();
1500        assert_eq!(resp.scan_offload.supported, true);
1501        assert_eq!(resp.probe_response_offload.supported, false);
1502    }
1503
1504    #[fuchsia::test(allow_stalls = false)]
1505    async fn ap_mlme_respond_to_query_mac_sublayer_support() {
1506        let (mut ap, _, _) = make_ap().await;
1507
1508        let (responder, receiver) = Responder::new();
1509        assert_variant!(
1510            ap.handle_mlme_req(wlan_sme::MlmeRequest::QueryMacSublayerSupport(responder)).await,
1511            Ok(())
1512        );
1513        let resp = receiver.await.unwrap();
1514        assert_eq!(resp.rate_selection_offload.supported, false);
1515        assert_eq!(resp.data_plane.data_plane_type, fidl_common::DataPlaneType::EthernetDevice);
1516        assert_eq!(resp.device.is_synthetic, true);
1517        assert_eq!(
1518            resp.device.mac_implementation_type,
1519            fidl_common::MacImplementationType::Softmac
1520        );
1521        assert_eq!(resp.device.tx_status_report_supported, true);
1522    }
1523
1524    #[fuchsia::test(allow_stalls = false)]
1525    async fn ap_mlme_respond_to_query_security_support() {
1526        let (mut ap, _, _) = make_ap().await;
1527
1528        let (responder, receiver) = Responder::new();
1529        assert_variant!(
1530            ap.handle_mlme_req(wlan_sme::MlmeRequest::QuerySecuritySupport(responder)).await,
1531            Ok(())
1532        );
1533        let resp = receiver.await.unwrap();
1534        assert_eq!(resp.mfp.supported, false);
1535        assert_eq!(resp.sae.driver_handler_supported, false);
1536        assert_eq!(resp.sae.sme_handler_supported, false);
1537    }
1538
1539    #[fuchsia::test(allow_stalls = false)]
1540    async fn ap_mlme_respond_to_query_spectrum_management_support() {
1541        let (mut ap, _, _) = make_ap().await;
1542
1543        let (responder, receiver) = Responder::new();
1544        assert_variant!(
1545            ap.handle_mlme_req(wlan_sme::MlmeRequest::QuerySpectrumManagementSupport(responder))
1546                .await,
1547            Ok(())
1548        );
1549        assert_eq!(receiver.await.unwrap().dfs.supported, true);
1550    }
1551
1552    #[test]
1553    fn display_rejection() {
1554        assert_eq!(format!("{}", Rejection::BadDsBits), "BadDsBits");
1555    }
1556}