wlan_mlme/ap/
infra_bss.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
5use crate::ap::remote_client::{ClientRejection, RemoteClient};
6use crate::ap::{frame_writer, BeaconOffloadParams, BufferedFrame, Context, Rejection, TimedEvent};
7use crate::ddk_converter::softmac_key_configuration_from_mlme;
8use crate::device::{self, DeviceOps};
9use crate::error::Error;
10use crate::WlanTxPacketExt as _;
11use anyhow::format_err;
12use fdf::ArenaStaticBox;
13use ieee80211::{MacAddr, MacAddrBytes, Ssid};
14use log::error;
15use std::collections::{HashMap, VecDeque};
16use std::fmt::Display;
17use wlan_common::mac::{self, CapabilityInfo, EthernetIIHdr};
18use wlan_common::{ie, tim, TimeUnit};
19use zerocopy::SplitByteSlice;
20use {
21    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_mlme as fidl_mlme,
22    fidl_fuchsia_wlan_softmac as fidl_softmac, fuchsia_trace as trace,
23};
24
25pub struct InfraBss {
26    pub ssid: Ssid,
27    pub rsne: Option<Vec<u8>>,
28    pub beacon_interval: TimeUnit,
29    pub dtim_period: u8,
30    pub capabilities: CapabilityInfo,
31    pub rates: Vec<u8>,
32    pub channel: u8,
33    pub clients: HashMap<MacAddr, RemoteClient>,
34
35    group_buffered: VecDeque<BufferedFrame>,
36    dtim_count: u8,
37}
38
39fn get_client_mut(
40    clients: &mut HashMap<MacAddr, RemoteClient>,
41    addr: MacAddr,
42) -> Result<&mut RemoteClient, Error> {
43    clients
44        .get_mut(&addr)
45        .ok_or(Error::Status(format!("client {:02X?} not found", addr), zx::Status::NOT_FOUND))
46}
47
48/// Prepends the client's MAC address to an error::Error.
49///
50/// This will discard any more specific error information (e.g. if it was a FIDL error or a
51/// anyhow::Error error), but will still preserve the underlying zx::Status code.
52fn make_client_error(addr: MacAddr, e: Error) -> Error {
53    Error::Status(format!("client {}: {}", addr, e), e.into())
54}
55
56impl InfraBss {
57    pub async fn new<D: DeviceOps>(
58        ctx: &mut Context<D>,
59        ssid: Ssid,
60        beacon_interval: TimeUnit,
61        dtim_period: u8,
62        capabilities: CapabilityInfo,
63        rates: Vec<u8>,
64        channel: u8,
65        rsne: Option<Vec<u8>>,
66    ) -> Result<Self, Error> {
67        let bss = Self {
68            ssid,
69            rsne,
70            beacon_interval,
71            dtim_period,
72            rates,
73            capabilities,
74            channel,
75            clients: HashMap::new(),
76
77            group_buffered: VecDeque::new(),
78            dtim_count: 0,
79        };
80
81        ctx.device
82            .set_channel(fidl_common::WlanChannel {
83                primary: channel,
84
85                // TODO(https://fxbug.dev/42116942): Correctly support this.
86                cbw: fidl_common::ChannelBandwidth::Cbw20,
87                secondary80: 0,
88            })
89            .await
90            .map_err(|s| Error::Status(format!("failed to set channel"), s))?;
91
92        // TODO(https://fxbug.dev/42113580): Support DTIM.
93
94        let (in_buffer, beacon_offload_params) = bss.make_beacon_frame(ctx)?;
95        let mac_frame = in_buffer.to_vec();
96        let tim_ele_offset = u64::try_from(beacon_offload_params.tim_ele_offset).map_err(|_| {
97            Error::Internal(format_err!(
98                "failed to convert TIM offset for beacon frame packet template"
99            ))
100        })?;
101        ctx.device
102            .enable_beaconing(fidl_softmac::WlanSoftmacBaseEnableBeaconingRequest {
103                packet_template: Some(fidl_softmac::WlanTxPacket::template(mac_frame)),
104                tim_ele_offset: Some(tim_ele_offset),
105                beacon_interval: Some(beacon_interval.0),
106                ..Default::default()
107            })
108            .await
109            .map_err(|s| Error::Status(format!("failed to enable beaconing"), s))?;
110        ctx.device
111            .set_ethernet_up()
112            .await
113            .map_err(|s| Error::Status(format!("Failed to set ethernet status to UP"), s))?;
114
115        Ok(bss)
116    }
117
118    pub async fn stop<D: DeviceOps>(&self, ctx: &mut Context<D>) -> Result<(), Error> {
119        ctx.device
120            .set_ethernet_down()
121            .await
122            .map_err(|s| Error::Status(format!("Failed to set ethernet status to DOWN"), s))?;
123        ctx.device
124            .disable_beaconing()
125            .await
126            .map_err(|s| Error::Status(format!("failed to disable beaconing"), s))
127    }
128
129    fn make_tim(&self) -> tim::TrafficIndicationMap {
130        let mut tim = tim::TrafficIndicationMap::new();
131        for client in self.clients.values() {
132            let aid = match client.aid() {
133                Some(aid) => aid,
134                None => {
135                    continue;
136                }
137            };
138            tim.set_traffic_buffered(aid, client.has_buffered_frames());
139        }
140        tim
141    }
142
143    pub async fn handle_mlme_setkeys_req<D: DeviceOps>(
144        &mut self,
145        ctx: &mut Context<D>,
146        keylist: Vec<fidl_mlme::SetKeyDescriptor>,
147    ) -> Result<(), Error> {
148        fn key_type_name(key_type: fidl_mlme::KeyType) -> impl Display {
149            match key_type {
150                fidl_mlme::KeyType::Group => "GTK",
151                fidl_mlme::KeyType::Pairwise => "PTK",
152                fidl_mlme::KeyType::PeerKey => "peer key",
153                fidl_mlme::KeyType::Igtk => "IGTK",
154            }
155        }
156
157        if self.rsne.is_none() {
158            return Err(Error::Status(
159                format!("cannot set keys for an unprotected BSS"),
160                zx::Status::BAD_STATE,
161            ));
162        }
163
164        for key_descriptor in keylist.into_iter() {
165            let key_type = key_descriptor.key_type;
166            ctx.device
167                .install_key(&softmac_key_configuration_from_mlme(key_descriptor))
168                .await
169                .map_err(|status| {
170                    Error::Status(
171                        format!("failed to set {} on PHY", key_type_name(key_type)),
172                        status,
173                    )
174                })?;
175        }
176        Ok(())
177    }
178
179    pub async fn handle_mlme_auth_resp<D: DeviceOps>(
180        &mut self,
181        ctx: &mut Context<D>,
182        resp: fidl_mlme::AuthenticateResponse,
183    ) -> Result<(), Error> {
184        let client = get_client_mut(&mut self.clients, resp.peer_sta_address.into())?;
185        client
186            .handle_mlme_auth_resp(ctx, resp.result_code)
187            .await
188            .map_err(|e| make_client_error(client.addr, e))
189    }
190
191    pub async fn handle_mlme_deauth_req<D: DeviceOps>(
192        &mut self,
193        ctx: &mut Context<D>,
194        req: fidl_mlme::DeauthenticateRequest,
195    ) -> Result<(), Error> {
196        let client = get_client_mut(&mut self.clients, req.peer_sta_address.into())?;
197        client
198            .handle_mlme_deauth_req(ctx, req.reason_code)
199            .await
200            .map_err(|e| make_client_error(client.addr, e))?;
201        if client.deauthenticated() {
202            self.clients.remove(&req.peer_sta_address.into());
203        }
204        Ok(())
205    }
206
207    pub async fn handle_mlme_assoc_resp<D: DeviceOps>(
208        &mut self,
209        ctx: &mut Context<D>,
210        resp: fidl_mlme::AssociateResponse,
211    ) -> Result<(), Error> {
212        let client = get_client_mut(&mut self.clients, resp.peer_sta_address.into())?;
213
214        client
215            .handle_mlme_assoc_resp(
216                ctx,
217                self.rsne.is_some(),
218                self.channel,
219                CapabilityInfo(resp.capability_info),
220                resp.result_code,
221                resp.association_id,
222                &resp.rates,
223            )
224            .await
225            .map_err(|e| make_client_error(client.addr, e))
226    }
227
228    pub async fn handle_mlme_disassoc_req<D: DeviceOps>(
229        &mut self,
230        ctx: &mut Context<D>,
231        req: fidl_mlme::DisassociateRequest,
232    ) -> Result<(), Error> {
233        let client = get_client_mut(&mut self.clients, req.peer_sta_address.into())?;
234        client
235            .handle_mlme_disassoc_req(ctx, req.reason_code.into_primitive())
236            .await
237            .map_err(|e| make_client_error(client.addr, e))
238    }
239
240    pub fn handle_mlme_set_controlled_port_req(
241        &mut self,
242        req: fidl_mlme::SetControlledPortRequest,
243    ) -> Result<(), Error> {
244        let client = get_client_mut(&mut self.clients, req.peer_sta_address.into())?;
245        client
246            .handle_mlme_set_controlled_port_req(req.state)
247            .map_err(|e| make_client_error(client.addr, e))
248    }
249
250    pub fn handle_mlme_eapol_req<D: DeviceOps>(
251        &mut self,
252        ctx: &mut Context<D>,
253        req: fidl_mlme::EapolRequest,
254    ) -> Result<(), Error> {
255        let client = get_client_mut(&mut self.clients, req.dst_addr.into())?;
256        match client
257            .handle_mlme_eapol_req(ctx, req.src_addr.into(), &req.data)
258            .map_err(|e| make_client_error(client.addr, e))
259        {
260            Ok(()) => {
261                ctx.send_mlme_eapol_conf(fidl_mlme::EapolResultCode::Success, req.dst_addr.into())
262            }
263            Err(e) => {
264                if let Err(e) = ctx.send_mlme_eapol_conf(
265                    fidl_mlme::EapolResultCode::TransmissionFailure,
266                    req.dst_addr.into(),
267                ) {
268                    error!("Failed to send eapol transmission failure: {:?}", e);
269                }
270                Err(e)
271            }
272        }
273    }
274
275    fn make_beacon_frame<D>(
276        &self,
277        ctx: &Context<D>,
278    ) -> Result<(ArenaStaticBox<[u8]>, BeaconOffloadParams), Error> {
279        let tim = self.make_tim();
280        let (pvb_offset, pvb_bitmap) = tim.make_partial_virtual_bitmap();
281
282        ctx.make_beacon_frame(
283            self.beacon_interval,
284            self.capabilities,
285            &self.ssid,
286            &self.rates,
287            self.channel,
288            ie::TimHeader {
289                dtim_count: self.dtim_count,
290                dtim_period: self.dtim_period,
291                bmp_ctrl: ie::BitmapControl(0)
292                    .with_group_traffic(!self.group_buffered.is_empty())
293                    .with_offset(pvb_offset),
294            },
295            pvb_bitmap,
296            self.rsne.as_ref().map_or(&[], |rsne| &rsne),
297        )
298    }
299
300    fn handle_probe_req<D: DeviceOps>(
301        &mut self,
302        ctx: &mut Context<D>,
303        client_addr: MacAddr,
304    ) -> Result<(), Rejection> {
305        // According to IEEE Std 802.11-2016, 11.1.4.1, we should intersect our IEs with the probe
306        // request IEs. However, the client is able to do this anyway so we just send the same IEs
307        // as we would with a beacon frame.
308        let buffer = ctx
309            .make_probe_resp_frame(
310                client_addr,
311                self.beacon_interval,
312                self.capabilities,
313                &self.ssid,
314                &self.rates,
315                self.channel,
316                self.rsne.as_ref().map_or(&[], |rsne| &rsne),
317            )
318            .map_err(|e| Rejection::Client(client_addr, ClientRejection::WlanSendError(e)))?;
319        ctx.device.send_wlan_frame(buffer, fidl_softmac::WlanTxInfoFlags::empty(), None).map_err(
320            |s| {
321                Rejection::Client(
322                    client_addr,
323                    ClientRejection::WlanSendError(Error::Status(
324                        format!("failed to send probe resp"),
325                        s,
326                    )),
327                )
328            },
329        )
330    }
331
332    pub async fn handle_mgmt_frame<B: SplitByteSlice, D: DeviceOps>(
333        &mut self,
334        ctx: &mut Context<D>,
335        mgmt_frame: mac::MgmtFrame<B>,
336    ) -> Result<(), Rejection> {
337        let frame_ctrl = mgmt_frame.frame_ctrl();
338        if frame_ctrl.to_ds() || frame_ctrl.from_ds() {
339            // IEEE Std 802.11-2016, 9.2.4.1.4 and Table 9-4: The To DS bit is only set for QMF
340            // (QoS Management frame) management frames, and the From DS bit is reserved.
341            return Err(Rejection::BadDsBits);
342        }
343
344        let to_bss = mgmt_frame.mgmt_hdr.addr1.as_array() == ctx.bssid.as_array()
345            && mgmt_frame.mgmt_hdr.addr3.as_array() == ctx.bssid.as_array();
346        let client_addr = mgmt_frame.mgmt_hdr.addr2;
347
348        if mgmt_frame.mgmt_subtype() == mac::MgmtSubtype::PROBE_REQ {
349            if device::try_query_discovery_support(&mut ctx.device)
350                .await
351                .map_err(anyhow::Error::from)?
352                .probe_response_offload
353                .supported
354            {
355                // We expected the probe response to be handled by hardware.
356                return Err(Rejection::Error(format_err!(
357                    "driver indicates probe response offload but MLME received a probe response!"
358                )));
359            }
360
361            if to_bss
362                || (mgmt_frame.mgmt_hdr.addr1 == ieee80211::BROADCAST_ADDR
363                    && mgmt_frame.mgmt_hdr.addr3 == ieee80211::BROADCAST_ADDR)
364            {
365                // Allow either probe request sent directly to the AP, or ones that are broadcast.
366                for (id, ie_body) in mgmt_frame.into_ies().1 {
367                    match id {
368                        ie::Id::SSID => {
369                            if !ie_body.is_empty() && *ie_body != self.ssid[..] {
370                                // Frame is not for this BSS.
371                                return Err(Rejection::OtherBss);
372                            }
373                        }
374                        _ => {}
375                    }
376                }
377
378                // Technically, the probe request must contain an SSID IE (IEEE Std 802.11-2016,
379                // 11.1.4.1), but we just treat it here as the same as it being an empty SSID.
380                return self.handle_probe_req(ctx, client_addr);
381            } else {
382                // Frame is not for this BSS.
383                return Err(Rejection::OtherBss);
384            }
385        } else if !to_bss {
386            // Frame is not for this BSS.
387            return Err(Rejection::OtherBss);
388        }
389
390        // We might allocate a client into the Option if there is none present in the map. We do not
391        // allocate directly into the map as we do not know yet if the client will even be added
392        // (e.g. if the frame being handled is bogus, or the client did not even authenticate).
393        let mut new_client = None;
394        let client = match self.clients.get_mut(&client_addr) {
395            Some(client) => client,
396            None => new_client.get_or_insert(RemoteClient::new(client_addr)),
397        };
398
399        if let Err(e) = client
400            .handle_mgmt_frame(ctx, self.capabilities, Some(self.ssid.clone()), mgmt_frame)
401            .await
402        {
403            return Err(Rejection::Client(client_addr, e));
404        }
405
406        // IEEE Std 802.11-2016, 9.2.4.1.7: The value [of the Power Management subfield] indicates
407        // the mode of the STA after the successful completion of the frame exchange sequence.
408        match client.set_power_state(ctx, frame_ctrl.power_mgmt()) {
409            Err(ClientRejection::NotAssociated) => {
410                error!("client {:02X?} tried to doze but is not associated", client_addr);
411            }
412            Err(e) => {
413                return Err(Rejection::Client(client.addr, e));
414            }
415            Ok(()) => {}
416        }
417
418        if client.deauthenticated() {
419            if new_client.is_none() {
420                // The client needs to be removed from the map, as it was not freshly allocated from
421                // handling this frame.
422                self.clients.remove(&client_addr);
423            }
424        } else {
425            // The client was successfully authenticated! Remember it here.
426            if let Some(client) = new_client.take() {
427                self.clients.insert(client_addr, client);
428            }
429        }
430
431        Ok(())
432    }
433
434    /// Handles an incoming data frame.
435    ///
436    ///
437    pub fn handle_data_frame<B: SplitByteSlice, D: DeviceOps>(
438        &mut self,
439        ctx: &mut Context<D>,
440        data_frame: mac::DataFrame<B>,
441    ) -> Result<(), Rejection> {
442        if mac::data_receiver_addr(&data_frame.fixed_fields).as_array() != ctx.bssid.as_array() {
443            // Frame is not for this BSSID.
444            return Err(Rejection::OtherBss);
445        }
446
447        let frame_ctrl = data_frame.frame_ctrl();
448        if !*&frame_ctrl.to_ds() || *&frame_ctrl.from_ds() {
449            // IEEE Std 802.11-2016, 9.2.4.1.4 and Table 9-3: Frame was not sent to a distribution
450            // system (e.g. an AP), or was received from another distribution system.
451            return Err(Rejection::BadDsBits);
452        }
453
454        let src_addr =
455            mac::data_src_addr(&data_frame.fixed_fields, data_frame.addr4.as_deref().copied())
456                .ok_or(Rejection::NoSrcAddr)?;
457
458        // Handle the frame, pretending that the client is an unauthenticated client if we don't
459        // know about it.
460        let mut maybe_client = None;
461        let client = self
462            .clients
463            .get_mut(&src_addr)
464            .unwrap_or_else(|| maybe_client.get_or_insert(RemoteClient::new(src_addr)));
465
466        client.handle_data_frame(ctx, data_frame).map_err(|e| Rejection::Client(client.addr, e))?;
467
468        // IEEE Std 802.11-2016, 9.2.4.1.7: The value [of the Power Management subfield] indicates
469        // the mode of the STA after the successful completion of the frame exchange sequence.
470        match client.set_power_state(ctx, frame_ctrl.power_mgmt()) {
471            Err(ClientRejection::NotAssociated) => {
472                error!("client {:02X?} tried to doze but is not associated", client.addr);
473            }
474            Err(e) => {
475                return Err(Rejection::Client(client.addr, e));
476            }
477            Ok(()) => {}
478        }
479
480        Ok(())
481    }
482
483    pub fn handle_ctrl_frame<B: SplitByteSlice, D: DeviceOps>(
484        &mut self,
485        ctx: &mut Context<D>,
486        ctrl_frame: mac::CtrlFrame<B>,
487    ) -> Result<(), Rejection> {
488        match ctrl_frame.try_into_ctrl_body().ok_or(Rejection::FrameMalformed)? {
489            mac::CtrlBody::PsPoll { ps_poll } => {
490                let client = match self.clients.get_mut(&ps_poll.ta) {
491                    Some(client) => client,
492                    _ => {
493                        return Err(Rejection::Client(
494                            ps_poll.ta,
495                            ClientRejection::NotAuthenticated,
496                        ));
497                    }
498                };
499
500                // IEEE 802.11-2016 9.3.1.5 states the ID in the PS-Poll frame is the association ID
501                // with the 2 MSBs set to 1.
502                const PS_POLL_MASK: u16 = 0b11000000_00000000;
503                client
504                    .handle_ps_poll(ctx, ps_poll.masked_aid & !PS_POLL_MASK)
505                    .map_err(|e| Rejection::Client(client.addr, e))
506            }
507            _ => Err(Rejection::FrameMalformed),
508        }
509    }
510
511    pub fn handle_multicast_eth_frame<D: DeviceOps>(
512        &mut self,
513        ctx: &mut Context<D>,
514        hdr: EthernetIIHdr,
515        body: &[u8],
516        async_id: trace::Id,
517    ) -> Result<(), Rejection> {
518        let buffer = ctx
519            .make_data_frame(
520                hdr.da,
521                hdr.sa,
522                self.rsne.is_some(),
523                false, // TODO(https://fxbug.dev/42113580): Support QoS.
524                hdr.ether_type.to_native(),
525                body,
526            )
527            .map_err(|e| Rejection::Client(hdr.da, ClientRejection::WlanSendError(e)))?;
528        let tx_flags = fidl_softmac::WlanTxInfoFlags::empty();
529
530        if !self.clients.values().any(|client| client.dozing()) {
531            ctx.device.send_wlan_frame(buffer, tx_flags, Some(async_id)).map_err(move |s| {
532                Rejection::Client(
533                    hdr.da,
534                    ClientRejection::WlanSendError(Error::Status(
535                        format!("error sending multicast data frame"),
536                        s,
537                    )),
538                )
539            })?;
540        } else {
541            self.group_buffered.push_back(BufferedFrame { buffer, tx_flags, async_id });
542        }
543
544        Ok(())
545    }
546
547    pub fn handle_eth_frame<D: DeviceOps>(
548        &mut self,
549        ctx: &mut Context<D>,
550        hdr: EthernetIIHdr,
551        body: &[u8],
552        async_id: trace::Id,
553    ) -> Result<(), Rejection> {
554        if hdr.da.is_multicast() {
555            return self.handle_multicast_eth_frame(ctx, hdr, body, async_id);
556        }
557
558        // Handle the frame, pretending that the client is an unauthenticated client if we don't
559        // know about it.
560        let mut maybe_client = None;
561        let client = self
562            .clients
563            .get_mut(&hdr.da)
564            .unwrap_or_else(|| maybe_client.get_or_insert(RemoteClient::new(hdr.da)));
565        client
566            .handle_eth_frame(ctx, hdr.da, hdr.sa, hdr.ether_type.to_native(), body, async_id)
567            .map_err(|e| Rejection::Client(client.addr, e))
568    }
569
570    // TODO(https://fxbug.dev/335283785): Remove or explain unused code.
571    #[allow(dead_code)]
572    pub fn handle_bcn_tx_complete_indication<D: DeviceOps>(
573        &mut self,
574        ctx: &mut Context<D>,
575    ) -> Result<(), Error> {
576        if self.dtim_count > 0 {
577            self.dtim_count -= 1;
578            return Ok(());
579        }
580
581        self.dtim_count = self.dtim_period;
582
583        let mut buffered = self.group_buffered.drain(..).peekable();
584        while let Some(BufferedFrame { mut buffer, tx_flags, async_id }) = buffered.next() {
585            if buffered.peek().is_some() {
586                frame_writer::set_more_data(&mut buffer[..])?;
587            }
588            ctx.device
589                .send_wlan_frame(buffer, tx_flags, Some(async_id))
590                .map_err(|s| Error::Status(format!("error sending buffered frame"), s))?;
591        }
592
593        Ok(())
594    }
595
596    // Timed event functions
597
598    /// Handles timed events.
599    pub async fn handle_timed_event<D: DeviceOps>(
600        &mut self,
601        ctx: &mut Context<D>,
602        event: TimedEvent,
603    ) -> Result<(), Rejection> {
604        match event {
605            TimedEvent::ClientEvent(addr, event) => {
606                let client = self.clients.get_mut(&addr).ok_or(Rejection::NoSuchClient(addr))?;
607                client.handle_event(ctx, event).await.map_err(|e| Rejection::Client(client.addr, e))
608            }
609        }
610    }
611}
612
613#[cfg(test)]
614mod tests {
615    use super::*;
616    use crate::ap::remote_client::ClientEvent;
617    use crate::device::{FakeDevice, FakeDeviceConfig, FakeDeviceState};
618    use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
619    use fuchsia_sync::Mutex;
620    use ieee80211::Bssid;
621    use lazy_static::lazy_static;
622    use std::sync::Arc;
623    use test_case::test_case;
624    use wlan_common::assert_variant;
625    use wlan_common::big_endian::BigEndianU16;
626    use wlan_common::mac::IntoBytesExt as _;
627    use wlan_common::test_utils::fake_frames::fake_wpa2_rsne;
628    use wlan_common::timer::{self, create_timer};
629
630    lazy_static! {
631        static ref CLIENT_ADDR: MacAddr = [4u8; 6].into();
632        static ref BSSID: Bssid = [2u8; 6].into();
633        static ref CLIENT_ADDR2: MacAddr = [6u8; 6].into();
634        static ref REMOTE_ADDR: MacAddr = [123u8; 6].into();
635    }
636
637    fn make_context(
638        fake_device: FakeDevice,
639    ) -> (Context<FakeDevice>, timer::EventStream<TimedEvent>) {
640        let (timer, time_stream) = create_timer();
641        (Context::new(fake_device, timer, *BSSID), time_stream)
642    }
643
644    async fn make_infra_bss(ctx: &mut Context<FakeDevice>) -> InfraBss {
645        InfraBss::new(
646            ctx,
647            Ssid::try_from("coolnet").unwrap(),
648            TimeUnit::DEFAULT_BEACON_INTERVAL,
649            2,
650            CapabilityInfo(0),
651            vec![0b11111000],
652            1,
653            None,
654        )
655        .await
656        .expect("expected InfraBss::new ok")
657    }
658
659    async fn make_protected_infra_bss(ctx: &mut Context<FakeDevice>) -> InfraBss {
660        InfraBss::new(
661            ctx,
662            Ssid::try_from("coolnet").unwrap(),
663            TimeUnit::DEFAULT_BEACON_INTERVAL,
664            2,
665            CapabilityInfo(0),
666            vec![0b11111000],
667            1,
668            Some(fake_wpa2_rsne()),
669        )
670        .await
671        .expect("expected InfraBss::new ok")
672    }
673
674    #[fuchsia::test(allow_stalls = false)]
675    async fn new() {
676        let (fake_device, fake_device_state) = FakeDevice::new().await;
677        let (mut ctx, _) = make_context(fake_device);
678        InfraBss::new(
679            &mut ctx,
680            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
681            TimeUnit::DEFAULT_BEACON_INTERVAL,
682            2,
683            CapabilityInfo(0).with_ess(true),
684            vec![0b11111000],
685            1,
686            None,
687        )
688        .await
689        .expect("expected InfraBss::new ok");
690
691        assert_eq!(
692            fake_device_state.lock().wlan_channel,
693            fidl_common::WlanChannel {
694                primary: 1,
695                cbw: fidl_common::ChannelBandwidth::Cbw20,
696                secondary80: 0
697            }
698        );
699
700        let beacon_tmpl = vec![
701            // Mgmt header
702            0b10000000, 0, // Frame Control
703            0, 0, // Duration
704            255, 255, 255, 255, 255, 255, // addr1
705            2, 2, 2, 2, 2, 2, // addr2
706            2, 2, 2, 2, 2, 2, // addr3
707            0, 0, // Sequence Control
708            // Beacon header:
709            0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
710            100, 0, // Beacon interval
711            1, 0, // Capabilities
712            // IEs:
713            0, 5, 1, 2, 3, 4, 5, // SSID
714            1, 1, 0b11111000, // Basic rates
715            3, 1, 1, // DSSS parameter set
716            5, 4, 0, 2, 0, 0, // TIM
717        ];
718
719        assert_eq!(
720            fake_device_state.lock().beacon_config.as_ref().expect("expected beacon_config"),
721            &(beacon_tmpl, 49, TimeUnit::DEFAULT_BEACON_INTERVAL)
722        );
723    }
724
725    #[fuchsia::test(allow_stalls = false)]
726    async fn stop() {
727        let (fake_device, fake_device_state) = FakeDevice::new().await;
728        let (mut ctx, _) = make_context(fake_device);
729        let bss = make_infra_bss(&mut ctx).await;
730        bss.stop(&mut ctx).await.expect("expected InfraBss::stop ok");
731        assert!(fake_device_state.lock().beacon_config.is_none());
732    }
733
734    #[fuchsia::test(allow_stalls = false)]
735    async fn handle_mlme_auth_resp() {
736        let (fake_device, fake_device_state) = FakeDevice::new().await;
737        let (mut ctx, _) = make_context(fake_device);
738        let mut bss = make_infra_bss(&mut ctx).await;
739
740        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
741
742        bss.handle_mlme_auth_resp(
743            &mut ctx,
744            fidl_mlme::AuthenticateResponse {
745                peer_sta_address: CLIENT_ADDR.to_array(),
746                result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
747            },
748        )
749        .await
750        .expect("expected InfraBss::handle_mlme_auth_resp ok");
751        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
752        assert_eq!(
753            &fake_device_state.lock().wlan_queue[0].0[..],
754            &[
755                // Mgmt header
756                0b10110000, 0, // Frame Control
757                0, 0, // Duration
758                4, 4, 4, 4, 4, 4, // addr1
759                2, 2, 2, 2, 2, 2, // addr2
760                2, 2, 2, 2, 2, 2, // addr3
761                0x10, 0, // Sequence Control
762                // Auth header:
763                0, 0, // auth algorithm
764                2, 0, // auth txn seq num
765                76, 0, // status code
766            ][..]
767        );
768    }
769
770    #[fuchsia::test(allow_stalls = false)]
771    async fn handle_mlme_auth_resp_no_such_client() {
772        let (fake_device, _) = FakeDevice::new().await;
773        let (mut ctx, _) = make_context(fake_device);
774        let mut bss = make_infra_bss(&mut ctx).await;
775
776        assert_eq!(
777            zx::Status::from(
778                bss.handle_mlme_auth_resp(
779                    &mut ctx,
780                    fidl_mlme::AuthenticateResponse {
781                        peer_sta_address: CLIENT_ADDR.to_array(),
782                        result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
783                    },
784                )
785                .await
786                .expect_err("expected InfraBss::handle_mlme_auth_resp error")
787            ),
788            zx::Status::NOT_FOUND
789        );
790    }
791
792    #[fuchsia::test(allow_stalls = false)]
793    async fn handle_mlme_deauth_req() {
794        let (fake_device, fake_device_state) = FakeDevice::new().await;
795        let (mut ctx, _) = make_context(fake_device);
796        let mut bss = make_infra_bss(&mut ctx).await;
797
798        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
799
800        bss.handle_mlme_deauth_req(
801            &mut ctx,
802            fidl_mlme::DeauthenticateRequest {
803                peer_sta_address: CLIENT_ADDR.to_array(),
804                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
805            },
806        )
807        .await
808        .expect("expected InfraBss::handle_mlme_deauth_req ok");
809        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
810        assert_eq!(
811            &fake_device_state.lock().wlan_queue[0].0[..],
812            &[
813                // Mgmt header
814                0b11000000, 0, // Frame Control
815                0, 0, // Duration
816                4, 4, 4, 4, 4, 4, // addr1
817                2, 2, 2, 2, 2, 2, // addr2
818                2, 2, 2, 2, 2, 2, // addr3
819                0x10, 0, // Sequence Control
820                // Deauth header:
821                3, 0, // reason code
822            ][..]
823        );
824
825        assert!(!bss.clients.contains_key(&CLIENT_ADDR));
826    }
827
828    #[fuchsia::test(allow_stalls = false)]
829    async fn handle_mlme_assoc_resp() {
830        let (fake_device, fake_device_state) = FakeDevice::new().await;
831        let (mut ctx, _) = make_context(fake_device);
832        let mut bss = make_infra_bss(&mut ctx).await;
833
834        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
835        bss.handle_mlme_assoc_resp(
836            &mut ctx,
837            fidl_mlme::AssociateResponse {
838                peer_sta_address: CLIENT_ADDR.to_array(),
839                result_code: fidl_mlme::AssociateResultCode::Success,
840                association_id: 1,
841                capability_info: 0,
842                rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
843            },
844        )
845        .await
846        .expect("expected InfraBss::handle_mlme_assoc_resp ok");
847        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
848        assert_eq!(
849            &fake_device_state.lock().wlan_queue[0].0[..],
850            &[
851                // Mgmt header
852                0b00010000, 0, // Frame Control
853                0, 0, // Duration
854                4, 4, 4, 4, 4, 4, // addr1
855                2, 2, 2, 2, 2, 2, // addr2
856                2, 2, 2, 2, 2, 2, // addr3
857                0x10, 0, // Sequence Control
858                // Association response header:
859                0, 0, // Capabilities
860                0, 0, // status code
861                1, 0, // AID
862                // IEs
863                1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
864                50, 2, 9, 10, // Extended rates
865                90, 3, 90, 0, 0, // BSS max idle period
866            ][..]
867        );
868        assert!(fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
869    }
870
871    #[fuchsia::test(allow_stalls = false)]
872    async fn handle_mlme_assoc_resp_with_caps() {
873        let (fake_device, fake_device_state) = FakeDevice::new().await;
874        let (mut ctx, _) = make_context(fake_device);
875        let mut bss = InfraBss::new(
876            &mut ctx,
877            Ssid::try_from("coolnet").unwrap(),
878            TimeUnit::DEFAULT_BEACON_INTERVAL,
879            2,
880            CapabilityInfo(0).with_short_preamble(true).with_ess(true),
881            vec![0b11111000],
882            1,
883            None,
884        )
885        .await
886        .expect("expected InfraBss::new ok");
887
888        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
889
890        bss.handle_mlme_assoc_resp(
891            &mut ctx,
892            fidl_mlme::AssociateResponse {
893                peer_sta_address: CLIENT_ADDR.to_array(),
894                result_code: fidl_mlme::AssociateResultCode::Success,
895                association_id: 1,
896                capability_info: CapabilityInfo(0).with_short_preamble(true).raw(),
897                rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
898            },
899        )
900        .await
901        .expect("expected InfraBss::handle_mlme_assoc_resp ok");
902        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
903        assert_eq!(
904            &fake_device_state.lock().wlan_queue[0].0[..],
905            &[
906                // Mgmt header
907                0b00010000, 0, // Frame Control
908                0, 0, // Duration
909                4, 4, 4, 4, 4, 4, // addr1
910                2, 2, 2, 2, 2, 2, // addr2
911                2, 2, 2, 2, 2, 2, // addr3
912                0x10, 0, // Sequence Control
913                // Association response header:
914                0b00100000, 0b00000000, // Capabilities
915                0, 0, // status code
916                1, 0, // AID
917                // IEs
918                1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
919                50, 2, 9, 10, // Extended rates
920                90, 3, 90, 0, 0, // BSS max idle period
921            ][..]
922        );
923        assert!(fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
924    }
925
926    #[fuchsia::test(allow_stalls = false)]
927    async fn handle_mlme_disassoc_req() {
928        let (fake_device, fake_device_state) = FakeDevice::new().await;
929        let (mut ctx, _) = make_context(fake_device);
930        let mut bss = make_infra_bss(&mut ctx).await;
931
932        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
933
934        bss.handle_mlme_disassoc_req(
935            &mut ctx,
936            fidl_mlme::DisassociateRequest {
937                peer_sta_address: CLIENT_ADDR.to_array(),
938                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
939            },
940        )
941        .await
942        .expect("expected InfraBss::handle_mlme_disassoc_req ok");
943        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
944        assert_eq!(
945            &fake_device_state.lock().wlan_queue[0].0[..],
946            &[
947                // Mgmt header
948                0b10100000, 0, // Frame Control
949                0, 0, // Duration
950                4, 4, 4, 4, 4, 4, // addr1
951                2, 2, 2, 2, 2, 2, // addr2
952                2, 2, 2, 2, 2, 2, // addr3
953                0x10, 0, // Sequence Control
954                // Disassoc header:
955                8, 0, // reason code
956            ][..]
957        );
958    }
959
960    #[fuchsia::test(allow_stalls = false)]
961    async fn handle_mlme_set_controlled_port_req() {
962        let (fake_device, _) = FakeDevice::new().await;
963        let (mut ctx, _) = make_context(fake_device);
964        let mut bss = make_protected_infra_bss(&mut ctx).await;
965
966        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
967
968        bss.handle_mlme_assoc_resp(
969            &mut ctx,
970            fidl_mlme::AssociateResponse {
971                peer_sta_address: CLIENT_ADDR.to_array(),
972                result_code: fidl_mlme::AssociateResultCode::Success,
973                association_id: 1,
974                capability_info: 0,
975                rates: vec![1, 2, 3],
976            },
977        )
978        .await
979        .expect("expected InfraBss::handle_mlme_assoc_resp ok");
980
981        bss.handle_mlme_set_controlled_port_req(fidl_mlme::SetControlledPortRequest {
982            peer_sta_address: CLIENT_ADDR.to_array(),
983            state: fidl_mlme::ControlledPortState::Open,
984        })
985        .expect("expected InfraBss::handle_mlme_set_controlled_port_req ok");
986    }
987
988    #[fuchsia::test(allow_stalls = false)]
989    async fn handle_mlme_eapol_req() {
990        let (fake_device, fake_device_state) = FakeDevice::new().await;
991        let (mut ctx, _) = make_context(fake_device);
992        let mut bss = make_infra_bss(&mut ctx).await;
993
994        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
995
996        bss.handle_mlme_eapol_req(
997            &mut ctx,
998            fidl_mlme::EapolRequest {
999                dst_addr: CLIENT_ADDR.to_array(),
1000                src_addr: BSSID.to_array(),
1001                data: vec![1, 2, 3],
1002            },
1003        )
1004        .expect("expected InfraBss::handle_mlme_eapol_req ok");
1005        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1006        assert_eq!(
1007            &fake_device_state.lock().wlan_queue[0].0[..],
1008            &[
1009                // Header
1010                0b00001000, 0b00000010, // Frame Control
1011                0, 0, // Duration
1012                4, 4, 4, 4, 4, 4, // addr1
1013                2, 2, 2, 2, 2, 2, // addr2
1014                2, 2, 2, 2, 2, 2, // addr3
1015                0x10, 0, // Sequence Control
1016                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1017                0, 0, 0, // OUI
1018                0x88, 0x8E, // EAPOL protocol ID
1019                // Data
1020                1, 2, 3,
1021            ][..]
1022        );
1023
1024        let confirm = fake_device_state
1025            .lock()
1026            .next_mlme_msg::<fidl_mlme::EapolConfirm>()
1027            .expect("Did not receive valid Eapol Confirm msg");
1028        assert_eq!(confirm.result_code, fidl_mlme::EapolResultCode::Success);
1029        assert_eq!(&confirm.dst_addr, CLIENT_ADDR.as_array());
1030    }
1031
1032    #[fuchsia::test(allow_stalls = false)]
1033    async fn handle_mgmt_frame_auth() {
1034        let (fake_device, fake_device_state) = FakeDevice::new().await;
1035        let (mut ctx, _) = make_context(fake_device);
1036        let mut bss = make_infra_bss(&mut ctx).await;
1037
1038        bss.handle_mgmt_frame(
1039            &mut ctx,
1040            mac::MgmtFrame {
1041                mgmt_hdr: mac::MgmtHdr {
1042                    frame_ctrl: mac::FrameControl(0)
1043                        .with_frame_type(mac::FrameType::MGMT)
1044                        .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
1045                    duration: 0,
1046                    addr1: (*BSSID).into(),
1047                    addr2: *CLIENT_ADDR,
1048                    addr3: (*BSSID).into(),
1049                    seq_ctrl: mac::SequenceControl(10),
1050                }
1051                .as_bytes_ref(),
1052                ht_ctrl: None,
1053                body: &[
1054                    // Auth body
1055                    0, 0, // Auth Algorithm Number
1056                    1, 0, // Auth Txn Seq Number
1057                    0, 0, // Status code
1058                ][..],
1059            },
1060        )
1061        .await
1062        .expect("expected OK");
1063
1064        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), true);
1065
1066        let msg = fake_device_state
1067            .lock()
1068            .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
1069            .expect("expected MLME message");
1070        assert_eq!(
1071            msg,
1072            fidl_mlme::AuthenticateIndication {
1073                peer_sta_address: CLIENT_ADDR.to_array(),
1074                auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1075            },
1076        );
1077    }
1078
1079    #[fuchsia::test(allow_stalls = false)]
1080    async fn handle_mgmt_frame_assoc_req() {
1081        let (fake_device, fake_device_state) = FakeDevice::new().await;
1082        let (mut ctx, _) = make_context(fake_device);
1083        let mut bss = make_infra_bss(&mut ctx).await;
1084
1085        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1086        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1087        client
1088            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1089            .await
1090            .expect("expected OK");
1091
1092        bss.handle_mgmt_frame(
1093            &mut ctx,
1094            mac::MgmtFrame {
1095                mgmt_hdr: mac::MgmtHdr {
1096                    frame_ctrl: mac::FrameControl(0)
1097                        .with_frame_type(mac::FrameType::MGMT)
1098                        .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_REQ),
1099                    duration: 0,
1100                    addr1: (*BSSID).into(),
1101                    addr2: *CLIENT_ADDR,
1102                    addr3: (*BSSID).into(),
1103                    seq_ctrl: mac::SequenceControl(10),
1104                }
1105                .as_bytes_ref(),
1106                ht_ctrl: None,
1107                body: &[
1108                    // Assoc req body
1109                    0, 0, // Capability info
1110                    10, 0, // Listen interval
1111                    // IEs
1112                    1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
1113                    50, 2, 9, 10, // Extended rates
1114                    48, 2, 77, 88, // RSNE
1115                ][..],
1116            },
1117        )
1118        .await
1119        .expect("expected OK");
1120
1121        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), true);
1122
1123        let msg = fake_device_state
1124            .lock()
1125            .next_mlme_msg::<fidl_mlme::AssociateIndication>()
1126            .expect("expected MLME message");
1127        assert_eq!(
1128            msg,
1129            fidl_mlme::AssociateIndication {
1130                peer_sta_address: CLIENT_ADDR.to_array(),
1131                listen_interval: 10,
1132                ssid: Some(Ssid::try_from("coolnet").unwrap().into()),
1133                capability_info: 0,
1134                rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
1135                rsne: Some(vec![48, 2, 77, 88]),
1136            },
1137        );
1138    }
1139
1140    #[fuchsia::test(allow_stalls = false)]
1141    async fn handle_mgmt_frame_bad_ds_bits_to_ds() {
1142        let (fake_device, _) = FakeDevice::new().await;
1143        let (mut ctx, _) = make_context(fake_device);
1144        let mut bss = make_infra_bss(&mut ctx).await;
1145
1146        assert_variant!(
1147            bss.handle_mgmt_frame(
1148                &mut ctx,
1149                mac::MgmtFrame {
1150                    mgmt_hdr: mac::MgmtHdr {
1151                        frame_ctrl: mac::FrameControl(0)
1152                            .with_frame_type(mac::FrameType::MGMT)
1153                            .with_mgmt_subtype(mac::MgmtSubtype::AUTH)
1154                            .with_to_ds(true),
1155                        duration: 0,
1156                        addr1: (*BSSID).into(),
1157                        addr2: *CLIENT_ADDR,
1158                        addr3: (*BSSID).into(),
1159                        seq_ctrl: mac::SequenceControl(10),
1160                    }
1161                    .as_bytes_ref(),
1162                    ht_ctrl: None,
1163                    body: &[
1164                        // Auth body
1165                        0, 0, // Auth Algorithm Number
1166                        1, 0, // Auth Txn Seq Number
1167                        0, 0, // Status code
1168                    ][..],
1169                },
1170            )
1171            .await
1172            .expect_err("expected error"),
1173            Rejection::BadDsBits
1174        );
1175
1176        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1177    }
1178
1179    #[fuchsia::test(allow_stalls = false)]
1180    async fn handle_mgmt_frame_bad_ds_bits_from_ds() {
1181        let (fake_device, _) = FakeDevice::new().await;
1182        let (mut ctx, _) = make_context(fake_device);
1183        let mut bss = make_infra_bss(&mut ctx).await;
1184
1185        assert_variant!(
1186            bss.handle_mgmt_frame(
1187                &mut ctx,
1188                mac::MgmtFrame {
1189                    mgmt_hdr: mac::MgmtHdr {
1190                        frame_ctrl: mac::FrameControl(0)
1191                            .with_frame_type(mac::FrameType::MGMT)
1192                            .with_mgmt_subtype(mac::MgmtSubtype::AUTH)
1193                            .with_from_ds(true),
1194                        duration: 0,
1195                        addr1: (*BSSID).into(),
1196                        addr2: *CLIENT_ADDR,
1197                        addr3: (*BSSID).into(),
1198                        seq_ctrl: mac::SequenceControl(10),
1199                    }
1200                    .as_bytes_ref(),
1201                    ht_ctrl: None,
1202                    body: &[
1203                        // Auth body
1204                        0, 0, // Auth Algorithm Number
1205                        1, 0, // Auth Txn Seq Number
1206                        0, 0, // Status code
1207                    ][..],
1208                },
1209            )
1210            .await
1211            .expect_err("expected error"),
1212            Rejection::BadDsBits
1213        );
1214
1215        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1216    }
1217
1218    #[fuchsia::test(allow_stalls = false)]
1219    async fn handle_mgmt_frame_no_such_client() {
1220        let (fake_device, _) = FakeDevice::new().await;
1221        let (mut ctx, _) = make_context(fake_device);
1222        let mut bss = make_infra_bss(&mut ctx).await;
1223
1224        assert_variant!(
1225            bss.handle_mgmt_frame(
1226                &mut ctx,
1227                mac::MgmtFrame {
1228                    mgmt_hdr: mac::MgmtHdr {
1229                        frame_ctrl: mac::FrameControl(0)
1230                            .with_frame_type(mac::FrameType::MGMT)
1231                            .with_mgmt_subtype(mac::MgmtSubtype::DISASSOC),
1232                        duration: 0,
1233                        addr1: (*BSSID).into(),
1234                        addr2: *CLIENT_ADDR,
1235                        addr3: (*BSSID).into(),
1236                        seq_ctrl: mac::SequenceControl(10),
1237                    }
1238                    .as_bytes_ref(),
1239                    ht_ctrl: None,
1240                    body: &[
1241                        // Disassoc header:
1242                        8, 0, // reason code
1243                    ][..],
1244                },
1245            )
1246            .await
1247            .expect_err("expected error"),
1248            Rejection::Client(_, ClientRejection::NotPermitted)
1249        );
1250
1251        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1252    }
1253
1254    #[fuchsia::test(allow_stalls = false)]
1255    async fn handle_mgmt_frame_bogus() {
1256        let (fake_device, _) = FakeDevice::new().await;
1257        let (mut ctx, _) = make_context(fake_device);
1258        let mut bss = make_infra_bss(&mut ctx).await;
1259
1260        assert_variant!(
1261            bss.handle_mgmt_frame(
1262                &mut ctx,
1263                mac::MgmtFrame {
1264                    mgmt_hdr: mac::MgmtHdr {
1265                        frame_ctrl: mac::FrameControl(0)
1266                            .with_frame_type(mac::FrameType::MGMT)
1267                            .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
1268                        duration: 0,
1269                        addr1: (*BSSID).into(),
1270                        addr2: *CLIENT_ADDR,
1271                        addr3: (*BSSID).into(),
1272                        seq_ctrl: mac::SequenceControl(10),
1273                    }
1274                    .as_bytes_ref(),
1275                    ht_ctrl: None,
1276                    // Auth frame should have a header; doesn't.
1277                    body: &[][..],
1278                },
1279            )
1280            .await
1281            .expect_err("expected error"),
1282            Rejection::Client(_, ClientRejection::ParseFailed)
1283        );
1284
1285        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1286    }
1287
1288    #[fuchsia::test(allow_stalls = false)]
1289    async fn handle_data_frame() {
1290        let (fake_device, fake_device_state) = FakeDevice::new().await;
1291        let (mut ctx, _) = make_context(fake_device);
1292        let mut bss = make_infra_bss(&mut ctx).await;
1293
1294        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1295        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1296
1297        // Move the client to associated so it can handle data frames.
1298        client
1299            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1300            .await
1301            .expect("expected OK");
1302        client
1303            .handle_mlme_assoc_resp(
1304                &mut ctx,
1305                false,
1306                1,
1307                mac::CapabilityInfo(0),
1308                fidl_mlme::AssociateResultCode::Success,
1309                1,
1310                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1311            )
1312            .await
1313            .expect("expected OK");
1314
1315        bss.handle_data_frame(
1316            &mut ctx,
1317            mac::DataFrame {
1318                fixed_fields: mac::FixedDataHdrFields {
1319                    frame_ctrl: mac::FrameControl(0)
1320                        .with_frame_type(mac::FrameType::DATA)
1321                        .with_to_ds(true),
1322                    duration: 0,
1323                    addr1: (*BSSID).into(),
1324                    addr2: *CLIENT_ADDR,
1325                    addr3: *CLIENT_ADDR2,
1326                    seq_ctrl: mac::SequenceControl(10),
1327                }
1328                .as_bytes_ref(),
1329                addr4: None,
1330                qos_ctrl: None,
1331                ht_ctrl: None,
1332                body: &[
1333                    7, 7, 7, // DSAP, SSAP & control
1334                    8, 8, 8, // OUI
1335                    0x12, 0x34, // eth type
1336                    // Trailing bytes
1337                    1, 2, 3, 4, 5,
1338                ][..],
1339            },
1340        )
1341        .expect("expected OK");
1342
1343        assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
1344        assert_eq!(
1345            &fake_device_state.lock().eth_queue[0][..],
1346            &[
1347                6, 6, 6, 6, 6, 6, // dest
1348                4, 4, 4, 4, 4, 4, // src
1349                0x12, 0x34, // ether_type
1350                // Data
1351                1, 2, 3, 4, 5,
1352            ][..]
1353        );
1354    }
1355
1356    #[fuchsia::test(allow_stalls = false)]
1357    async fn handle_data_frame_bad_ds_bits() {
1358        let (fake_device, fake_device_state) = FakeDevice::new().await;
1359        let (mut ctx, _) = make_context(fake_device);
1360        let mut bss = make_infra_bss(&mut ctx).await;
1361
1362        assert_variant!(
1363            bss.handle_data_frame(
1364                &mut ctx,
1365                mac::DataFrame {
1366                    fixed_fields: mac::FixedDataHdrFields {
1367                        frame_ctrl: mac::FrameControl(0)
1368                            .with_frame_type(mac::FrameType::DATA)
1369                            .with_to_ds(false),
1370                        duration: 0,
1371                        addr1: (*BSSID).into(),
1372                        addr2: *CLIENT_ADDR,
1373                        addr3: *CLIENT_ADDR2,
1374                        seq_ctrl: mac::SequenceControl(10),
1375                    }
1376                    .as_bytes_ref(),
1377                    addr4: None,
1378                    qos_ctrl: None,
1379                    ht_ctrl: None,
1380                    body: &[
1381                        7, 7, 7, // DSAP, SSAP & control
1382                        8, 8, 8, // OUI
1383                        0x12, 0x34, // eth type
1384                        // Trailing bytes
1385                        1, 2, 3, 4, 5,
1386                    ][..],
1387                },
1388            )
1389            .expect_err("expected error"),
1390            Rejection::BadDsBits
1391        );
1392
1393        assert_eq!(fake_device_state.lock().eth_queue.len(), 0);
1394    }
1395
1396    #[fuchsia::test(allow_stalls = false)]
1397    async fn handle_client_event() {
1398        let (fake_device, fake_device_state) = FakeDevice::new().await;
1399        let (mut ctx, mut time_stream) = make_context(fake_device);
1400        let mut bss = make_infra_bss(&mut ctx).await;
1401
1402        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1403        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1404
1405        // Move the client to associated so it can handle data frames.
1406        client
1407            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1408            .await
1409            .expect("expected OK");
1410        client
1411            .handle_mlme_assoc_resp(
1412                &mut ctx,
1413                false,
1414                1,
1415                mac::CapabilityInfo(0),
1416                fidl_mlme::AssociateResultCode::Success,
1417                1,
1418                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1419            )
1420            .await
1421            .expect("expected OK");
1422
1423        fake_device_state.lock().wlan_queue.clear();
1424
1425        let _ = time_stream.try_next().unwrap().expect("Should have scheduled a timeout");
1426        bss.handle_timed_event(
1427            &mut ctx,
1428            TimedEvent::ClientEvent(*CLIENT_ADDR, ClientEvent::BssIdleTimeout),
1429        )
1430        .await
1431        .expect("expected OK");
1432
1433        // Check that we received a disassociation frame.
1434        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1435        #[rustfmt::skip]
1436        assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1437            // Mgmt header
1438            0b10100000, 0, // 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            0x30, 0, // Sequence Control
1444            // Disassoc header:
1445            4, 0, // reason code
1446        ][..]);
1447
1448        let msg = fake_device_state
1449            .lock()
1450            .next_mlme_msg::<fidl_mlme::DisassociateIndication>()
1451            .expect("expected MLME message");
1452        assert_eq!(
1453            msg,
1454            fidl_mlme::DisassociateIndication {
1455                peer_sta_address: CLIENT_ADDR.to_array(),
1456                reason_code: fidl_ieee80211::ReasonCode::ReasonInactivity,
1457                locally_initiated: true,
1458            },
1459        );
1460    }
1461
1462    #[fuchsia::test(allow_stalls = false)]
1463    async fn handle_data_frame_no_such_client() {
1464        let (fake_device, fake_device_state) = FakeDevice::new().await;
1465        let (mut ctx, _) = make_context(fake_device);
1466        let mut bss = make_infra_bss(&mut ctx).await;
1467
1468        assert_variant!(
1469            bss.handle_data_frame(
1470                &mut ctx,
1471                mac::DataFrame {
1472                    fixed_fields: mac::FixedDataHdrFields {
1473                        frame_ctrl: mac::FrameControl(0)
1474                            .with_frame_type(mac::FrameType::DATA)
1475                            .with_to_ds(true),
1476                        duration: 0,
1477                        addr1: (*BSSID).into(),
1478                        addr2: *CLIENT_ADDR,
1479                        addr3: *CLIENT_ADDR2,
1480                        seq_ctrl: mac::SequenceControl(10),
1481                    }
1482                    .as_bytes_ref(),
1483                    addr4: None,
1484                    qos_ctrl: None,
1485                    ht_ctrl: None,
1486                    body: &[
1487                        7, 7, 7, // DSAP, SSAP & control
1488                        8, 8, 8, // OUI
1489                        0x12, 0x34, // eth type
1490                        // Trailing bytes
1491                        1, 2, 3, 4, 5,
1492                    ][..],
1493                },
1494            )
1495            .expect_err("expected error"),
1496            Rejection::Client(_, ClientRejection::NotPermitted)
1497        );
1498
1499        assert_eq!(fake_device_state.lock().eth_queue.len(), 0);
1500
1501        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1502        assert_eq!(
1503            fake_device_state.lock().wlan_queue[0].0,
1504            &[
1505                // Mgmt header
1506                0b11000000, 0b00000000, // Frame Control
1507                0, 0, // Duration
1508                4, 4, 4, 4, 4, 4, // addr1
1509                2, 2, 2, 2, 2, 2, // addr2
1510                2, 2, 2, 2, 2, 2, // addr3
1511                0x10, 0, // Sequence Control
1512                // Disassoc header:
1513                7, 0, // reason code
1514            ][..]
1515        );
1516    }
1517
1518    #[fuchsia::test(allow_stalls = false)]
1519    async fn handle_data_frame_client_not_associated() {
1520        let (fake_device, fake_device_state) = FakeDevice::new().await;
1521        let (mut ctx, _) = make_context(fake_device);
1522        let mut bss = make_infra_bss(&mut ctx).await;
1523
1524        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1525        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1526
1527        // Move the client to authenticated, but not associated: data frames are still not
1528        // permitted.
1529        client
1530            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1531            .await
1532            .expect("expected OK");
1533
1534        fake_device_state.lock().wlan_queue.clear();
1535
1536        assert_variant!(
1537            bss.handle_data_frame(
1538                &mut ctx,
1539                mac::DataFrame {
1540                    fixed_fields: mac::FixedDataHdrFields {
1541                        frame_ctrl: mac::FrameControl(0)
1542                            .with_frame_type(mac::FrameType::DATA)
1543                            .with_to_ds(true),
1544                        duration: 0,
1545                        addr1: (*BSSID).into(),
1546                        addr2: *CLIENT_ADDR,
1547                        addr3: *CLIENT_ADDR2,
1548                        seq_ctrl: mac::SequenceControl(10),
1549                    }
1550                    .as_bytes_ref(),
1551                    addr4: None,
1552                    qos_ctrl: None,
1553                    ht_ctrl: None,
1554                    body: &[
1555                        7, 7, 7, // DSAP, SSAP & control
1556                        8, 8, 8, // OUI
1557                        0x12, 0x34, // eth type
1558                        // Trailing bytes
1559                        1, 2, 3, 4, 5,
1560                    ][..],
1561                },
1562            )
1563            .expect_err("expected error"),
1564            Rejection::Client(_, ClientRejection::NotPermitted)
1565        );
1566
1567        assert_eq!(fake_device_state.lock().eth_queue.len(), 0);
1568
1569        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1570        assert_eq!(
1571            fake_device_state.lock().wlan_queue[0].0,
1572            &[
1573                // Mgmt header
1574                0b10100000, 0b00000000, // Frame Control
1575                0, 0, // Duration
1576                4, 4, 4, 4, 4, 4, // addr1
1577                2, 2, 2, 2, 2, 2, // addr2
1578                2, 2, 2, 2, 2, 2, // addr3
1579                0x20, 0, // Sequence Control
1580                // Disassoc header:
1581                7, 0, // reason code
1582            ][..]
1583        );
1584    }
1585
1586    #[fuchsia::test(allow_stalls = false)]
1587    async fn handle_eth_frame_no_rsn() {
1588        let (fake_device, fake_device_state) = FakeDevice::new().await;
1589        let (mut ctx, _) = make_context(fake_device);
1590        let mut bss = make_infra_bss(&mut ctx).await;
1591        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1592
1593        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1594        client
1595            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1596            .await
1597            .expect("expected OK");
1598        client
1599            .handle_mlme_assoc_resp(
1600                &mut ctx,
1601                false,
1602                1,
1603                mac::CapabilityInfo(0),
1604                fidl_mlme::AssociateResultCode::Success,
1605                1,
1606                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1607            )
1608            .await
1609            .expect("expected OK");
1610        fake_device_state.lock().wlan_queue.clear();
1611
1612        bss.handle_eth_frame(
1613            &mut ctx,
1614            EthernetIIHdr {
1615                da: *CLIENT_ADDR,
1616                sa: *CLIENT_ADDR2,
1617                ether_type: BigEndianU16::from_native(0x1234),
1618            },
1619            &[1, 2, 3, 4, 5][..],
1620            0.into(),
1621        )
1622        .expect("expected OK");
1623
1624        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1625        assert_eq!(
1626            &fake_device_state.lock().wlan_queue[0].0[..],
1627            &[
1628                // Mgmt header
1629                0b00001000, 0b00000010, // Frame Control
1630                0, 0, // Duration
1631                4, 4, 4, 4, 4, 4, // addr1
1632                2, 2, 2, 2, 2, 2, // addr2
1633                6, 6, 6, 6, 6, 6, // addr3
1634                0x30, 0, // Sequence Control
1635                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1636                0, 0, 0, // OUI
1637                0x12, 0x34, // Protocol ID
1638                // Data
1639                1, 2, 3, 4, 5,
1640            ][..]
1641        );
1642    }
1643
1644    #[fuchsia::test(allow_stalls = false)]
1645    async fn handle_eth_frame_no_client() {
1646        let (fake_device, fake_device_state) = FakeDevice::new().await;
1647        let (mut ctx, _) = make_context(fake_device);
1648        let mut bss = make_infra_bss(&mut ctx).await;
1649
1650        assert_variant!(
1651            bss.handle_eth_frame(
1652                &mut ctx,
1653                EthernetIIHdr {
1654                    da: *CLIENT_ADDR,
1655                    sa: *CLIENT_ADDR2,
1656                    ether_type: BigEndianU16::from_native(0x1234)
1657                },
1658                &[1, 2, 3, 4, 5][..],
1659                0.into(),
1660            )
1661            .expect_err("expected error"),
1662            Rejection::Client(_, ClientRejection::NotAssociated)
1663        );
1664
1665        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
1666    }
1667
1668    #[fuchsia::test(allow_stalls = false)]
1669    async fn handle_eth_frame_is_rsn_eapol_controlled_port_closed() {
1670        let (fake_device, fake_device_state) = FakeDevice::new().await;
1671        let (mut ctx, _) = make_context(fake_device);
1672        let mut bss = make_protected_infra_bss(&mut ctx).await;
1673        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1674
1675        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1676        client
1677            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1678            .await
1679            .expect("expected OK");
1680        client
1681            .handle_mlme_assoc_resp(
1682                &mut ctx,
1683                true,
1684                1,
1685                mac::CapabilityInfo(0),
1686                fidl_mlme::AssociateResultCode::Success,
1687                1,
1688                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1689            )
1690            .await
1691            .expect("expected OK");
1692        fake_device_state.lock().wlan_queue.clear();
1693
1694        assert_variant!(
1695            bss.handle_eth_frame(
1696                &mut ctx,
1697                EthernetIIHdr {
1698                    da: *CLIENT_ADDR,
1699                    sa: *CLIENT_ADDR2,
1700                    ether_type: BigEndianU16::from_native(0x1234)
1701                },
1702                &[1, 2, 3, 4, 5][..],
1703                0.into(),
1704            )
1705            .expect_err("expected error"),
1706            Rejection::Client(_, ClientRejection::ControlledPortClosed)
1707        );
1708    }
1709
1710    #[fuchsia::test(allow_stalls = false)]
1711    async fn handle_eth_frame_is_rsn_eapol_controlled_port_open() {
1712        let (fake_device, fake_device_state) = FakeDevice::new().await;
1713        let (mut ctx, _) = make_context(fake_device);
1714        let mut bss = make_protected_infra_bss(&mut ctx).await;
1715        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1716
1717        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1718        client
1719            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1720            .await
1721            .expect("expected OK");
1722        client
1723            .handle_mlme_assoc_resp(
1724                &mut ctx,
1725                true,
1726                1,
1727                mac::CapabilityInfo(0),
1728                fidl_mlme::AssociateResultCode::Success,
1729                1,
1730                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1731            )
1732            .await
1733            .expect("expected OK");
1734        fake_device_state.lock().wlan_queue.clear();
1735
1736        client
1737            .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Open)
1738            .expect("expected OK");
1739
1740        bss.handle_eth_frame(
1741            &mut ctx,
1742            EthernetIIHdr {
1743                da: *CLIENT_ADDR,
1744                sa: *CLIENT_ADDR2,
1745                ether_type: BigEndianU16::from_native(0x1234),
1746            },
1747            &[1, 2, 3, 4, 5][..],
1748            0.into(),
1749        )
1750        .expect("expected OK");
1751
1752        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1753        assert_eq!(
1754            &fake_device_state.lock().wlan_queue[0].0[..],
1755            &[
1756                // Mgmt header
1757                0b00001000, 0b01000010, // Frame Control
1758                0, 0, // Duration
1759                4, 4, 4, 4, 4, 4, // addr1
1760                2, 2, 2, 2, 2, 2, // addr2
1761                6, 6, 6, 6, 6, 6, // addr3
1762                0x30, 0, // Sequence Control
1763                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1764                0, 0, 0, // OUI
1765                0x12, 0x34, // Protocol ID
1766                // Data
1767                1, 2, 3, 4, 5,
1768            ][..]
1769        );
1770    }
1771
1772    #[test_case(false; "Controlled port closed")]
1773    #[test_case(true; "Controlled port open")]
1774    #[fuchsia::test(allow_stalls = false)]
1775    async fn handle_data_frame_is_rsn_eapol(controlled_port_open: bool) {
1776        let (fake_device, fake_device_state) = FakeDevice::new().await;
1777        let (mut ctx, _) = make_context(fake_device);
1778        let mut bss = make_protected_infra_bss(&mut ctx).await;
1779        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1780
1781        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1782        client
1783            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1784            .await
1785            .expect("expected OK");
1786        client
1787            .handle_mlme_assoc_resp(
1788                &mut ctx,
1789                true,
1790                1,
1791                mac::CapabilityInfo(0),
1792                fidl_mlme::AssociateResultCode::Success,
1793                1,
1794                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1795            )
1796            .await
1797            .expect("expected OK");
1798        fake_device_state.lock().wlan_queue.clear();
1799
1800        if controlled_port_open {
1801            client
1802                .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Open)
1803                .expect("expected OK");
1804        }
1805
1806        bss.handle_data_frame(
1807            &mut ctx,
1808            mac::DataFrame {
1809                fixed_fields: mac::FixedDataHdrFields {
1810                    frame_ctrl: mac::FrameControl(0)
1811                        .with_frame_type(mac::FrameType::DATA)
1812                        .with_to_ds(true),
1813                    duration: 0,
1814                    addr1: (*BSSID).into(),
1815                    addr2: *CLIENT_ADDR,
1816                    addr3: *CLIENT_ADDR2,
1817                    seq_ctrl: mac::SequenceControl(10),
1818                }
1819                .as_bytes_ref(),
1820                addr4: None,
1821                qos_ctrl: None,
1822                ht_ctrl: None,
1823                body: &[
1824                    7, 7, 7, // DSAP, SSAP & control
1825                    8, 8, 8, // OUI
1826                    0x12, 0x34, // eth type
1827                    // Trailing bytes
1828                    1, 2, 3, 4, 5,
1829                ][..],
1830            },
1831        )
1832        .expect("expected OK");
1833
1834        if controlled_port_open {
1835            assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
1836        } else {
1837            assert!(fake_device_state.lock().eth_queue.is_empty());
1838        }
1839    }
1840
1841    async fn authenticate_client(
1842        fake_device_state: Arc<Mutex<FakeDeviceState>>,
1843        ctx: &mut Context<FakeDevice>,
1844        bss: &mut InfraBss,
1845        client_addr: MacAddr,
1846    ) {
1847        bss.handle_mgmt_frame(
1848            ctx,
1849            mac::MgmtFrame {
1850                mgmt_hdr: mac::MgmtHdr {
1851                    frame_ctrl: mac::FrameControl(0)
1852                        .with_frame_type(mac::FrameType::MGMT)
1853                        .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
1854                    duration: 0,
1855                    addr1: (*BSSID).into(),
1856                    addr2: client_addr,
1857                    addr3: (*BSSID).into(),
1858                    seq_ctrl: mac::SequenceControl(10),
1859                }
1860                .as_bytes_ref(),
1861                ht_ctrl: None,
1862                body: &[
1863                    // Auth body
1864                    0, 0, // Auth Algorithm Number
1865                    1, 0, // Auth Txn Seq Number
1866                    0, 0, // Status code
1867                ][..],
1868            },
1869        )
1870        .await
1871        .expect("failed to handle auth req frame");
1872
1873        fake_device_state
1874            .lock()
1875            .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
1876            .expect("expected auth indication");
1877        bss.handle_mlme_auth_resp(
1878            ctx,
1879            fidl_mlme::AuthenticateResponse {
1880                peer_sta_address: client_addr.to_array(),
1881                result_code: fidl_mlme::AuthenticateResultCode::Success,
1882            },
1883        )
1884        .await
1885        .expect("failed to handle auth resp");
1886        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1887        fake_device_state.lock().wlan_queue.clear();
1888    }
1889
1890    async fn associate_client(
1891        fake_device_state: Arc<Mutex<FakeDeviceState>>,
1892        ctx: &mut Context<FakeDevice>,
1893        bss: &mut InfraBss,
1894        client_addr: MacAddr,
1895        association_id: u16,
1896    ) {
1897        bss.handle_mgmt_frame(
1898            ctx,
1899            mac::MgmtFrame {
1900                mgmt_hdr: mac::MgmtHdr {
1901                    frame_ctrl: mac::FrameControl(0)
1902                        .with_frame_type(mac::FrameType::MGMT)
1903                        .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_REQ),
1904                    duration: 0,
1905                    addr1: (*BSSID).into(),
1906                    addr2: client_addr,
1907                    addr3: (*BSSID).into(),
1908                    seq_ctrl: mac::SequenceControl(10),
1909                }
1910                .as_bytes_ref(),
1911                ht_ctrl: None,
1912                body: &[
1913                    // Assoc req body
1914                    0, 0, // Capability info
1915                    10, 0, // Listen interval
1916                    // IEs
1917                    1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
1918                    50, 2, 9, 10, // Extended rates
1919                    48, 2, 77, 88, // RSNE
1920                ][..],
1921            },
1922        )
1923        .await
1924        .expect("expected OK");
1925        let msg = fake_device_state
1926            .lock()
1927            .next_mlme_msg::<fidl_mlme::AssociateIndication>()
1928            .expect("expected assoc indication");
1929        bss.handle_mlme_assoc_resp(
1930            ctx,
1931            fidl_mlme::AssociateResponse {
1932                peer_sta_address: client_addr.to_array(),
1933                result_code: fidl_mlme::AssociateResultCode::Success,
1934                association_id,
1935                capability_info: msg.capability_info,
1936                rates: msg.rates,
1937            },
1938        )
1939        .await
1940        .expect("failed to handle assoc resp");
1941        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1942        fake_device_state.lock().wlan_queue.clear();
1943    }
1944
1945    fn send_eth_frame_from_ds_to_client(
1946        ctx: &mut Context<FakeDevice>,
1947        bss: &mut InfraBss,
1948        client_addr: MacAddr,
1949    ) {
1950        bss.handle_eth_frame(
1951            ctx,
1952            EthernetIIHdr {
1953                da: client_addr,
1954                sa: *REMOTE_ADDR,
1955                ether_type: BigEndianU16::from_native(0x1234),
1956            },
1957            &[1, 2, 3, 4, 5][..],
1958            0.into(),
1959        )
1960        .expect("expected OK");
1961    }
1962
1963    #[fuchsia::test(allow_stalls = false)]
1964    async fn handle_multiple_complete_associations() {
1965        let (fake_device, fake_device_state) = FakeDevice::new().await;
1966        let (mut ctx, _) = make_context(fake_device);
1967        let mut bss = make_infra_bss(&mut ctx).await;
1968
1969        authenticate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR).await;
1970        authenticate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR2).await;
1971
1972        associate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR, 1).await;
1973        associate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR2, 2).await;
1974
1975        assert!(bss.clients.contains_key(&CLIENT_ADDR));
1976        assert!(bss.clients.contains_key(&CLIENT_ADDR2));
1977
1978        send_eth_frame_from_ds_to_client(&mut ctx, &mut bss, *CLIENT_ADDR);
1979        send_eth_frame_from_ds_to_client(&mut ctx, &mut bss, *CLIENT_ADDR2);
1980
1981        assert_eq!(fake_device_state.lock().wlan_queue.len(), 2);
1982    }
1983
1984    #[fuchsia::test(allow_stalls = false)]
1985    async fn handle_ps_poll() {
1986        let (fake_device, fake_device_state) = FakeDevice::new().await;
1987        let (mut ctx, _) = make_context(fake_device);
1988        let mut bss = make_infra_bss(&mut ctx).await;
1989        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1990
1991        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1992        client
1993            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1994            .await
1995            .expect("expected OK");
1996        client
1997            .handle_mlme_assoc_resp(
1998                &mut ctx,
1999                false,
2000                1,
2001                mac::CapabilityInfo(0),
2002                fidl_mlme::AssociateResultCode::Success,
2003                1,
2004                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
2005            )
2006            .await
2007            .expect("expected OK");
2008        client.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze ok");
2009        fake_device_state.lock().wlan_queue.clear();
2010
2011        bss.handle_eth_frame(
2012            &mut ctx,
2013            EthernetIIHdr {
2014                da: *CLIENT_ADDR,
2015                sa: *CLIENT_ADDR2,
2016                ether_type: BigEndianU16::from_native(0x1234),
2017            },
2018            &[1, 2, 3, 4, 5][..],
2019            0.into(),
2020        )
2021        .expect("expected OK");
2022        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
2023
2024        bss.handle_ctrl_frame(
2025            &mut ctx,
2026            mac::CtrlFrame {
2027                frame_ctrl: mac::FrameControl(0)
2028                    .with_frame_type(mac::FrameType::CTRL)
2029                    .with_ctrl_subtype(mac::CtrlSubtype::PS_POLL),
2030                body: &[
2031                    0b00000001, 0b11000000, // Masked AID
2032                    2, 2, 2, 2, 2, 2, // RA
2033                    4, 4, 4, 4, 4, 4, // TA
2034                ][..],
2035            },
2036        )
2037        .expect("expected OK");
2038        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2039        assert_eq!(
2040            &fake_device_state.lock().wlan_queue[0].0[..],
2041            &[
2042                // Mgmt header
2043                0b00001000, 0b00000010, // Frame Control
2044                0, 0, // Duration
2045                4, 4, 4, 4, 4, 4, // addr1
2046                2, 2, 2, 2, 2, 2, // addr2
2047                6, 6, 6, 6, 6, 6, // addr3
2048                0x30, 0, // Sequence Control
2049                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
2050                0, 0, 0, // OUI
2051                0x12, 0x34, // Protocol ID
2052                // Data
2053                1, 2, 3, 4, 5,
2054            ][..]
2055        );
2056    }
2057
2058    #[fuchsia::test(allow_stalls = false)]
2059    async fn handle_mlme_setkeys_req() {
2060        let (fake_device, fake_device_state) = FakeDevice::new().await;
2061        let (mut ctx, _) = make_context(fake_device);
2062        let mut bss = make_protected_infra_bss(&mut ctx).await;
2063        bss.handle_mlme_setkeys_req(
2064            &mut ctx,
2065            vec![fidl_mlme::SetKeyDescriptor {
2066                cipher_suite_oui: [1, 2, 3],
2067                cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
2068                key_type: fidl_mlme::KeyType::Pairwise,
2069                address: [5; 6].into(),
2070                key_id: 6,
2071                key: vec![1, 2, 3, 4, 5, 6, 7],
2072                rsc: 8,
2073            }],
2074        )
2075        .await
2076        .expect("expected InfraBss::handle_mlme_setkeys_req OK");
2077        assert_eq!(
2078            fake_device_state.lock().keys,
2079            vec![fidl_softmac::WlanKeyConfiguration {
2080                protection: Some(fidl_softmac::WlanProtection::RxTx),
2081                cipher_oui: Some([1, 2, 3]),
2082                cipher_type: Some(4),
2083                key_type: Some(fidl_ieee80211::KeyType::Pairwise),
2084                peer_addr: Some([5; 6]),
2085                key_idx: Some(6),
2086                key: Some(vec![1, 2, 3, 4, 5, 6, 7]),
2087                rsc: Some(8),
2088                ..Default::default()
2089            }]
2090        );
2091    }
2092
2093    #[fuchsia::test(allow_stalls = false)]
2094    async fn handle_mlme_setkeys_req_no_rsne() {
2095        let (fake_device, fake_device_state) = FakeDevice::new().await;
2096        let (mut ctx, _) = make_context(fake_device);
2097        let mut bss = make_infra_bss(&mut ctx).await;
2098        assert_variant!(
2099            bss.handle_mlme_setkeys_req(
2100                &mut ctx,
2101                vec![fidl_mlme::SetKeyDescriptor {
2102                    cipher_suite_oui: [1, 2, 3],
2103                    cipher_suite_type:
2104                        fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
2105                    key_type: fidl_mlme::KeyType::Pairwise,
2106                    address: [5; 6],
2107                    key_id: 6,
2108                    key: vec![1, 2, 3, 4, 5, 6, 7],
2109                    rsc: 8,
2110                }]
2111            )
2112            .await
2113            .expect_err("expected InfraBss::handle_mlme_setkeys_req error"),
2114            Error::Status(_, zx::Status::BAD_STATE)
2115        );
2116        assert!(fake_device_state.lock().keys.is_empty());
2117    }
2118
2119    #[fuchsia::test(allow_stalls = false)]
2120    async fn handle_probe_req() {
2121        let (fake_device, fake_device_state) = FakeDevice::new().await;
2122        let (mut ctx, _) = make_context(fake_device);
2123        let mut bss = InfraBss::new(
2124            &mut ctx,
2125            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2126            TimeUnit::DEFAULT_BEACON_INTERVAL,
2127            2,
2128            mac::CapabilityInfo(33),
2129            vec![248],
2130            1,
2131            Some(vec![48, 2, 77, 88]),
2132        )
2133        .await
2134        .expect("expected InfraBss::new ok");
2135
2136        bss.handle_probe_req(&mut ctx, *CLIENT_ADDR)
2137            .expect("expected InfraBss::handle_probe_req ok");
2138        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2139        assert_eq!(
2140            &fake_device_state.lock().wlan_queue[0].0[..],
2141            &[
2142                // Mgmt header
2143                0b01010000, 0, // Frame Control
2144                0, 0, // Duration
2145                4, 4, 4, 4, 4, 4, // addr1
2146                2, 2, 2, 2, 2, 2, // addr2
2147                2, 2, 2, 2, 2, 2, // addr3
2148                0x10, 0, // Sequence Control
2149                // Beacon header:
2150                0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
2151                100, 0, // Beacon interval
2152                33, 0, // Capabilities
2153                // IEs:
2154                0, 5, 1, 2, 3, 4, 5, // SSID
2155                1, 1, 248, // Supported rates
2156                3, 1, 1, // DSSS parameter set
2157                48, 2, 77, 88, // RSNE
2158            ][..]
2159        );
2160    }
2161
2162    #[fuchsia::test(allow_stalls = false)]
2163    async fn handle_probe_req_has_offload() {
2164        let (fake_device, _fake_device_state) = FakeDevice::new_with_config(
2165            FakeDeviceConfig::default().with_mock_probe_response_offload(
2166                fidl_common::ProbeResponseOffloadExtension { supported: true },
2167            ),
2168        )
2169        .await;
2170
2171        let (mut ctx, _) = make_context(fake_device);
2172        let mut bss = InfraBss::new(
2173            &mut ctx,
2174            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2175            TimeUnit::DEFAULT_BEACON_INTERVAL,
2176            2,
2177            CapabilityInfo(33),
2178            vec![0b11111000],
2179            1,
2180            Some(vec![48, 2, 77, 88]),
2181        )
2182        .await
2183        .expect("expected InfraBss::new ok");
2184
2185        bss.handle_mgmt_frame(
2186            &mut ctx,
2187            mac::MgmtFrame {
2188                mgmt_hdr: mac::MgmtHdr {
2189                    frame_ctrl: mac::FrameControl(0)
2190                        .with_frame_type(mac::FrameType::MGMT)
2191                        .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2192                    duration: 0,
2193                    addr1: (*BSSID).into(),
2194                    addr2: *CLIENT_ADDR,
2195                    addr3: (*BSSID).into(),
2196                    seq_ctrl: mac::SequenceControl(10),
2197                }
2198                .as_bytes_ref(),
2199                ht_ctrl: None,
2200                body: &[][..],
2201            },
2202        )
2203        .await
2204        .expect_err("expected InfraBss::handle_mgmt_frame error");
2205    }
2206
2207    #[fuchsia::test(allow_stalls = false)]
2208    async fn handle_probe_req_wildcard_ssid() {
2209        let (fake_device, fake_device_state) = FakeDevice::new().await;
2210        let (mut ctx, _) = make_context(fake_device);
2211        let mut bss = InfraBss::new(
2212            &mut ctx,
2213            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2214            TimeUnit::DEFAULT_BEACON_INTERVAL,
2215            2,
2216            CapabilityInfo(33),
2217            vec![0b11111000],
2218            1,
2219            Some(vec![48, 2, 77, 88]),
2220        )
2221        .await
2222        .expect("expected InfraBss::new ok");
2223
2224        bss.handle_mgmt_frame(
2225            &mut ctx,
2226            mac::MgmtFrame {
2227                mgmt_hdr: mac::MgmtHdr {
2228                    frame_ctrl: mac::FrameControl(0)
2229                        .with_frame_type(mac::FrameType::MGMT)
2230                        .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2231                    duration: 0,
2232                    addr1: (*BSSID).into(),
2233                    addr2: *CLIENT_ADDR,
2234                    addr3: (*BSSID).into(),
2235                    seq_ctrl: mac::SequenceControl(10),
2236                }
2237                .as_bytes_ref(),
2238                ht_ctrl: None,
2239                body: &[
2240                    0, 0, // Wildcard SSID
2241                ][..],
2242            },
2243        )
2244        .await
2245        .expect("expected InfraBss::handle_mgmt_frame ok");
2246
2247        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2248        assert_eq!(
2249            &fake_device_state.lock().wlan_queue[0].0[..],
2250            &[
2251                // Mgmt header
2252                0b01010000, 0, // Frame Control
2253                0, 0, // Duration
2254                4, 4, 4, 4, 4, 4, // addr1
2255                2, 2, 2, 2, 2, 2, // addr2
2256                2, 2, 2, 2, 2, 2, // addr3
2257                0x10, 0, // Sequence Control
2258                // Beacon header:
2259                0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
2260                100, 0, // Beacon interval
2261                33, 0, // Capabilities
2262                // IEs:
2263                0, 5, 1, 2, 3, 4, 5, // SSID
2264                1, 1, 248, // Supported rates
2265                3, 1, 1, // DSSS parameter set
2266                48, 2, 77, 88, // RSNE
2267            ][..]
2268        );
2269    }
2270
2271    #[fuchsia::test(allow_stalls = false)]
2272    async fn handle_probe_req_matching_ssid() {
2273        let (fake_device, fake_device_state) = FakeDevice::new().await;
2274        let (mut ctx, _) = make_context(fake_device);
2275        let mut bss = InfraBss::new(
2276            &mut ctx,
2277            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2278            TimeUnit::DEFAULT_BEACON_INTERVAL,
2279            2,
2280            CapabilityInfo(33),
2281            vec![0b11111000],
2282            1,
2283            Some(vec![48, 2, 77, 88]),
2284        )
2285        .await
2286        .expect("expected InfraBss::new ok");
2287
2288        bss.handle_mgmt_frame(
2289            &mut ctx,
2290            mac::MgmtFrame {
2291                mgmt_hdr: mac::MgmtHdr {
2292                    frame_ctrl: mac::FrameControl(0)
2293                        .with_frame_type(mac::FrameType::MGMT)
2294                        .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2295                    duration: 0,
2296                    addr1: (*BSSID).into(),
2297                    addr2: *CLIENT_ADDR,
2298                    addr3: (*BSSID).into(),
2299                    seq_ctrl: mac::SequenceControl(10),
2300                }
2301                .as_bytes_ref(),
2302                ht_ctrl: None,
2303                body: &[0, 5, 1, 2, 3, 4, 5][..],
2304            },
2305        )
2306        .await
2307        .expect("expected InfraBss::handle_mgmt_frame ok");
2308
2309        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2310        assert_eq!(
2311            &fake_device_state.lock().wlan_queue[0].0[..],
2312            &[
2313                // Mgmt header
2314                0b01010000, 0, // Frame Control
2315                0, 0, // Duration
2316                4, 4, 4, 4, 4, 4, // addr1
2317                2, 2, 2, 2, 2, 2, // addr2
2318                2, 2, 2, 2, 2, 2, // addr3
2319                0x10, 0, // Sequence Control
2320                // Beacon header:
2321                0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
2322                100, 0, // Beacon interval
2323                33, 0, // Capabilities
2324                // IEs:
2325                0, 5, 1, 2, 3, 4, 5, // SSID
2326                1, 1, 248, // Supported rates
2327                3, 1, 1, // DSSS parameter set
2328                48, 2, 77, 88, // RSNE
2329            ][..]
2330        );
2331    }
2332
2333    #[fuchsia::test(allow_stalls = false)]
2334    async fn handle_probe_req_mismatching_ssid() {
2335        let (fake_device, _) = FakeDevice::new().await;
2336        let (mut ctx, _) = make_context(fake_device);
2337        let mut bss = InfraBss::new(
2338            &mut ctx,
2339            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2340            TimeUnit::DEFAULT_BEACON_INTERVAL,
2341            2,
2342            CapabilityInfo(33),
2343            vec![0b11111000],
2344            1,
2345            Some(vec![48, 2, 77, 88]),
2346        )
2347        .await
2348        .expect("expected InfraBss::new ok");
2349
2350        assert_variant!(
2351            bss.handle_mgmt_frame(
2352                &mut ctx,
2353                mac::MgmtFrame {
2354                    mgmt_hdr: mac::MgmtHdr {
2355                        frame_ctrl: mac::FrameControl(0)
2356                            .with_frame_type(mac::FrameType::MGMT)
2357                            .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2358                        duration: 0,
2359                        addr1: (*BSSID).into(),
2360                        addr2: *CLIENT_ADDR,
2361                        addr3: (*BSSID).into(),
2362                        seq_ctrl: mac::SequenceControl(10),
2363                    }
2364                    .as_bytes_ref(),
2365                    ht_ctrl: None,
2366                    body: &[0, 5, 1, 2, 3, 4, 6][..],
2367                },
2368            )
2369            .await
2370            .expect_err("expected InfraBss::handle_mgmt_frame error"),
2371            Rejection::OtherBss
2372        );
2373    }
2374
2375    #[fuchsia::test(allow_stalls = false)]
2376    async fn make_tim() {
2377        let (fake_device, _) = FakeDevice::new().await;
2378        let (mut ctx, _) = make_context(fake_device);
2379        let mut bss = make_infra_bss(&mut ctx).await;
2380        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
2381
2382        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
2383        client
2384            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
2385            .await
2386            .expect("expected OK");
2387        client
2388            .handle_mlme_assoc_resp(
2389                &mut ctx,
2390                false,
2391                1,
2392                mac::CapabilityInfo(0),
2393                fidl_mlme::AssociateResultCode::Success,
2394                1,
2395                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
2396            )
2397            .await
2398            .expect("expected OK");
2399        client.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
2400
2401        bss.handle_eth_frame(
2402            &mut ctx,
2403            EthernetIIHdr {
2404                da: *CLIENT_ADDR,
2405                sa: *CLIENT_ADDR2,
2406                ether_type: BigEndianU16::from_native(0x1234),
2407            },
2408            &[1, 2, 3, 4, 5][..],
2409            0.into(),
2410        )
2411        .expect("expected OK");
2412
2413        let tim = bss.make_tim();
2414        let (pvb_offset, pvb_bitmap) = tim.make_partial_virtual_bitmap();
2415        assert_eq!(pvb_offset, 0);
2416        assert_eq!(pvb_bitmap, &[0b00000010][..]);
2417    }
2418
2419    #[fuchsia::test(allow_stalls = false)]
2420    async fn make_tim_empty() {
2421        let (fake_device, _) = FakeDevice::new().await;
2422        let (mut ctx, _) = make_context(fake_device);
2423        let bss = make_infra_bss(&mut ctx).await;
2424
2425        let tim = bss.make_tim();
2426        let (pvb_offset, pvb_bitmap) = tim.make_partial_virtual_bitmap();
2427        assert_eq!(pvb_offset, 0);
2428        assert_eq!(pvb_bitmap, &[0b00000000][..]);
2429    }
2430}