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