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