Skip to main content

wlan_mlme/ap/
mod.rs

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