Skip to main content

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