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_mac_sublayer_support(
300        &mut self,
301        responder: wlan_sme::responder::Responder<fidl_common::MacSublayerSupport>,
302    ) -> Result<(), Error> {
303        let support = device::try_query_mac_sublayer_support(&mut self.ctx.device).await?;
304        responder.respond(support);
305        Ok(())
306    }
307
308    pub async fn handle_mlme_query_security_support(
309        &mut self,
310        responder: wlan_sme::responder::Responder<fidl_common::SecuritySupport>,
311    ) -> Result<(), Error> {
312        let support = device::try_query_security_support(&mut self.ctx.device).await?;
313        responder.respond(support);
314        Ok(())
315    }
316
317    pub async fn handle_mlme_query_spectrum_management_support(
318        &mut self,
319        responder: wlan_sme::responder::Responder<fidl_common::SpectrumManagementSupport>,
320    ) -> Result<(), Error> {
321        let support = device::try_query_spectrum_management_support(&mut self.ctx.device).await?;
322        responder.respond(support);
323        Ok(())
324    }
325
326    pub async fn handle_mlme_req(&mut self, req: wlan_sme::MlmeRequest) -> Result<(), Error> {
327        use wlan_sme::MlmeRequest as Req;
328        match req {
329            Req::Start(req) => self.handle_mlme_start_req(req).await,
330            Req::Stop(req) => self.handle_mlme_stop_req(req).await,
331            Req::SetKeys(req) => self.handle_mlme_setkeys_req(req).await,
332            Req::QueryDeviceInfo(responder) => self.handle_mlme_query_device_info(responder).await,
333            Req::QueryMacSublayerSupport(responder) => {
334                self.handle_mlme_query_mac_sublayer_support(responder).await
335            }
336            Req::QuerySecuritySupport(responder) => {
337                self.handle_mlme_query_security_support(responder).await
338            }
339            Req::QuerySpectrumManagementSupport(responder) => {
340                self.handle_mlme_query_spectrum_management_support(responder).await
341            }
342            Req::ListMinstrelPeers(responder) => self.handle_sme_list_minstrel_peers(responder),
343            Req::GetMinstrelStats(req, responder) => {
344                self.handle_sme_get_minstrel_stats(responder, &req.peer_addr.into())
345            }
346            Req::AuthResponse(resp) => {
347                // TODO(https://fxbug.dev/42172646) - Added to help investigate hw-sim test. Remove later
348                info!("Handling MLME auth resp. self.bss.is_some()?: {}", self.bss.is_some());
349                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_auth_resp(&mut self.ctx, resp).await
350            }
351            Req::Deauthenticate(req) => {
352                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_deauth_req(&mut self.ctx, req).await
353            }
354            Req::AssocResponse(resp) => {
355                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_assoc_resp(&mut self.ctx, resp).await
356            }
357            Req::Disassociate(req) => {
358                self.bss
359                    .as_mut()
360                    .ok_or_bss_err()?
361                    .handle_mlme_disassoc_req(&mut self.ctx, req)
362                    .await
363            }
364            Req::SetCtrlPort(req) => {
365                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_set_controlled_port_req(req)
366            }
367            Req::Eapol(req) => {
368                self.bss.as_mut().ok_or_bss_err()?.handle_mlme_eapol_req(&mut self.ctx, req)
369            }
370            _ => Err(Error::Status(format!("not supported"), zx::Status::NOT_SUPPORTED)),
371        }
372        .map_err(|e| {
373            error!("error handling MLME message: {}", e);
374            e
375        })
376    }
377
378    pub fn handle_eth_frame_tx(&mut self, frame: &[u8], async_id: trace::Id) {
379        let bss = match self.bss.as_mut() {
380            Some(bss) => bss,
381            None => {
382                error!("received Ethernet frame but BSS was not started yet");
383                return;
384            }
385        };
386
387        let mac::EthernetFrame { hdr, body } =
388            match mac::EthernetFrame::parse(frame).ok_or_else(|| Rejection::FrameMalformed) {
389                Ok(eth_frame) => eth_frame,
390                Err(e) => {
391                    error!("failed to parse Ethernet frame: {}", e);
392                    return;
393                }
394            };
395
396        if let Err(e) = bss.handle_eth_frame(&mut self.ctx, *hdr, body, async_id) {
397            e.log("failed to handle Ethernet frame")
398        }
399    }
400
401    pub async fn handle_mac_frame_rx<B: SplitByteSlice>(
402        &mut self,
403        bytes: B,
404        rx_info: fidl_softmac::WlanRxInfo,
405        async_id: trace::Id,
406    ) {
407        let bss = match self.bss.as_mut() {
408            Some(bss) => bss,
409            None => {
410                error!("received WLAN frame but BSS was not started yet");
411                wtrace::async_end_wlansoftmac_rx(async_id, "BSS not started");
412                return;
413            }
414        };
415
416        // Rogue frames received from the wrong channel
417        if rx_info.channel.primary != bss.channel {
418            wtrace::async_end_wlansoftmac_rx(async_id, "frame from wrong channel");
419            return;
420        }
421
422        let body_aligned = (rx_info.rx_flags & fidl_softmac::WlanRxInfoFlags::FRAME_BODY_PADDING_4)
423            != fidl_softmac::WlanRxInfoFlags::empty();
424
425        let mac_frame = match mac::MacFrame::parse(bytes, body_aligned) {
426            Some(mac_frame) => mac_frame,
427            None => {
428                error!("failed to parse MAC frame");
429                wtrace::async_end_wlansoftmac_rx(async_id, "failed to parse frame");
430                return;
431            }
432        };
433
434        if let Err(e) = match mac_frame {
435            mac::MacFrame::Mgmt(mgmt) => bss.handle_mgmt_frame(&mut self.ctx, mgmt).await,
436            mac::MacFrame::Data(data_frame) => bss.handle_data_frame(&mut self.ctx, data_frame),
437            mac::MacFrame::Ctrl(ctrl_frame) => bss.handle_ctrl_frame(&mut self.ctx, ctrl_frame),
438            mac::MacFrame::Unsupported { frame_ctrl } => {
439                error!("received unsupported MAC frame: frame_ctrl = {:?}", frame_ctrl);
440                wtrace::async_end_wlansoftmac_rx(async_id, "received unsupported frame");
441                return;
442            }
443        } {
444            wtrace::async_end_wlansoftmac_rx(async_id, "failed to handle frame");
445            e.log("failed to handle MAC frame")
446        } else {
447            wtrace::async_end_wlansoftmac_rx(async_id, "successfully handled frame");
448        }
449    }
450}
451
452#[cfg(test)]
453mod tests {
454    use super::*;
455    use crate::device::{test_utils, FakeDevice, FakeDeviceConfig, FakeDeviceState, LinkStatus};
456    use crate::test_utils::MockWlanRxInfo;
457    use fuchsia_sync::Mutex;
458    use ieee80211::MacAddrBytes;
459    use lazy_static::lazy_static;
460    use std::sync::Arc;
461    use wlan_common::big_endian::BigEndianU16;
462    use wlan_common::test_utils::fake_frames::fake_wpa2_rsne;
463    use wlan_common::{assert_variant, timer};
464    use wlan_frame_writer::write_frame_to_vec;
465    use wlan_sme::responder::Responder;
466    use {
467        fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
468        fidl_fuchsia_wlan_softmac as fidl_softmac,
469    };
470
471    lazy_static! {
472        static ref CLIENT_ADDR: MacAddr = [4u8; 6].into();
473        static ref BSSID: Bssid = [2u8; 6].into();
474        static ref CLIENT_ADDR2: MacAddr = [6u8; 6].into();
475    }
476
477    fn make_eth_frame(
478        dst_addr: MacAddr,
479        src_addr: MacAddr,
480        protocol_id: u16,
481        body: &[u8],
482    ) -> Vec<u8> {
483        write_frame_to_vec!({
484            headers: {
485                mac::EthernetIIHdr: &mac::EthernetIIHdr {
486                    da: dst_addr,
487                    sa: src_addr,
488                    ether_type: BigEndianU16::from_native(protocol_id),
489                },
490            },
491            payload: body,
492        })
493        .unwrap() // This frame construction never fails
494    }
495
496    // TODO(https://fxbug.dev/327499461): This function is async to ensure MLME functions will
497    // run in an async context and not call `wlan_common::timer::Timer::now` without an
498    // executor.
499    async fn make_ap(
500    ) -> (Ap<FakeDevice>, Arc<Mutex<FakeDeviceState>>, timer::EventStream<TimedEvent>) {
501        let (timer, time_stream) = timer::create_timer();
502        let (fake_device, fake_device_state) = FakeDevice::new_with_config(
503            FakeDeviceConfig::default()
504                .with_mock_mac_role(fidl_common::WlanMacRole::Ap)
505                .with_mock_sta_addr((*BSSID).to_array()),
506        )
507        .await;
508        (Ap::new(fake_device, timer, *BSSID), fake_device_state, time_stream)
509    }
510
511    #[fuchsia::test(allow_stalls = false)]
512    async fn ap_handle_eth_frame() {
513        let (mut ap, fake_device_state, _) = make_ap().await;
514        ap.bss.replace(
515            InfraBss::new(
516                &mut ap.ctx,
517                Ssid::try_from("coolnet").unwrap(),
518                TimeUnit::DEFAULT_BEACON_INTERVAL,
519                2,
520                CapabilityInfo(0),
521                vec![0b11111000],
522                1,
523                None,
524            )
525            .await
526            .expect("expected InfraBss::new ok"),
527        );
528        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
529
530        let client = ap.bss.as_mut().unwrap().clients.get_mut(&CLIENT_ADDR).unwrap();
531        client
532            .handle_mlme_auth_resp(&mut ap.ctx, fidl_mlme::AuthenticateResultCode::Success)
533            .await
534            .expect("expected OK");
535        client
536            .handle_mlme_assoc_resp(
537                &mut ap.ctx,
538                false,
539                1,
540                mac::CapabilityInfo(0),
541                fidl_mlme::AssociateResultCode::Success,
542                1,
543                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
544            )
545            .await
546            .expect("expected OK");
547        fake_device_state.lock().wlan_queue.clear();
548
549        ap.handle_eth_frame_tx(
550            &make_eth_frame(*CLIENT_ADDR, *CLIENT_ADDR2, 0x1234, &[1, 2, 3, 4, 5][..]),
551            0.into(),
552        );
553
554        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
555        assert_eq!(
556            &fake_device_state.lock().wlan_queue[0].0[..],
557            &[
558                // Mgmt header
559                0b00001000, 0b00000010, // Frame Control
560                0, 0, // Duration
561                4, 4, 4, 4, 4, 4, // addr1
562                2, 2, 2, 2, 2, 2, // addr2
563                6, 6, 6, 6, 6, 6, // addr3
564                0x30, 0, // Sequence Control
565                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
566                0, 0, 0, // OUI
567                0x12, 0x34, // Protocol ID
568                // Data
569                1, 2, 3, 4, 5,
570            ][..]
571        );
572    }
573
574    #[fuchsia::test(allow_stalls = false)]
575    async fn ap_handle_eth_frame_no_such_client() {
576        let (mut ap, _, _) = make_ap().await;
577        ap.bss.replace(
578            InfraBss::new(
579                &mut ap.ctx,
580                Ssid::try_from("coolnet").unwrap(),
581                TimeUnit::DEFAULT_BEACON_INTERVAL,
582                2,
583                CapabilityInfo(0),
584                vec![0b11111000],
585                1,
586                None,
587            )
588            .await
589            .expect("expected InfraBss::new ok"),
590        );
591        ap.handle_eth_frame_tx(
592            &make_eth_frame(*CLIENT_ADDR2, *CLIENT_ADDR, 0x1234, &[1, 2, 3, 4, 5][..]),
593            0.into(),
594        );
595    }
596
597    fn mock_rx_info(ap: &Ap<FakeDevice>) -> fidl_softmac::WlanRxInfo {
598        let channel = fidl_common::WlanChannel {
599            primary: ap.bss.as_ref().unwrap().channel,
600            cbw: fidl_common::ChannelBandwidth::Cbw20,
601            secondary80: 0,
602        };
603        MockWlanRxInfo::with_channel(channel).into()
604    }
605
606    #[fuchsia::test(allow_stalls = false)]
607    async fn ap_handle_mac_frame() {
608        let (mut ap, fake_device_state, _) = make_ap().await;
609        ap.bss.replace(
610            InfraBss::new(
611                &mut ap.ctx,
612                Ssid::try_from("coolnet").unwrap(),
613                TimeUnit::DEFAULT_BEACON_INTERVAL,
614                2,
615                CapabilityInfo(0),
616                vec![0b11111000],
617                1,
618                None,
619            )
620            .await
621            .expect("expected InfraBss::new ok"),
622        );
623        ap.handle_mac_frame_rx(
624            &[
625                // Mgmt header
626                0b10110000, 0b00000000, // Frame Control
627                0, 0, // Duration
628                2, 2, 2, 2, 2, 2, // addr1
629                4, 4, 4, 4, 4, 4, // addr2
630                2, 2, 2, 2, 2, 2, // addr3
631                0x10, 0, // Sequence Control
632                // Auth body
633                0, 0, // Auth Algorithm Number
634                1, 0, // Auth Txn Seq Number
635                0, 0, // Status code
636            ][..],
637            mock_rx_info(&ap),
638            0.into(),
639        )
640        .await;
641
642        assert_eq!(ap.bss.as_mut().unwrap().clients.contains_key(&CLIENT_ADDR), true);
643
644        let msg = fake_device_state
645            .lock()
646            .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
647            .expect("expected MLME message");
648        assert_eq!(
649            msg,
650            fidl_mlme::AuthenticateIndication {
651                peer_sta_address: CLIENT_ADDR.to_array(),
652                auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
653            },
654        );
655    }
656
657    #[fuchsia::test(allow_stalls = false)]
658    async fn ap_handle_mac_frame_ps_poll() {
659        let (mut ap, fake_device_state, _) = make_ap().await;
660        ap.bss.replace(
661            InfraBss::new(
662                &mut ap.ctx,
663                Ssid::try_from("coolnet").unwrap(),
664                TimeUnit::DEFAULT_BEACON_INTERVAL,
665                2,
666                CapabilityInfo(0),
667                vec![0b11111000],
668                1,
669                None,
670            )
671            .await
672            .expect("expected InfraBss::new ok"),
673        );
674        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
675
676        let client = ap.bss.as_mut().unwrap().clients.get_mut(&CLIENT_ADDR).unwrap();
677        client
678            .handle_mlme_auth_resp(&mut ap.ctx, fidl_mlme::AuthenticateResultCode::Success)
679            .await
680            .expect("expected OK");
681        client
682            .handle_mlme_assoc_resp(
683                &mut ap.ctx,
684                false,
685                1,
686                mac::CapabilityInfo(0),
687                fidl_mlme::AssociateResultCode::Success,
688                1,
689                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
690            )
691            .await
692            .expect("expected OK");
693        fake_device_state.lock().wlan_queue.clear();
694
695        // Put the client into dozing.
696        ap.handle_mac_frame_rx(
697            &[
698                0b01001000, 0b00010001, // Frame control.
699                0, 0, // Duration.
700                2, 2, 2, 2, 2, 2, // BSSID.
701                4, 4, 4, 4, 4, 4, // MAC address.
702                2, 2, 2, 2, 2, 2, // BSSID.
703                0x10, 0, // Sequence control.
704            ][..],
705            mock_rx_info(&ap),
706            0.into(),
707        )
708        .await;
709
710        ap.handle_eth_frame_tx(
711            &make_eth_frame(*CLIENT_ADDR, *CLIENT_ADDR2, 0x1234, &[1, 2, 3, 4, 5][..]),
712            0.into(),
713        );
714        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
715
716        // Send a PS-Poll.
717        ap.handle_mac_frame_rx(
718            &[
719                // Ctrl header
720                0b10100100, 0b00000000, // Frame Control
721                0b00000001, 0b11000000, // Masked AID
722                2, 2, 2, 2, 2, 2, // addr1
723                4, 4, 4, 4, 4, 4, // addr2
724            ][..],
725            mock_rx_info(&ap),
726            0.into(),
727        )
728        .await;
729
730        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
731        assert_eq!(
732            &fake_device_state.lock().wlan_queue[0].0[..],
733            &[
734                // Mgmt header
735                0b00001000, 0b00000010, // Frame Control
736                0, 0, // Duration
737                4, 4, 4, 4, 4, 4, // addr1
738                2, 2, 2, 2, 2, 2, // addr2
739                6, 6, 6, 6, 6, 6, // addr3
740                0x30, 0, // Sequence Control
741                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
742                0, 0, 0, // OUI
743                0x12, 0x34, // Protocol ID
744                // Data
745                1, 2, 3, 4, 5,
746            ][..]
747        );
748    }
749
750    #[fuchsia::test(allow_stalls = false)]
751    async fn ap_handle_mac_frame_no_such_client() {
752        let (mut ap, _, _) = make_ap().await;
753        ap.bss.replace(
754            InfraBss::new(
755                &mut ap.ctx,
756                Ssid::try_from("coolnet").unwrap(),
757                TimeUnit::DEFAULT_BEACON_INTERVAL,
758                2,
759                CapabilityInfo(0),
760                vec![0b11111000],
761                1,
762                None,
763            )
764            .await
765            .expect("expected InfraBss::new ok"),
766        );
767        ap.handle_mac_frame_rx(
768            &[
769                // Mgmt header
770                0b10100000, 0b00000001, // Frame Control
771                0, 0, // Duration
772                2, 2, 2, 2, 2, 2, // addr1
773                4, 4, 4, 4, 4, 4, // addr2
774                2, 2, 2, 2, 2, 2, // addr3
775                0x10, 0, // Sequence Control
776                // Disassoc header:
777                8, 0, // reason code
778            ][..],
779            mock_rx_info(&ap),
780            0.into(),
781        )
782        .await;
783
784        assert_eq!(ap.bss.as_mut().unwrap().clients.contains_key(&CLIENT_ADDR), false);
785    }
786
787    #[fuchsia::test(allow_stalls = false)]
788    async fn ap_handle_mac_frame_bogus() {
789        let (mut ap, _, _) = make_ap().await;
790        ap.bss.replace(
791            InfraBss::new(
792                &mut ap.ctx,
793                Ssid::try_from("coolnet").unwrap(),
794                TimeUnit::DEFAULT_BEACON_INTERVAL,
795                2,
796                CapabilityInfo(0),
797                vec![0b11111000],
798                1,
799                None,
800            )
801            .await
802            .expect("expected InfraBss::new ok"),
803        );
804        ap.handle_mac_frame_rx(
805            &[0][..],
806            fidl_softmac::WlanRxInfo {
807                rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
808                valid_fields: fidl_softmac::WlanRxInfoValid::empty(),
809                phy: fidl_common::WlanPhyType::Dsss,
810                data_rate: 0,
811                channel: fidl_common::WlanChannel {
812                    primary: 0,
813                    cbw: fidl_common::ChannelBandwidth::Cbw20,
814                    secondary80: 0,
815                },
816                mcs: 0,
817                rssi_dbm: 0,
818                snr_dbh: 0,
819            },
820            0.into(),
821        )
822        .await;
823    }
824
825    #[fuchsia::test(allow_stalls = false)]
826    async fn ap_handle_mac_frame_wrong_channel_drop() {
827        let (mut ap, fake_device_state, _) = make_ap().await;
828        ap.bss.replace(
829            InfraBss::new(
830                &mut ap.ctx,
831                Ssid::try_from("coolnet").unwrap(),
832                TimeUnit::DEFAULT_BEACON_INTERVAL,
833                2,
834                CapabilityInfo(0),
835                vec![0b11111000],
836                1,
837                None,
838            )
839            .await
840            .expect("expected InfraBss::new ok"),
841        );
842        let probe_req = [
843            // Mgmt header
844            0b01000000, 0b00000000, // Frame Control
845            0, 0, // Duration
846            2, 2, 2, 2, 2, 2, // addr1
847            4, 4, 4, 4, 4, 4, // addr2
848            2, 2, 2, 2, 2, 2, // addr3
849            0x10, 0, // Sequence Control
850            // SSID
851            0, 7, 0x63, 0x6f, 0x6f, 0x6c, 0x6e, 0x65, 0x74, 0x0a,
852        ];
853        let rx_info_wrong_channel = fidl_softmac::WlanRxInfo {
854            rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
855            valid_fields: fidl_softmac::WlanRxInfoValid::empty(),
856            phy: fidl_common::WlanPhyType::Dsss,
857            data_rate: 0,
858            channel: fidl_common::WlanChannel {
859                primary: 0,
860                cbw: fidl_common::ChannelBandwidth::Cbw20,
861                secondary80: 0,
862            },
863            mcs: 0,
864            rssi_dbm: 0,
865            snr_dbh: 0,
866        };
867        ap.handle_mac_frame_rx(&probe_req[..], rx_info_wrong_channel.clone(), 0.into()).await;
868
869        // Probe Request from the wrong channel should be dropped and no probe response sent.
870        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
871
872        // Frame from the same channel must be processed and a probe response sent.
873        let rx_info_same_channel = fidl_softmac::WlanRxInfo {
874            channel: fidl_common::WlanChannel {
875                primary: 1,
876                cbw: fidl_common::ChannelBandwidth::Cbw20,
877                secondary80: 0,
878            },
879            ..rx_info_wrong_channel
880        };
881        fake_device_state.lock().wlan_queue.clear();
882        ap.handle_mac_frame_rx(&probe_req[..], rx_info_same_channel, 0.into()).await;
883        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
884    }
885
886    #[fuchsia::test(allow_stalls = false)]
887    async fn ap_handle_mlme_start_req() {
888        let (mut ap, fake_device_state, _) = make_ap().await;
889        ap.handle_mlme_start_req(fidl_mlme::StartRequest {
890            ssid: Ssid::try_from("coolnet").unwrap().into(),
891            bss_type: fidl_common::BssType::Infrastructure,
892            beacon_period: 5,
893            dtim_period: 1,
894            channel: 2,
895            capability_info: CapabilityInfo(0).raw(),
896            rates: vec![0b11111000],
897            country: fidl_mlme::Country { alpha2: *b"xx", suffix: fidl_mlme::COUNTRY_ENVIRON_ALL },
898            mesh_id: vec![],
899            rsne: None,
900            phy: fidl_common::WlanPhyType::Erp,
901            channel_bandwidth: fidl_common::ChannelBandwidth::Cbw20,
902        })
903        .await
904        .expect("expected Ap::handle_mlme_start_request OK");
905
906        assert!(ap.bss.is_some());
907        assert_eq!(
908            fake_device_state.lock().wlan_channel,
909            fidl_common::WlanChannel {
910                primary: 2,
911                // TODO(https://fxbug.dev/42116942): Correctly support this.
912                cbw: fidl_common::ChannelBandwidth::Cbw20,
913                secondary80: 0,
914            }
915        );
916        assert_eq!(fake_device_state.lock().link_status, LinkStatus::UP);
917
918        let msg = fake_device_state
919            .lock()
920            .next_mlme_msg::<fidl_mlme::StartConfirm>()
921            .expect("expected MLME message");
922        assert_eq!(
923            msg,
924            fidl_mlme::StartConfirm { result_code: fidl_mlme::StartResultCode::Success },
925        );
926    }
927
928    #[fuchsia::test(allow_stalls = false)]
929    async fn ap_handle_mlme_start_req_already_started() {
930        let (mut ap, fake_device_state, _) = make_ap().await;
931        ap.bss.replace(
932            InfraBss::new(
933                &mut ap.ctx,
934                Ssid::try_from("coolnet").unwrap(),
935                TimeUnit::DEFAULT_BEACON_INTERVAL,
936                2,
937                CapabilityInfo(0),
938                vec![0b11111000],
939                1,
940                None,
941            )
942            .await
943            .expect("expected InfraBss::new ok"),
944        );
945
946        ap.handle_mlme_start_req(fidl_mlme::StartRequest {
947            ssid: Ssid::try_from("coolnet").unwrap().into(),
948            bss_type: fidl_common::BssType::Infrastructure,
949            beacon_period: 5,
950            dtim_period: 1,
951            channel: 2,
952            capability_info: CapabilityInfo(0).raw(),
953            rates: vec![],
954            country: fidl_mlme::Country { alpha2: *b"xx", suffix: fidl_mlme::COUNTRY_ENVIRON_ALL },
955            mesh_id: vec![],
956            rsne: None,
957            phy: fidl_common::WlanPhyType::Erp,
958            channel_bandwidth: fidl_common::ChannelBandwidth::Cbw20,
959        })
960        .await
961        .expect("expected Ap::handle_mlme_start_request OK");
962
963        let msg = fake_device_state
964            .lock()
965            .next_mlme_msg::<fidl_mlme::StartConfirm>()
966            .expect("expected MLME message");
967        assert_eq!(
968            msg,
969            fidl_mlme::StartConfirm {
970                result_code: fidl_mlme::StartResultCode::BssAlreadyStartedOrJoined
971            },
972        );
973    }
974
975    #[fuchsia::test(allow_stalls = false)]
976    async fn ap_handle_mlme_stop_req() {
977        let (mut ap, fake_device_state, _) = make_ap().await;
978        ap.bss.replace(
979            InfraBss::new(
980                &mut ap.ctx,
981                Ssid::try_from("coolnet").unwrap(),
982                TimeUnit::DEFAULT_BEACON_INTERVAL,
983                2,
984                CapabilityInfo(0),
985                vec![0b11111000],
986                1,
987                None,
988            )
989            .await
990            .expect("expected InfraBss::new ok"),
991        );
992
993        ap.handle_mlme_stop_req(fidl_mlme::StopRequest {
994            ssid: Ssid::try_from("coolnet").unwrap().into(),
995        })
996        .await
997        .expect("expected Ap::handle_mlme_stop_request OK");
998        assert!(ap.bss.is_none());
999        assert_eq!(fake_device_state.lock().link_status, LinkStatus::DOWN);
1000
1001        let msg = fake_device_state
1002            .lock()
1003            .next_mlme_msg::<fidl_mlme::StopConfirm>()
1004            .expect("expected MLME message");
1005        assert_eq!(msg, fidl_mlme::StopConfirm { result_code: fidl_mlme::StopResultCode::Success },);
1006    }
1007
1008    #[fuchsia::test(allow_stalls = false)]
1009    async fn ap_handle_mlme_stop_req_already_stopped() {
1010        let (mut ap, fake_device_state, _) = make_ap().await;
1011
1012        ap.handle_mlme_stop_req(fidl_mlme::StopRequest {
1013            ssid: Ssid::try_from("coolnet").unwrap().into(),
1014        })
1015        .await
1016        .expect("expected Ap::handle_mlme_stop_request OK");
1017        assert!(ap.bss.is_none());
1018
1019        let msg = fake_device_state
1020            .lock()
1021            .next_mlme_msg::<fidl_mlme::StopConfirm>()
1022            .expect("expected MLME message");
1023        assert_eq!(
1024            msg,
1025            fidl_mlme::StopConfirm { result_code: fidl_mlme::StopResultCode::BssAlreadyStopped },
1026        );
1027    }
1028
1029    #[fuchsia::test(allow_stalls = false)]
1030    async fn ap_handle_mlme_setkeys_req() {
1031        let (mut ap, fake_device_state, _) = make_ap().await;
1032        ap.bss.replace(
1033            InfraBss::new(
1034                &mut ap.ctx,
1035                Ssid::try_from("coolnet").unwrap(),
1036                TimeUnit::DEFAULT_BEACON_INTERVAL,
1037                2,
1038                CapabilityInfo(0),
1039                vec![0b11111000],
1040                1,
1041                Some(fake_wpa2_rsne()),
1042            )
1043            .await
1044            .expect("expected InfraBss::new ok"),
1045        );
1046
1047        ap.handle_mlme_setkeys_req(fidl_mlme::SetKeysRequest {
1048            keylist: vec![fidl_mlme::SetKeyDescriptor {
1049                cipher_suite_oui: [1, 2, 3],
1050                cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
1051                key_type: fidl_mlme::KeyType::Pairwise,
1052                address: [5; 6],
1053                key_id: 6,
1054                key: vec![1, 2, 3, 4, 5, 6, 7],
1055                rsc: 8,
1056            }],
1057        })
1058        .await
1059        .expect("expected Ap::handle_mlme_setkeys_req OK");
1060        assert_eq!(
1061            fake_device_state.lock().keys,
1062            vec![fidl_softmac::WlanKeyConfiguration {
1063                protection: Some(fidl_softmac::WlanProtection::RxTx),
1064                cipher_oui: Some([1, 2, 3]),
1065                cipher_type: Some(4),
1066                key_type: Some(fidl_ieee80211::KeyType::Pairwise),
1067                peer_addr: Some([5; 6]),
1068                key_idx: Some(6),
1069                key: Some(vec![1, 2, 3, 4, 5, 6, 7]),
1070                rsc: Some(8),
1071                ..Default::default()
1072            }]
1073        );
1074    }
1075
1076    #[fuchsia::test(allow_stalls = false)]
1077    async fn ap_handle_mlme_setkeys_req_no_bss() {
1078        let (mut ap, _, _) = make_ap().await;
1079        assert_variant!(
1080            ap.handle_mlme_setkeys_req(fidl_mlme::SetKeysRequest {
1081                keylist: vec![fidl_mlme::SetKeyDescriptor {
1082                    cipher_suite_oui: [1, 2, 3],
1083                    cipher_suite_type:
1084                        fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
1085                    key_type: fidl_mlme::KeyType::Pairwise,
1086                    address: [5; 6],
1087                    key_id: 6,
1088                    key: vec![1, 2, 3, 4, 5, 6, 7],
1089                    rsc: 8,
1090                }],
1091            })
1092            .await
1093            .expect_err("expected Ap::handle_mlme_setkeys_req error"),
1094            Error::Status(_, zx::Status::BAD_STATE)
1095        );
1096    }
1097
1098    #[fuchsia::test(allow_stalls = false)]
1099    async fn ap_handle_mlme_setkeys_req_bss_no_rsne() {
1100        let (mut ap, _, _) = make_ap().await;
1101        ap.bss.replace(
1102            InfraBss::new(
1103                &mut ap.ctx,
1104                Ssid::try_from("coolnet").unwrap(),
1105                TimeUnit::DEFAULT_BEACON_INTERVAL,
1106                2,
1107                CapabilityInfo(0),
1108                vec![0b11111000],
1109                1,
1110                None,
1111            )
1112            .await
1113            .expect("expected InfraBss::new ok"),
1114        );
1115
1116        assert_variant!(
1117            ap.handle_mlme_setkeys_req(fidl_mlme::SetKeysRequest {
1118                keylist: vec![fidl_mlme::SetKeyDescriptor {
1119                    cipher_suite_oui: [1, 2, 3],
1120                    cipher_suite_type:
1121                        fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
1122                    key_type: fidl_mlme::KeyType::Pairwise,
1123                    address: [5; 6],
1124                    key_id: 6,
1125                    key: vec![1, 2, 3, 4, 5, 6, 7],
1126                    rsc: 8,
1127                }],
1128            })
1129            .await
1130            .expect_err("expected Ap::handle_mlme_setkeys_req error"),
1131            Error::Status(_, zx::Status::BAD_STATE)
1132        );
1133    }
1134
1135    #[fuchsia::test(allow_stalls = false)]
1136    async fn ap_handle_mlme_req_handle_mlme_auth_resp() {
1137        let (mut ap, fake_device_state, _) = make_ap().await;
1138        ap.bss.replace(
1139            InfraBss::new(
1140                &mut ap.ctx,
1141                Ssid::try_from("coolnet").unwrap(),
1142                TimeUnit::DEFAULT_BEACON_INTERVAL,
1143                2,
1144                CapabilityInfo(0),
1145                vec![0b11111000],
1146                1,
1147                None,
1148            )
1149            .await
1150            .expect("expected InfraBss::new ok"),
1151        );
1152        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1153
1154        ap.handle_mlme_req(wlan_sme::MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
1155            peer_sta_address: CLIENT_ADDR.to_array(),
1156            result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
1157        }))
1158        .await
1159        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AuthenticateResp) ok");
1160        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1161        assert_eq!(
1162            &fake_device_state.lock().wlan_queue[0].0[..],
1163            &[
1164                // Mgmt header
1165                0b10110000, 0, // Frame Control
1166                0, 0, // Duration
1167                4, 4, 4, 4, 4, 4, // addr1
1168                2, 2, 2, 2, 2, 2, // addr2
1169                2, 2, 2, 2, 2, 2, // addr3
1170                0x10, 0, // Sequence Control
1171                // Auth header:
1172                0, 0, // auth algorithm
1173                2, 0, // auth txn seq num
1174                76, 0, // status code
1175            ][..]
1176        );
1177    }
1178
1179    #[fuchsia::test(allow_stalls = false)]
1180    async fn ap_handle_mlme_req_handle_mlme_auth_resp_no_bss() {
1181        let (mut ap, _, _) = make_ap().await;
1182
1183        assert_eq!(
1184            zx::Status::from(
1185                ap.handle_mlme_req(wlan_sme::MlmeRequest::AuthResponse(
1186                    fidl_mlme::AuthenticateResponse {
1187                        peer_sta_address: CLIENT_ADDR.to_array(),
1188                        result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
1189                    }
1190                ))
1191                .await
1192                .expect_err(
1193                    "expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AuthenticateResp) error"
1194                )
1195            ),
1196            zx::Status::BAD_STATE
1197        );
1198    }
1199
1200    #[fuchsia::test(allow_stalls = false)]
1201    async fn ap_handle_mlme_req_handle_mlme_auth_resp_no_such_client() {
1202        let (mut ap, _, _) = make_ap().await;
1203        ap.bss.replace(
1204            InfraBss::new(
1205                &mut ap.ctx,
1206                Ssid::try_from("coolnet").unwrap(),
1207                TimeUnit::DEFAULT_BEACON_INTERVAL,
1208                2,
1209                CapabilityInfo(0),
1210                vec![0b11111000],
1211                1,
1212                None,
1213            )
1214            .await
1215            .expect("expected InfraBss::new ok"),
1216        );
1217
1218        assert_eq!(
1219            zx::Status::from(
1220                ap.handle_mlme_req(wlan_sme::MlmeRequest::AuthResponse(
1221                    fidl_mlme::AuthenticateResponse {
1222                        peer_sta_address: CLIENT_ADDR.to_array(),
1223                        result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
1224                    }
1225                ))
1226                .await
1227                .expect_err(
1228                    "expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AuthenticateResp) error"
1229                )
1230            ),
1231            zx::Status::NOT_FOUND
1232        );
1233    }
1234
1235    #[fuchsia::test(allow_stalls = false)]
1236    async fn ap_handle_mlme_req_handle_mlme_deauth_req() {
1237        let (mut ap, fake_device_state, _) = make_ap().await;
1238        ap.bss.replace(
1239            InfraBss::new(
1240                &mut ap.ctx,
1241                Ssid::try_from("coolnet").unwrap(),
1242                TimeUnit::DEFAULT_BEACON_INTERVAL,
1243                2,
1244                CapabilityInfo(0),
1245                vec![0b11111000],
1246                1,
1247                None,
1248            )
1249            .await
1250            .expect("expected InfraBss::new ok"),
1251        );
1252        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1253
1254        ap.handle_mlme_req(wlan_sme::MlmeRequest::Deauthenticate(
1255            fidl_mlme::DeauthenticateRequest {
1256                peer_sta_address: CLIENT_ADDR.to_array(),
1257                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
1258            },
1259        ))
1260        .await
1261        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::DeauthenticateReq) ok");
1262        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1263        assert_eq!(
1264            &fake_device_state.lock().wlan_queue[0].0[..],
1265            &[
1266                // Mgmt header
1267                0b11000000, 0, // Frame Control
1268                0, 0, // Duration
1269                4, 4, 4, 4, 4, 4, // addr1
1270                2, 2, 2, 2, 2, 2, // addr2
1271                2, 2, 2, 2, 2, 2, // addr3
1272                0x10, 0, // Sequence Control
1273                // Deauth header:
1274                3, 0, // reason code
1275            ][..]
1276        );
1277    }
1278
1279    #[fuchsia::test(allow_stalls = false)]
1280    async fn ap_handle_mlme_req_handle_mlme_assoc_resp() {
1281        let (mut ap, fake_device_state, _) = make_ap().await;
1282        ap.bss.replace(
1283            InfraBss::new(
1284                &mut ap.ctx,
1285                Ssid::try_from("coolnet").unwrap(),
1286                TimeUnit::DEFAULT_BEACON_INTERVAL,
1287                2,
1288                CapabilityInfo(0),
1289                vec![0b11111000],
1290                1,
1291                None,
1292            )
1293            .await
1294            .expect("expected InfraBss::new ok"),
1295        );
1296        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1297
1298        ap.handle_mlme_req(wlan_sme::MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1299            peer_sta_address: CLIENT_ADDR.to_array(),
1300            result_code: fidl_mlme::AssociateResultCode::Success,
1301            association_id: 1,
1302            capability_info: CapabilityInfo(0).raw(),
1303            rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
1304        }))
1305        .await
1306        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AssociateResp) ok");
1307        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1308        assert_eq!(
1309            &fake_device_state.lock().wlan_queue[0].0[..],
1310            &[
1311                // Mgmt header
1312                0b00010000, 0, // Frame Control
1313                0, 0, // Duration
1314                4, 4, 4, 4, 4, 4, // addr1
1315                2, 2, 2, 2, 2, 2, // addr2
1316                2, 2, 2, 2, 2, 2, // addr3
1317                0x10, 0, // Sequence Control
1318                // Association response header:
1319                0, 0, // Capabilities
1320                0, 0, // status code
1321                1, 0, // AID
1322                // IEs
1323                1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
1324                50, 2, 9, 10, // Extended rates
1325                90, 3, 90, 0, 0, // BSS max idle period
1326            ][..]
1327        );
1328    }
1329
1330    #[fuchsia::test(allow_stalls = false)]
1331    async fn ap_handle_mlme_req_handle_mlme_disassoc_req() {
1332        let (mut ap, fake_device_state, _) = make_ap().await;
1333        ap.bss.replace(
1334            InfraBss::new(
1335                &mut ap.ctx,
1336                Ssid::try_from("coolnet").unwrap(),
1337                TimeUnit::DEFAULT_BEACON_INTERVAL,
1338                2,
1339                CapabilityInfo(0),
1340                vec![0b11111000],
1341                1,
1342                None,
1343            )
1344            .await
1345            .expect("expected InfraBss::new ok"),
1346        );
1347        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1348
1349        ap.handle_mlme_req(wlan_sme::MlmeRequest::Disassociate(fidl_mlme::DisassociateRequest {
1350            peer_sta_address: CLIENT_ADDR.to_array(),
1351            reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
1352        }))
1353        .await
1354        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::DisassociateReq) ok");
1355        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1356        assert_eq!(
1357            &fake_device_state.lock().wlan_queue[0].0[..],
1358            &[
1359                // Mgmt header
1360                0b10100000, 0, // Frame Control
1361                0, 0, // Duration
1362                4, 4, 4, 4, 4, 4, // addr1
1363                2, 2, 2, 2, 2, 2, // addr2
1364                2, 2, 2, 2, 2, 2, // addr3
1365                0x10, 0, // Sequence Control
1366                // Disassoc header:
1367                8, 0, // reason code
1368            ][..]
1369        );
1370    }
1371
1372    #[fuchsia::test(allow_stalls = false)]
1373    async fn ap_handle_mlme_req_handle_mlme_set_controlled_port_req() {
1374        let (mut ap, _, _) = make_ap().await;
1375        ap.bss.replace(
1376            InfraBss::new(
1377                &mut ap.ctx,
1378                Ssid::try_from("coolnet").unwrap(),
1379                TimeUnit::DEFAULT_BEACON_INTERVAL,
1380                2,
1381                CapabilityInfo(0),
1382                vec![0b11111000],
1383                1,
1384                Some(fake_wpa2_rsne()),
1385            )
1386            .await
1387            .expect("expected InfraBss::new ok"),
1388        );
1389        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1390
1391        ap.handle_mlme_req(wlan_sme::MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1392            peer_sta_address: CLIENT_ADDR.to_array(),
1393            result_code: fidl_mlme::AssociateResultCode::Success,
1394            association_id: 1,
1395            capability_info: CapabilityInfo(0).raw(),
1396            rates: vec![1, 2, 3],
1397        }))
1398        .await
1399        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::AssociateResp) ok");
1400
1401        ap.handle_mlme_req(wlan_sme::MlmeRequest::SetCtrlPort(
1402            fidl_mlme::SetControlledPortRequest {
1403                peer_sta_address: CLIENT_ADDR.to_array(),
1404                state: fidl_mlme::ControlledPortState::Open,
1405            },
1406        ))
1407        .await
1408        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::SetControlledPort) ok");
1409    }
1410
1411    #[fuchsia::test(allow_stalls = false)]
1412    async fn ap_handle_mlme_req_handle_mlme_eapol_req() {
1413        let (mut ap, fake_device_state, _) = make_ap().await;
1414        ap.bss.replace(
1415            InfraBss::new(
1416                &mut ap.ctx,
1417                Ssid::try_from("coolnet").unwrap(),
1418                TimeUnit::DEFAULT_BEACON_INTERVAL,
1419                2,
1420                CapabilityInfo(0),
1421                vec![0b11111000],
1422                1,
1423                None,
1424            )
1425            .await
1426            .expect("expected InfraBss::new ok"),
1427        );
1428        ap.bss.as_mut().unwrap().clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1429
1430        ap.handle_mlme_req(wlan_sme::MlmeRequest::Eapol(fidl_mlme::EapolRequest {
1431            dst_addr: CLIENT_ADDR.to_array(),
1432            src_addr: BSSID.to_array(),
1433            data: vec![1, 2, 3],
1434        }))
1435        .await
1436        .expect("expected Ap::handle_mlme_msg(fidl_mlme::MlmeRequest::EapolReq) ok");
1437        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1438        assert_eq!(
1439            &fake_device_state.lock().wlan_queue[0].0[..],
1440            &[
1441                // Header
1442                0b00001000, 0b00000010, // Frame Control
1443                0, 0, // Duration
1444                4, 4, 4, 4, 4, 4, // addr1
1445                2, 2, 2, 2, 2, 2, // addr2
1446                2, 2, 2, 2, 2, 2, // addr3
1447                0x10, 0, // Sequence Control
1448                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1449                0, 0, 0, // OUI
1450                0x88, 0x8E, // EAPOL protocol ID
1451                // Data
1452                1, 2, 3,
1453            ][..]
1454        );
1455    }
1456
1457    #[fuchsia::test(allow_stalls = false)]
1458    async fn ap_mlme_respond_to_query_device_info() {
1459        let (mut ap, _, _) = make_ap().await;
1460
1461        let (responder, receiver) = Responder::new();
1462        assert_variant!(
1463            ap.handle_mlme_req(wlan_sme::MlmeRequest::QueryDeviceInfo(responder)).await,
1464            Ok(())
1465        );
1466        assert_eq!(
1467            receiver.await.unwrap(),
1468            fidl_mlme::DeviceInfo {
1469                sta_addr: BSSID.to_array(),
1470                role: fidl_common::WlanMacRole::Ap,
1471                bands: test_utils::fake_mlme_band_caps(),
1472                softmac_hardware_capability: 0,
1473                qos_capable: false,
1474            }
1475        );
1476    }
1477
1478    #[fuchsia::test(allow_stalls = false)]
1479    async fn ap_mlme_respond_to_query_mac_sublayer_support() {
1480        let (mut ap, _, _) = make_ap().await;
1481
1482        let (responder, receiver) = Responder::new();
1483        assert_variant!(
1484            ap.handle_mlme_req(wlan_sme::MlmeRequest::QueryMacSublayerSupport(responder)).await,
1485            Ok(())
1486        );
1487        let resp = receiver.await.unwrap();
1488        assert_eq!(resp.rate_selection_offload.supported, false);
1489        assert_eq!(resp.data_plane.data_plane_type, fidl_common::DataPlaneType::EthernetDevice);
1490        assert_eq!(resp.device.is_synthetic, true);
1491        assert_eq!(
1492            resp.device.mac_implementation_type,
1493            fidl_common::MacImplementationType::Softmac
1494        );
1495        assert_eq!(resp.device.tx_status_report_supported, true);
1496    }
1497
1498    #[fuchsia::test(allow_stalls = false)]
1499    async fn ap_mlme_respond_to_query_security_support() {
1500        let (mut ap, _, _) = make_ap().await;
1501
1502        let (responder, receiver) = Responder::new();
1503        assert_variant!(
1504            ap.handle_mlme_req(wlan_sme::MlmeRequest::QuerySecuritySupport(responder)).await,
1505            Ok(())
1506        );
1507        let resp = receiver.await.unwrap();
1508        assert_eq!(resp.mfp.supported, false);
1509        assert_eq!(resp.sae.driver_handler_supported, false);
1510        assert_eq!(resp.sae.sme_handler_supported, false);
1511    }
1512
1513    #[fuchsia::test(allow_stalls = false)]
1514    async fn ap_mlme_respond_to_query_spectrum_management_support() {
1515        let (mut ap, _, _) = make_ap().await;
1516
1517        let (responder, receiver) = Responder::new();
1518        assert_variant!(
1519            ap.handle_mlme_req(wlan_sme::MlmeRequest::QuerySpectrumManagementSupport(responder))
1520                .await,
1521            Ok(())
1522        );
1523        assert_eq!(receiver.await.unwrap().dfs.supported, true);
1524    }
1525
1526    #[test]
1527    fn display_rejection() {
1528        assert_eq!(format!("{}", Rejection::BadDsBits), "BadDsBits");
1529    }
1530}