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::WlanTxPacketExt as _;
6use crate::ap::remote_client::{ClientRejection, RemoteClient};
7use crate::ap::{BeaconOffloadParams, BufferedFrame, Context, Rejection, TimedEvent, frame_writer};
8use crate::ddk_converter::softmac_key_configuration_from_mlme;
9use crate::device::{self, DeviceOps};
10use crate::error::Error;
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::{TimeUnit, ie, tim};
19use zerocopy::SplitByteSlice;
20use {
21    fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, 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_ieee80211::WlanChannel {
83                primary: channel,
84
85                // TODO(https://fxbug.dev/42116942): Correctly support this.
86                cbw: fidl_ieee80211::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_with(|| 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_with(|| 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.get(),
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_with(|| RemoteClient::new(hdr.da)));
565        client
566            .handle_eth_frame(ctx, hdr.da, hdr.sa, hdr.ether_type.get(), 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 assert_matches::assert_matches;
619    use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
620    use fuchsia_sync::Mutex;
621    use ieee80211::Bssid;
622    use std::sync::{Arc, LazyLock};
623    use test_case::test_case;
624    use wlan_common::mac::IntoBytesExt as _;
625    use wlan_common::test_utils::fake_frames::fake_wpa2_rsne;
626    use wlan_common::timer::{self, create_timer};
627    use zerocopy::byteorder::big_endian::U16 as BigEndianU16;
628
629    static CLIENT_ADDR: LazyLock<MacAddr> = LazyLock::new(|| [4u8; 6].into());
630    static BSSID: LazyLock<Bssid> = LazyLock::new(|| [2u8; 6].into());
631    static CLIENT_ADDR2: LazyLock<MacAddr> = LazyLock::new(|| [6u8; 6].into());
632    static REMOTE_ADDR: LazyLock<MacAddr> = LazyLock::new(|| [123u8; 6].into());
633
634    fn make_context(
635        fake_device: FakeDevice,
636    ) -> (Context<FakeDevice>, timer::EventStream<TimedEvent>) {
637        let (timer, time_stream) = create_timer();
638        (Context::new(fake_device, timer, *BSSID), time_stream)
639    }
640
641    async fn make_infra_bss(ctx: &mut Context<FakeDevice>) -> InfraBss {
642        InfraBss::new(
643            ctx,
644            Ssid::try_from("coolnet").unwrap(),
645            TimeUnit::DEFAULT_BEACON_INTERVAL,
646            2,
647            CapabilityInfo(0),
648            vec![0b11111000],
649            1,
650            None,
651        )
652        .await
653        .expect("expected InfraBss::new ok")
654    }
655
656    async fn make_protected_infra_bss(ctx: &mut Context<FakeDevice>) -> InfraBss {
657        InfraBss::new(
658            ctx,
659            Ssid::try_from("coolnet").unwrap(),
660            TimeUnit::DEFAULT_BEACON_INTERVAL,
661            2,
662            CapabilityInfo(0),
663            vec![0b11111000],
664            1,
665            Some(fake_wpa2_rsne()),
666        )
667        .await
668        .expect("expected InfraBss::new ok")
669    }
670
671    #[fuchsia::test(allow_stalls = false)]
672    async fn new() {
673        let (fake_device, fake_device_state) = FakeDevice::new().await;
674        let (mut ctx, _) = make_context(fake_device);
675        InfraBss::new(
676            &mut ctx,
677            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
678            TimeUnit::DEFAULT_BEACON_INTERVAL,
679            2,
680            CapabilityInfo(0).with_ess(true),
681            vec![0b11111000],
682            1,
683            None,
684        )
685        .await
686        .expect("expected InfraBss::new ok");
687
688        assert_eq!(
689            fake_device_state.lock().wlan_channel,
690            fidl_ieee80211::WlanChannel {
691                primary: 1,
692                cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
693                secondary80: 0
694            }
695        );
696
697        let beacon_tmpl = vec![
698            // Mgmt header
699            0b10000000, 0, // Frame Control
700            0, 0, // Duration
701            255, 255, 255, 255, 255, 255, // addr1
702            2, 2, 2, 2, 2, 2, // addr2
703            2, 2, 2, 2, 2, 2, // addr3
704            0, 0, // Sequence Control
705            // Beacon header:
706            0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
707            100, 0, // Beacon interval
708            1, 0, // Capabilities
709            // IEs:
710            0, 5, 1, 2, 3, 4, 5, // SSID
711            1, 1, 0b11111000, // Basic rates
712            3, 1, 1, // DSSS parameter set
713            5, 4, 0, 2, 0, 0, // TIM
714        ];
715
716        assert_eq!(
717            fake_device_state.lock().beacon_config.as_ref().expect("expected beacon_config"),
718            &(beacon_tmpl, 49, TimeUnit::DEFAULT_BEACON_INTERVAL)
719        );
720    }
721
722    #[fuchsia::test(allow_stalls = false)]
723    async fn stop() {
724        let (fake_device, fake_device_state) = FakeDevice::new().await;
725        let (mut ctx, _) = make_context(fake_device);
726        let bss = make_infra_bss(&mut ctx).await;
727        bss.stop(&mut ctx).await.expect("expected InfraBss::stop ok");
728        assert!(fake_device_state.lock().beacon_config.is_none());
729    }
730
731    #[fuchsia::test(allow_stalls = false)]
732    async fn handle_mlme_auth_resp() {
733        let (fake_device, fake_device_state) = FakeDevice::new().await;
734        let (mut ctx, _) = make_context(fake_device);
735        let mut bss = make_infra_bss(&mut ctx).await;
736
737        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
738
739        bss.handle_mlme_auth_resp(
740            &mut ctx,
741            fidl_mlme::AuthenticateResponse {
742                peer_sta_address: CLIENT_ADDR.to_array(),
743                result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
744            },
745        )
746        .await
747        .expect("expected InfraBss::handle_mlme_auth_resp ok");
748        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
749        assert_eq!(
750            &fake_device_state.lock().wlan_queue[0].0[..],
751            &[
752                // Mgmt header
753                0b10110000, 0, // Frame Control
754                0, 0, // Duration
755                4, 4, 4, 4, 4, 4, // addr1
756                2, 2, 2, 2, 2, 2, // addr2
757                2, 2, 2, 2, 2, 2, // addr3
758                0x10, 0, // Sequence Control
759                // Auth header:
760                0, 0, // auth algorithm
761                2, 0, // auth txn seq num
762                76, 0, // status code
763            ][..]
764        );
765    }
766
767    #[fuchsia::test(allow_stalls = false)]
768    async fn handle_mlme_auth_resp_no_such_client() {
769        let (fake_device, _) = FakeDevice::new().await;
770        let (mut ctx, _) = make_context(fake_device);
771        let mut bss = make_infra_bss(&mut ctx).await;
772
773        assert_eq!(
774            zx::Status::from(
775                bss.handle_mlme_auth_resp(
776                    &mut ctx,
777                    fidl_mlme::AuthenticateResponse {
778                        peer_sta_address: CLIENT_ADDR.to_array(),
779                        result_code: fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
780                    },
781                )
782                .await
783                .expect_err("expected InfraBss::handle_mlme_auth_resp error")
784            ),
785            zx::Status::NOT_FOUND
786        );
787    }
788
789    #[fuchsia::test(allow_stalls = false)]
790    async fn handle_mlme_deauth_req() {
791        let (fake_device, fake_device_state) = FakeDevice::new().await;
792        let (mut ctx, _) = make_context(fake_device);
793        let mut bss = make_infra_bss(&mut ctx).await;
794
795        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
796
797        bss.handle_mlme_deauth_req(
798            &mut ctx,
799            fidl_mlme::DeauthenticateRequest {
800                peer_sta_address: CLIENT_ADDR.to_array(),
801                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDeauth,
802            },
803        )
804        .await
805        .expect("expected InfraBss::handle_mlme_deauth_req ok");
806        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
807        assert_eq!(
808            &fake_device_state.lock().wlan_queue[0].0[..],
809            &[
810                // Mgmt header
811                0b11000000, 0, // Frame Control
812                0, 0, // Duration
813                4, 4, 4, 4, 4, 4, // addr1
814                2, 2, 2, 2, 2, 2, // addr2
815                2, 2, 2, 2, 2, 2, // addr3
816                0x10, 0, // Sequence Control
817                // Deauth header:
818                3, 0, // reason code
819            ][..]
820        );
821
822        assert!(!bss.clients.contains_key(&CLIENT_ADDR));
823    }
824
825    #[fuchsia::test(allow_stalls = false)]
826    async fn handle_mlme_assoc_resp() {
827        let (fake_device, fake_device_state) = FakeDevice::new().await;
828        let (mut ctx, _) = make_context(fake_device);
829        let mut bss = make_infra_bss(&mut ctx).await;
830
831        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
832        bss.handle_mlme_assoc_resp(
833            &mut ctx,
834            fidl_mlme::AssociateResponse {
835                peer_sta_address: CLIENT_ADDR.to_array(),
836                result_code: fidl_mlme::AssociateResultCode::Success,
837                association_id: 1,
838                capability_info: 0,
839                rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
840            },
841        )
842        .await
843        .expect("expected InfraBss::handle_mlme_assoc_resp ok");
844        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
845        assert_eq!(
846            &fake_device_state.lock().wlan_queue[0].0[..],
847            &[
848                // Mgmt header
849                0b00010000, 0, // Frame Control
850                0, 0, // Duration
851                4, 4, 4, 4, 4, 4, // addr1
852                2, 2, 2, 2, 2, 2, // addr2
853                2, 2, 2, 2, 2, 2, // addr3
854                0x10, 0, // Sequence Control
855                // Association response header:
856                0, 0, // Capabilities
857                0, 0, // status code
858                1, 0, // AID
859                // IEs
860                1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
861                50, 2, 9, 10, // Extended rates
862                90, 3, 90, 0, 0, // BSS max idle period
863            ][..]
864        );
865        assert!(fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
866    }
867
868    #[fuchsia::test(allow_stalls = false)]
869    async fn handle_mlme_assoc_resp_with_caps() {
870        let (fake_device, fake_device_state) = FakeDevice::new().await;
871        let (mut ctx, _) = make_context(fake_device);
872        let mut bss = InfraBss::new(
873            &mut ctx,
874            Ssid::try_from("coolnet").unwrap(),
875            TimeUnit::DEFAULT_BEACON_INTERVAL,
876            2,
877            CapabilityInfo(0).with_short_preamble(true).with_ess(true),
878            vec![0b11111000],
879            1,
880            None,
881        )
882        .await
883        .expect("expected InfraBss::new ok");
884
885        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
886
887        bss.handle_mlme_assoc_resp(
888            &mut ctx,
889            fidl_mlme::AssociateResponse {
890                peer_sta_address: CLIENT_ADDR.to_array(),
891                result_code: fidl_mlme::AssociateResultCode::Success,
892                association_id: 1,
893                capability_info: CapabilityInfo(0).with_short_preamble(true).raw(),
894                rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
895            },
896        )
897        .await
898        .expect("expected InfraBss::handle_mlme_assoc_resp ok");
899        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
900        assert_eq!(
901            &fake_device_state.lock().wlan_queue[0].0[..],
902            &[
903                // Mgmt header
904                0b00010000, 0, // Frame Control
905                0, 0, // Duration
906                4, 4, 4, 4, 4, 4, // addr1
907                2, 2, 2, 2, 2, 2, // addr2
908                2, 2, 2, 2, 2, 2, // addr3
909                0x10, 0, // Sequence Control
910                // Association response header:
911                0b00100000, 0b00000000, // Capabilities
912                0, 0, // status code
913                1, 0, // AID
914                // IEs
915                1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
916                50, 2, 9, 10, // Extended rates
917                90, 3, 90, 0, 0, // BSS max idle period
918            ][..]
919        );
920        assert!(fake_device_state.lock().assocs.contains_key(&CLIENT_ADDR));
921    }
922
923    #[fuchsia::test(allow_stalls = false)]
924    async fn handle_mlme_disassoc_req() {
925        let (fake_device, fake_device_state) = FakeDevice::new().await;
926        let (mut ctx, _) = make_context(fake_device);
927        let mut bss = make_infra_bss(&mut ctx).await;
928
929        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
930
931        bss.handle_mlme_disassoc_req(
932            &mut ctx,
933            fidl_mlme::DisassociateRequest {
934                peer_sta_address: CLIENT_ADDR.to_array(),
935                reason_code: fidl_ieee80211::ReasonCode::LeavingNetworkDisassoc,
936            },
937        )
938        .await
939        .expect("expected InfraBss::handle_mlme_disassoc_req ok");
940        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
941        assert_eq!(
942            &fake_device_state.lock().wlan_queue[0].0[..],
943            &[
944                // Mgmt header
945                0b10100000, 0, // Frame Control
946                0, 0, // Duration
947                4, 4, 4, 4, 4, 4, // addr1
948                2, 2, 2, 2, 2, 2, // addr2
949                2, 2, 2, 2, 2, 2, // addr3
950                0x10, 0, // Sequence Control
951                // Disassoc header:
952                8, 0, // reason code
953            ][..]
954        );
955    }
956
957    #[fuchsia::test(allow_stalls = false)]
958    async fn handle_mlme_set_controlled_port_req() {
959        let (fake_device, _) = FakeDevice::new().await;
960        let (mut ctx, _) = make_context(fake_device);
961        let mut bss = make_protected_infra_bss(&mut ctx).await;
962
963        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
964
965        bss.handle_mlme_assoc_resp(
966            &mut ctx,
967            fidl_mlme::AssociateResponse {
968                peer_sta_address: CLIENT_ADDR.to_array(),
969                result_code: fidl_mlme::AssociateResultCode::Success,
970                association_id: 1,
971                capability_info: 0,
972                rates: vec![1, 2, 3],
973            },
974        )
975        .await
976        .expect("expected InfraBss::handle_mlme_assoc_resp ok");
977
978        bss.handle_mlme_set_controlled_port_req(fidl_mlme::SetControlledPortRequest {
979            peer_sta_address: CLIENT_ADDR.to_array(),
980            state: fidl_mlme::ControlledPortState::Open,
981        })
982        .expect("expected InfraBss::handle_mlme_set_controlled_port_req ok");
983    }
984
985    #[fuchsia::test(allow_stalls = false)]
986    async fn handle_mlme_eapol_req() {
987        let (fake_device, fake_device_state) = FakeDevice::new().await;
988        let (mut ctx, _) = make_context(fake_device);
989        let mut bss = make_infra_bss(&mut ctx).await;
990
991        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
992
993        bss.handle_mlme_eapol_req(
994            &mut ctx,
995            fidl_mlme::EapolRequest {
996                dst_addr: CLIENT_ADDR.to_array(),
997                src_addr: BSSID.to_array(),
998                data: vec![1, 2, 3],
999            },
1000        )
1001        .expect("expected InfraBss::handle_mlme_eapol_req ok");
1002        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1003        assert_eq!(
1004            &fake_device_state.lock().wlan_queue[0].0[..],
1005            &[
1006                // Header
1007                0b00001000, 0b00000010, // Frame Control
1008                0, 0, // Duration
1009                4, 4, 4, 4, 4, 4, // addr1
1010                2, 2, 2, 2, 2, 2, // addr2
1011                2, 2, 2, 2, 2, 2, // addr3
1012                0x10, 0, // Sequence Control
1013                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1014                0, 0, 0, // OUI
1015                0x88, 0x8E, // EAPOL protocol ID
1016                // Data
1017                1, 2, 3,
1018            ][..]
1019        );
1020
1021        let confirm = fake_device_state
1022            .lock()
1023            .next_mlme_msg::<fidl_mlme::EapolConfirm>()
1024            .expect("Did not receive valid Eapol Confirm msg");
1025        assert_eq!(confirm.result_code, fidl_mlme::EapolResultCode::Success);
1026        assert_eq!(&confirm.dst_addr, CLIENT_ADDR.as_array());
1027    }
1028
1029    #[fuchsia::test(allow_stalls = false)]
1030    async fn handle_mgmt_frame_auth() {
1031        let (fake_device, fake_device_state) = FakeDevice::new().await;
1032        let (mut ctx, _) = make_context(fake_device);
1033        let mut bss = make_infra_bss(&mut ctx).await;
1034
1035        bss.handle_mgmt_frame(
1036            &mut ctx,
1037            mac::MgmtFrame {
1038                mgmt_hdr: mac::MgmtHdr {
1039                    frame_ctrl: mac::FrameControl(0)
1040                        .with_frame_type(mac::FrameType::MGMT)
1041                        .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
1042                    duration: 0,
1043                    addr1: (*BSSID).into(),
1044                    addr2: *CLIENT_ADDR,
1045                    addr3: (*BSSID).into(),
1046                    seq_ctrl: mac::SequenceControl(10),
1047                }
1048                .as_bytes_ref(),
1049                ht_ctrl: None,
1050                body: &[
1051                    // Auth body
1052                    0, 0, // Auth Algorithm Number
1053                    1, 0, // Auth Txn Seq Number
1054                    0, 0, // Status code
1055                ][..],
1056            },
1057        )
1058        .await
1059        .expect("expected OK");
1060
1061        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), true);
1062
1063        let msg = fake_device_state
1064            .lock()
1065            .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
1066            .expect("expected MLME message");
1067        assert_eq!(
1068            msg,
1069            fidl_mlme::AuthenticateIndication {
1070                peer_sta_address: CLIENT_ADDR.to_array(),
1071                auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
1072            },
1073        );
1074    }
1075
1076    #[fuchsia::test(allow_stalls = false)]
1077    async fn handle_mgmt_frame_assoc_req() {
1078        let (fake_device, fake_device_state) = FakeDevice::new().await;
1079        let (mut ctx, _) = make_context(fake_device);
1080        let mut bss = make_infra_bss(&mut ctx).await;
1081
1082        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1083        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1084        client
1085            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1086            .await
1087            .expect("expected OK");
1088
1089        bss.handle_mgmt_frame(
1090            &mut ctx,
1091            mac::MgmtFrame {
1092                mgmt_hdr: mac::MgmtHdr {
1093                    frame_ctrl: mac::FrameControl(0)
1094                        .with_frame_type(mac::FrameType::MGMT)
1095                        .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_REQ),
1096                    duration: 0,
1097                    addr1: (*BSSID).into(),
1098                    addr2: *CLIENT_ADDR,
1099                    addr3: (*BSSID).into(),
1100                    seq_ctrl: mac::SequenceControl(10),
1101                }
1102                .as_bytes_ref(),
1103                ht_ctrl: None,
1104                body: &[
1105                    // Assoc req body
1106                    0, 0, // Capability info
1107                    10, 0, // Listen interval
1108                    // IEs
1109                    1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
1110                    50, 2, 9, 10, // Extended rates
1111                    48, 2, 77, 88, // RSNE
1112                ][..],
1113            },
1114        )
1115        .await
1116        .expect("expected OK");
1117
1118        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), true);
1119
1120        let msg = fake_device_state
1121            .lock()
1122            .next_mlme_msg::<fidl_mlme::AssociateIndication>()
1123            .expect("expected MLME message");
1124        assert_eq!(
1125            msg,
1126            fidl_mlme::AssociateIndication {
1127                peer_sta_address: CLIENT_ADDR.to_array(),
1128                listen_interval: 10,
1129                ssid: Some(Ssid::try_from("coolnet").unwrap().into()),
1130                capability_info: 0,
1131                rates: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
1132                rsne: Some(vec![48, 2, 77, 88]),
1133            },
1134        );
1135    }
1136
1137    #[fuchsia::test(allow_stalls = false)]
1138    async fn handle_mgmt_frame_bad_ds_bits_to_ds() {
1139        let (fake_device, _) = FakeDevice::new().await;
1140        let (mut ctx, _) = make_context(fake_device);
1141        let mut bss = make_infra_bss(&mut ctx).await;
1142
1143        assert_matches!(
1144            bss.handle_mgmt_frame(
1145                &mut ctx,
1146                mac::MgmtFrame {
1147                    mgmt_hdr: mac::MgmtHdr {
1148                        frame_ctrl: mac::FrameControl(0)
1149                            .with_frame_type(mac::FrameType::MGMT)
1150                            .with_mgmt_subtype(mac::MgmtSubtype::AUTH)
1151                            .with_to_ds(true),
1152                        duration: 0,
1153                        addr1: (*BSSID).into(),
1154                        addr2: *CLIENT_ADDR,
1155                        addr3: (*BSSID).into(),
1156                        seq_ctrl: mac::SequenceControl(10),
1157                    }
1158                    .as_bytes_ref(),
1159                    ht_ctrl: None,
1160                    body: &[
1161                        // Auth body
1162                        0, 0, // Auth Algorithm Number
1163                        1, 0, // Auth Txn Seq Number
1164                        0, 0, // Status code
1165                    ][..],
1166                },
1167            )
1168            .await
1169            .expect_err("expected error"),
1170            Rejection::BadDsBits
1171        );
1172
1173        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1174    }
1175
1176    #[fuchsia::test(allow_stalls = false)]
1177    async fn handle_mgmt_frame_bad_ds_bits_from_ds() {
1178        let (fake_device, _) = FakeDevice::new().await;
1179        let (mut ctx, _) = make_context(fake_device);
1180        let mut bss = make_infra_bss(&mut ctx).await;
1181
1182        assert_matches!(
1183            bss.handle_mgmt_frame(
1184                &mut ctx,
1185                mac::MgmtFrame {
1186                    mgmt_hdr: mac::MgmtHdr {
1187                        frame_ctrl: mac::FrameControl(0)
1188                            .with_frame_type(mac::FrameType::MGMT)
1189                            .with_mgmt_subtype(mac::MgmtSubtype::AUTH)
1190                            .with_from_ds(true),
1191                        duration: 0,
1192                        addr1: (*BSSID).into(),
1193                        addr2: *CLIENT_ADDR,
1194                        addr3: (*BSSID).into(),
1195                        seq_ctrl: mac::SequenceControl(10),
1196                    }
1197                    .as_bytes_ref(),
1198                    ht_ctrl: None,
1199                    body: &[
1200                        // Auth body
1201                        0, 0, // Auth Algorithm Number
1202                        1, 0, // Auth Txn Seq Number
1203                        0, 0, // Status code
1204                    ][..],
1205                },
1206            )
1207            .await
1208            .expect_err("expected error"),
1209            Rejection::BadDsBits
1210        );
1211
1212        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1213    }
1214
1215    #[fuchsia::test(allow_stalls = false)]
1216    async fn handle_mgmt_frame_no_such_client() {
1217        let (fake_device, _) = FakeDevice::new().await;
1218        let (mut ctx, _) = make_context(fake_device);
1219        let mut bss = make_infra_bss(&mut ctx).await;
1220
1221        assert_matches!(
1222            bss.handle_mgmt_frame(
1223                &mut ctx,
1224                mac::MgmtFrame {
1225                    mgmt_hdr: mac::MgmtHdr {
1226                        frame_ctrl: mac::FrameControl(0)
1227                            .with_frame_type(mac::FrameType::MGMT)
1228                            .with_mgmt_subtype(mac::MgmtSubtype::DISASSOC),
1229                        duration: 0,
1230                        addr1: (*BSSID).into(),
1231                        addr2: *CLIENT_ADDR,
1232                        addr3: (*BSSID).into(),
1233                        seq_ctrl: mac::SequenceControl(10),
1234                    }
1235                    .as_bytes_ref(),
1236                    ht_ctrl: None,
1237                    body: &[
1238                        // Disassoc header:
1239                        8, 0, // reason code
1240                    ][..],
1241                },
1242            )
1243            .await
1244            .expect_err("expected error"),
1245            Rejection::Client(_, ClientRejection::NotPermitted)
1246        );
1247
1248        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1249    }
1250
1251    #[fuchsia::test(allow_stalls = false)]
1252    async fn handle_mgmt_frame_bogus() {
1253        let (fake_device, _) = FakeDevice::new().await;
1254        let (mut ctx, _) = make_context(fake_device);
1255        let mut bss = make_infra_bss(&mut ctx).await;
1256
1257        assert_matches!(
1258            bss.handle_mgmt_frame(
1259                &mut ctx,
1260                mac::MgmtFrame {
1261                    mgmt_hdr: mac::MgmtHdr {
1262                        frame_ctrl: mac::FrameControl(0)
1263                            .with_frame_type(mac::FrameType::MGMT)
1264                            .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
1265                        duration: 0,
1266                        addr1: (*BSSID).into(),
1267                        addr2: *CLIENT_ADDR,
1268                        addr3: (*BSSID).into(),
1269                        seq_ctrl: mac::SequenceControl(10),
1270                    }
1271                    .as_bytes_ref(),
1272                    ht_ctrl: None,
1273                    // Auth frame should have a header; doesn't.
1274                    body: &[][..],
1275                },
1276            )
1277            .await
1278            .expect_err("expected error"),
1279            Rejection::Client(_, ClientRejection::ParseFailed)
1280        );
1281
1282        assert_eq!(bss.clients.contains_key(&CLIENT_ADDR), false);
1283    }
1284
1285    #[fuchsia::test(allow_stalls = false)]
1286    async fn handle_data_frame() {
1287        let (fake_device, fake_device_state) = FakeDevice::new().await;
1288        let (mut ctx, _) = make_context(fake_device);
1289        let mut bss = make_infra_bss(&mut ctx).await;
1290
1291        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1292        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1293
1294        // Move the client to associated so it can handle data frames.
1295        client
1296            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1297            .await
1298            .expect("expected OK");
1299        client
1300            .handle_mlme_assoc_resp(
1301                &mut ctx,
1302                false,
1303                1,
1304                mac::CapabilityInfo(0),
1305                fidl_mlme::AssociateResultCode::Success,
1306                1,
1307                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1308            )
1309            .await
1310            .expect("expected OK");
1311
1312        bss.handle_data_frame(
1313            &mut ctx,
1314            mac::DataFrame {
1315                fixed_fields: mac::FixedDataHdrFields {
1316                    frame_ctrl: mac::FrameControl(0)
1317                        .with_frame_type(mac::FrameType::DATA)
1318                        .with_to_ds(true),
1319                    duration: 0,
1320                    addr1: (*BSSID).into(),
1321                    addr2: *CLIENT_ADDR,
1322                    addr3: *CLIENT_ADDR2,
1323                    seq_ctrl: mac::SequenceControl(10),
1324                }
1325                .as_bytes_ref(),
1326                addr4: None,
1327                qos_ctrl: None,
1328                ht_ctrl: None,
1329                body: &[
1330                    7, 7, 7, // DSAP, SSAP & control
1331                    8, 8, 8, // OUI
1332                    0x12, 0x34, // eth type
1333                    // Trailing bytes
1334                    1, 2, 3, 4, 5,
1335                ][..],
1336            },
1337        )
1338        .expect("expected OK");
1339
1340        assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
1341        assert_eq!(
1342            &fake_device_state.lock().eth_queue[0][..],
1343            &[
1344                6, 6, 6, 6, 6, 6, // dest
1345                4, 4, 4, 4, 4, 4, // src
1346                0x12, 0x34, // ether_type
1347                // Data
1348                1, 2, 3, 4, 5,
1349            ][..]
1350        );
1351    }
1352
1353    #[fuchsia::test(allow_stalls = false)]
1354    async fn handle_data_frame_bad_ds_bits() {
1355        let (fake_device, fake_device_state) = FakeDevice::new().await;
1356        let (mut ctx, _) = make_context(fake_device);
1357        let mut bss = make_infra_bss(&mut ctx).await;
1358
1359        assert_matches!(
1360            bss.handle_data_frame(
1361                &mut ctx,
1362                mac::DataFrame {
1363                    fixed_fields: mac::FixedDataHdrFields {
1364                        frame_ctrl: mac::FrameControl(0)
1365                            .with_frame_type(mac::FrameType::DATA)
1366                            .with_to_ds(false),
1367                        duration: 0,
1368                        addr1: (*BSSID).into(),
1369                        addr2: *CLIENT_ADDR,
1370                        addr3: *CLIENT_ADDR2,
1371                        seq_ctrl: mac::SequenceControl(10),
1372                    }
1373                    .as_bytes_ref(),
1374                    addr4: None,
1375                    qos_ctrl: None,
1376                    ht_ctrl: None,
1377                    body: &[
1378                        7, 7, 7, // DSAP, SSAP & control
1379                        8, 8, 8, // OUI
1380                        0x12, 0x34, // eth type
1381                        // Trailing bytes
1382                        1, 2, 3, 4, 5,
1383                    ][..],
1384                },
1385            )
1386            .expect_err("expected error"),
1387            Rejection::BadDsBits
1388        );
1389
1390        assert_eq!(fake_device_state.lock().eth_queue.len(), 0);
1391    }
1392
1393    #[fuchsia::test(allow_stalls = false)]
1394    async fn handle_client_event() {
1395        let (fake_device, fake_device_state) = FakeDevice::new().await;
1396        let (mut ctx, mut time_stream) = make_context(fake_device);
1397        let mut bss = make_infra_bss(&mut ctx).await;
1398
1399        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1400        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1401
1402        // Move the client to associated so it can handle data frames.
1403        client
1404            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1405            .await
1406            .expect("expected OK");
1407        client
1408            .handle_mlme_assoc_resp(
1409                &mut ctx,
1410                false,
1411                1,
1412                mac::CapabilityInfo(0),
1413                fidl_mlme::AssociateResultCode::Success,
1414                1,
1415                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1416            )
1417            .await
1418            .expect("expected OK");
1419
1420        fake_device_state.lock().wlan_queue.clear();
1421
1422        let _ = time_stream.try_next().unwrap().expect("Should have scheduled a timeout");
1423        bss.handle_timed_event(
1424            &mut ctx,
1425            TimedEvent::ClientEvent(*CLIENT_ADDR, ClientEvent::BssIdleTimeout),
1426        )
1427        .await
1428        .expect("expected OK");
1429
1430        // Check that we received a disassociation frame.
1431        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1432        #[rustfmt::skip]
1433        assert_eq!(&fake_device_state.lock().wlan_queue[0].0[..], &[
1434            // Mgmt header
1435            0b10100000, 0, // Frame Control
1436            0, 0, // Duration
1437            4, 4, 4, 4, 4, 4, // addr1
1438            2, 2, 2, 2, 2, 2, // addr2
1439            2, 2, 2, 2, 2, 2, // addr3
1440            0x30, 0, // Sequence Control
1441            // Disassoc header:
1442            4, 0, // reason code
1443        ][..]);
1444
1445        let msg = fake_device_state
1446            .lock()
1447            .next_mlme_msg::<fidl_mlme::DisassociateIndication>()
1448            .expect("expected MLME message");
1449        assert_eq!(
1450            msg,
1451            fidl_mlme::DisassociateIndication {
1452                peer_sta_address: CLIENT_ADDR.to_array(),
1453                reason_code: fidl_ieee80211::ReasonCode::ReasonInactivity,
1454                locally_initiated: true,
1455            },
1456        );
1457    }
1458
1459    #[fuchsia::test(allow_stalls = false)]
1460    async fn handle_data_frame_no_such_client() {
1461        let (fake_device, fake_device_state) = FakeDevice::new().await;
1462        let (mut ctx, _) = make_context(fake_device);
1463        let mut bss = make_infra_bss(&mut ctx).await;
1464
1465        assert_matches!(
1466            bss.handle_data_frame(
1467                &mut ctx,
1468                mac::DataFrame {
1469                    fixed_fields: mac::FixedDataHdrFields {
1470                        frame_ctrl: mac::FrameControl(0)
1471                            .with_frame_type(mac::FrameType::DATA)
1472                            .with_to_ds(true),
1473                        duration: 0,
1474                        addr1: (*BSSID).into(),
1475                        addr2: *CLIENT_ADDR,
1476                        addr3: *CLIENT_ADDR2,
1477                        seq_ctrl: mac::SequenceControl(10),
1478                    }
1479                    .as_bytes_ref(),
1480                    addr4: None,
1481                    qos_ctrl: None,
1482                    ht_ctrl: None,
1483                    body: &[
1484                        7, 7, 7, // DSAP, SSAP & control
1485                        8, 8, 8, // OUI
1486                        0x12, 0x34, // eth type
1487                        // Trailing bytes
1488                        1, 2, 3, 4, 5,
1489                    ][..],
1490                },
1491            )
1492            .expect_err("expected error"),
1493            Rejection::Client(_, ClientRejection::NotPermitted)
1494        );
1495
1496        assert_eq!(fake_device_state.lock().eth_queue.len(), 0);
1497
1498        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1499        assert_eq!(
1500            fake_device_state.lock().wlan_queue[0].0,
1501            &[
1502                // Mgmt header
1503                0b11000000, 0b00000000, // Frame Control
1504                0, 0, // Duration
1505                4, 4, 4, 4, 4, 4, // addr1
1506                2, 2, 2, 2, 2, 2, // addr2
1507                2, 2, 2, 2, 2, 2, // addr3
1508                0x10, 0, // Sequence Control
1509                // Disassoc header:
1510                7, 0, // reason code
1511            ][..]
1512        );
1513    }
1514
1515    #[fuchsia::test(allow_stalls = false)]
1516    async fn handle_data_frame_client_not_associated() {
1517        let (fake_device, fake_device_state) = FakeDevice::new().await;
1518        let (mut ctx, _) = make_context(fake_device);
1519        let mut bss = make_infra_bss(&mut ctx).await;
1520
1521        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1522        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1523
1524        // Move the client to authenticated, but not associated: data frames are still not
1525        // permitted.
1526        client
1527            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1528            .await
1529            .expect("expected OK");
1530
1531        fake_device_state.lock().wlan_queue.clear();
1532
1533        assert_matches!(
1534            bss.handle_data_frame(
1535                &mut ctx,
1536                mac::DataFrame {
1537                    fixed_fields: mac::FixedDataHdrFields {
1538                        frame_ctrl: mac::FrameControl(0)
1539                            .with_frame_type(mac::FrameType::DATA)
1540                            .with_to_ds(true),
1541                        duration: 0,
1542                        addr1: (*BSSID).into(),
1543                        addr2: *CLIENT_ADDR,
1544                        addr3: *CLIENT_ADDR2,
1545                        seq_ctrl: mac::SequenceControl(10),
1546                    }
1547                    .as_bytes_ref(),
1548                    addr4: None,
1549                    qos_ctrl: None,
1550                    ht_ctrl: None,
1551                    body: &[
1552                        7, 7, 7, // DSAP, SSAP & control
1553                        8, 8, 8, // OUI
1554                        0x12, 0x34, // eth type
1555                        // Trailing bytes
1556                        1, 2, 3, 4, 5,
1557                    ][..],
1558                },
1559            )
1560            .expect_err("expected error"),
1561            Rejection::Client(_, ClientRejection::NotPermitted)
1562        );
1563
1564        assert_eq!(fake_device_state.lock().eth_queue.len(), 0);
1565
1566        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1567        assert_eq!(
1568            fake_device_state.lock().wlan_queue[0].0,
1569            &[
1570                // Mgmt header
1571                0b10100000, 0b00000000, // Frame Control
1572                0, 0, // Duration
1573                4, 4, 4, 4, 4, 4, // addr1
1574                2, 2, 2, 2, 2, 2, // addr2
1575                2, 2, 2, 2, 2, 2, // addr3
1576                0x20, 0, // Sequence Control
1577                // Disassoc header:
1578                7, 0, // reason code
1579            ][..]
1580        );
1581    }
1582
1583    #[fuchsia::test(allow_stalls = false)]
1584    async fn handle_eth_frame_no_rsn() {
1585        let (fake_device, fake_device_state) = FakeDevice::new().await;
1586        let (mut ctx, _) = make_context(fake_device);
1587        let mut bss = make_infra_bss(&mut ctx).await;
1588        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1589
1590        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1591        client
1592            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1593            .await
1594            .expect("expected OK");
1595        client
1596            .handle_mlme_assoc_resp(
1597                &mut ctx,
1598                false,
1599                1,
1600                mac::CapabilityInfo(0),
1601                fidl_mlme::AssociateResultCode::Success,
1602                1,
1603                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1604            )
1605            .await
1606            .expect("expected OK");
1607        fake_device_state.lock().wlan_queue.clear();
1608
1609        bss.handle_eth_frame(
1610            &mut ctx,
1611            EthernetIIHdr {
1612                da: *CLIENT_ADDR,
1613                sa: *CLIENT_ADDR2,
1614                ether_type: BigEndianU16::new(0x1234),
1615            },
1616            &[1, 2, 3, 4, 5][..],
1617            0.into(),
1618        )
1619        .expect("expected OK");
1620
1621        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1622        assert_eq!(
1623            &fake_device_state.lock().wlan_queue[0].0[..],
1624            &[
1625                // Mgmt header
1626                0b00001000, 0b00000010, // Frame Control
1627                0, 0, // Duration
1628                4, 4, 4, 4, 4, 4, // addr1
1629                2, 2, 2, 2, 2, 2, // addr2
1630                6, 6, 6, 6, 6, 6, // addr3
1631                0x30, 0, // Sequence Control
1632                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1633                0, 0, 0, // OUI
1634                0x12, 0x34, // Protocol ID
1635                // Data
1636                1, 2, 3, 4, 5,
1637            ][..]
1638        );
1639    }
1640
1641    #[fuchsia::test(allow_stalls = false)]
1642    async fn handle_eth_frame_no_client() {
1643        let (fake_device, fake_device_state) = FakeDevice::new().await;
1644        let (mut ctx, _) = make_context(fake_device);
1645        let mut bss = make_infra_bss(&mut ctx).await;
1646
1647        assert_matches!(
1648            bss.handle_eth_frame(
1649                &mut ctx,
1650                EthernetIIHdr {
1651                    da: *CLIENT_ADDR,
1652                    sa: *CLIENT_ADDR2,
1653                    ether_type: BigEndianU16::new(0x1234)
1654                },
1655                &[1, 2, 3, 4, 5][..],
1656                0.into(),
1657            )
1658            .expect_err("expected error"),
1659            Rejection::Client(_, ClientRejection::NotAssociated)
1660        );
1661
1662        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
1663    }
1664
1665    #[fuchsia::test(allow_stalls = false)]
1666    async fn handle_eth_frame_is_rsn_eapol_controlled_port_closed() {
1667        let (fake_device, fake_device_state) = FakeDevice::new().await;
1668        let (mut ctx, _) = make_context(fake_device);
1669        let mut bss = make_protected_infra_bss(&mut ctx).await;
1670        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1671
1672        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1673        client
1674            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1675            .await
1676            .expect("expected OK");
1677        client
1678            .handle_mlme_assoc_resp(
1679                &mut ctx,
1680                true,
1681                1,
1682                mac::CapabilityInfo(0),
1683                fidl_mlme::AssociateResultCode::Success,
1684                1,
1685                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1686            )
1687            .await
1688            .expect("expected OK");
1689        fake_device_state.lock().wlan_queue.clear();
1690
1691        assert_matches!(
1692            bss.handle_eth_frame(
1693                &mut ctx,
1694                EthernetIIHdr {
1695                    da: *CLIENT_ADDR,
1696                    sa: *CLIENT_ADDR2,
1697                    ether_type: BigEndianU16::new(0x1234)
1698                },
1699                &[1, 2, 3, 4, 5][..],
1700                0.into(),
1701            )
1702            .expect_err("expected error"),
1703            Rejection::Client(_, ClientRejection::ControlledPortClosed)
1704        );
1705    }
1706
1707    #[fuchsia::test(allow_stalls = false)]
1708    async fn handle_eth_frame_is_rsn_eapol_controlled_port_open() {
1709        let (fake_device, fake_device_state) = FakeDevice::new().await;
1710        let (mut ctx, _) = make_context(fake_device);
1711        let mut bss = make_protected_infra_bss(&mut ctx).await;
1712        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1713
1714        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1715        client
1716            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1717            .await
1718            .expect("expected OK");
1719        client
1720            .handle_mlme_assoc_resp(
1721                &mut ctx,
1722                true,
1723                1,
1724                mac::CapabilityInfo(0),
1725                fidl_mlme::AssociateResultCode::Success,
1726                1,
1727                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1728            )
1729            .await
1730            .expect("expected OK");
1731        fake_device_state.lock().wlan_queue.clear();
1732
1733        client
1734            .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Open)
1735            .expect("expected OK");
1736
1737        bss.handle_eth_frame(
1738            &mut ctx,
1739            EthernetIIHdr {
1740                da: *CLIENT_ADDR,
1741                sa: *CLIENT_ADDR2,
1742                ether_type: BigEndianU16::new(0x1234),
1743            },
1744            &[1, 2, 3, 4, 5][..],
1745            0.into(),
1746        )
1747        .expect("expected OK");
1748
1749        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1750        assert_eq!(
1751            &fake_device_state.lock().wlan_queue[0].0[..],
1752            &[
1753                // Mgmt header
1754                0b00001000, 0b01000010, // Frame Control
1755                0, 0, // Duration
1756                4, 4, 4, 4, 4, 4, // addr1
1757                2, 2, 2, 2, 2, 2, // addr2
1758                6, 6, 6, 6, 6, 6, // addr3
1759                0x30, 0, // Sequence Control
1760                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
1761                0, 0, 0, // OUI
1762                0x12, 0x34, // Protocol ID
1763                // Data
1764                1, 2, 3, 4, 5,
1765            ][..]
1766        );
1767    }
1768
1769    #[test_case(false; "Controlled port closed")]
1770    #[test_case(true; "Controlled port open")]
1771    #[fuchsia::test(allow_stalls = false)]
1772    async fn handle_data_frame_is_rsn_eapol(controlled_port_open: bool) {
1773        let (fake_device, fake_device_state) = FakeDevice::new().await;
1774        let (mut ctx, _) = make_context(fake_device);
1775        let mut bss = make_protected_infra_bss(&mut ctx).await;
1776        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1777
1778        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1779        client
1780            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1781            .await
1782            .expect("expected OK");
1783        client
1784            .handle_mlme_assoc_resp(
1785                &mut ctx,
1786                true,
1787                1,
1788                mac::CapabilityInfo(0),
1789                fidl_mlme::AssociateResultCode::Success,
1790                1,
1791                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
1792            )
1793            .await
1794            .expect("expected OK");
1795        fake_device_state.lock().wlan_queue.clear();
1796
1797        if controlled_port_open {
1798            client
1799                .handle_mlme_set_controlled_port_req(fidl_mlme::ControlledPortState::Open)
1800                .expect("expected OK");
1801        }
1802
1803        bss.handle_data_frame(
1804            &mut ctx,
1805            mac::DataFrame {
1806                fixed_fields: mac::FixedDataHdrFields {
1807                    frame_ctrl: mac::FrameControl(0)
1808                        .with_frame_type(mac::FrameType::DATA)
1809                        .with_to_ds(true),
1810                    duration: 0,
1811                    addr1: (*BSSID).into(),
1812                    addr2: *CLIENT_ADDR,
1813                    addr3: *CLIENT_ADDR2,
1814                    seq_ctrl: mac::SequenceControl(10),
1815                }
1816                .as_bytes_ref(),
1817                addr4: None,
1818                qos_ctrl: None,
1819                ht_ctrl: None,
1820                body: &[
1821                    7, 7, 7, // DSAP, SSAP & control
1822                    8, 8, 8, // OUI
1823                    0x12, 0x34, // eth type
1824                    // Trailing bytes
1825                    1, 2, 3, 4, 5,
1826                ][..],
1827            },
1828        )
1829        .expect("expected OK");
1830
1831        if controlled_port_open {
1832            assert_eq!(fake_device_state.lock().eth_queue.len(), 1);
1833        } else {
1834            assert!(fake_device_state.lock().eth_queue.is_empty());
1835        }
1836    }
1837
1838    async fn authenticate_client(
1839        fake_device_state: Arc<Mutex<FakeDeviceState>>,
1840        ctx: &mut Context<FakeDevice>,
1841        bss: &mut InfraBss,
1842        client_addr: MacAddr,
1843    ) {
1844        bss.handle_mgmt_frame(
1845            ctx,
1846            mac::MgmtFrame {
1847                mgmt_hdr: mac::MgmtHdr {
1848                    frame_ctrl: mac::FrameControl(0)
1849                        .with_frame_type(mac::FrameType::MGMT)
1850                        .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
1851                    duration: 0,
1852                    addr1: (*BSSID).into(),
1853                    addr2: client_addr,
1854                    addr3: (*BSSID).into(),
1855                    seq_ctrl: mac::SequenceControl(10),
1856                }
1857                .as_bytes_ref(),
1858                ht_ctrl: None,
1859                body: &[
1860                    // Auth body
1861                    0, 0, // Auth Algorithm Number
1862                    1, 0, // Auth Txn Seq Number
1863                    0, 0, // Status code
1864                ][..],
1865            },
1866        )
1867        .await
1868        .expect("failed to handle auth req frame");
1869
1870        fake_device_state
1871            .lock()
1872            .next_mlme_msg::<fidl_mlme::AuthenticateIndication>()
1873            .expect("expected auth indication");
1874        bss.handle_mlme_auth_resp(
1875            ctx,
1876            fidl_mlme::AuthenticateResponse {
1877                peer_sta_address: client_addr.to_array(),
1878                result_code: fidl_mlme::AuthenticateResultCode::Success,
1879            },
1880        )
1881        .await
1882        .expect("failed to handle auth resp");
1883        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1884        fake_device_state.lock().wlan_queue.clear();
1885    }
1886
1887    async fn associate_client(
1888        fake_device_state: Arc<Mutex<FakeDeviceState>>,
1889        ctx: &mut Context<FakeDevice>,
1890        bss: &mut InfraBss,
1891        client_addr: MacAddr,
1892        association_id: u16,
1893    ) {
1894        bss.handle_mgmt_frame(
1895            ctx,
1896            mac::MgmtFrame {
1897                mgmt_hdr: mac::MgmtHdr {
1898                    frame_ctrl: mac::FrameControl(0)
1899                        .with_frame_type(mac::FrameType::MGMT)
1900                        .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_REQ),
1901                    duration: 0,
1902                    addr1: (*BSSID).into(),
1903                    addr2: client_addr,
1904                    addr3: (*BSSID).into(),
1905                    seq_ctrl: mac::SequenceControl(10),
1906                }
1907                .as_bytes_ref(),
1908                ht_ctrl: None,
1909                body: &[
1910                    // Assoc req body
1911                    0, 0, // Capability info
1912                    10, 0, // Listen interval
1913                    // IEs
1914                    1, 8, 1, 2, 3, 4, 5, 6, 7, 8, // Rates
1915                    50, 2, 9, 10, // Extended rates
1916                    48, 2, 77, 88, // RSNE
1917                ][..],
1918            },
1919        )
1920        .await
1921        .expect("expected OK");
1922        let msg = fake_device_state
1923            .lock()
1924            .next_mlme_msg::<fidl_mlme::AssociateIndication>()
1925            .expect("expected assoc indication");
1926        bss.handle_mlme_assoc_resp(
1927            ctx,
1928            fidl_mlme::AssociateResponse {
1929                peer_sta_address: client_addr.to_array(),
1930                result_code: fidl_mlme::AssociateResultCode::Success,
1931                association_id,
1932                capability_info: msg.capability_info,
1933                rates: msg.rates,
1934            },
1935        )
1936        .await
1937        .expect("failed to handle assoc resp");
1938        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
1939        fake_device_state.lock().wlan_queue.clear();
1940    }
1941
1942    fn send_eth_frame_from_ds_to_client(
1943        ctx: &mut Context<FakeDevice>,
1944        bss: &mut InfraBss,
1945        client_addr: MacAddr,
1946    ) {
1947        bss.handle_eth_frame(
1948            ctx,
1949            EthernetIIHdr {
1950                da: client_addr,
1951                sa: *REMOTE_ADDR,
1952                ether_type: BigEndianU16::new(0x1234),
1953            },
1954            &[1, 2, 3, 4, 5][..],
1955            0.into(),
1956        )
1957        .expect("expected OK");
1958    }
1959
1960    #[fuchsia::test(allow_stalls = false)]
1961    async fn handle_multiple_complete_associations() {
1962        let (fake_device, fake_device_state) = FakeDevice::new().await;
1963        let (mut ctx, _) = make_context(fake_device);
1964        let mut bss = make_infra_bss(&mut ctx).await;
1965
1966        authenticate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR).await;
1967        authenticate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR2).await;
1968
1969        associate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR, 1).await;
1970        associate_client(fake_device_state.clone(), &mut ctx, &mut bss, *CLIENT_ADDR2, 2).await;
1971
1972        assert!(bss.clients.contains_key(&CLIENT_ADDR));
1973        assert!(bss.clients.contains_key(&CLIENT_ADDR2));
1974
1975        send_eth_frame_from_ds_to_client(&mut ctx, &mut bss, *CLIENT_ADDR);
1976        send_eth_frame_from_ds_to_client(&mut ctx, &mut bss, *CLIENT_ADDR2);
1977
1978        assert_eq!(fake_device_state.lock().wlan_queue.len(), 2);
1979    }
1980
1981    #[fuchsia::test(allow_stalls = false)]
1982    async fn handle_ps_poll() {
1983        let (fake_device, fake_device_state) = FakeDevice::new().await;
1984        let (mut ctx, _) = make_context(fake_device);
1985        let mut bss = make_infra_bss(&mut ctx).await;
1986        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
1987
1988        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
1989        client
1990            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
1991            .await
1992            .expect("expected OK");
1993        client
1994            .handle_mlme_assoc_resp(
1995                &mut ctx,
1996                false,
1997                1,
1998                mac::CapabilityInfo(0),
1999                fidl_mlme::AssociateResultCode::Success,
2000                1,
2001                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
2002            )
2003            .await
2004            .expect("expected OK");
2005        client.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze ok");
2006        fake_device_state.lock().wlan_queue.clear();
2007
2008        bss.handle_eth_frame(
2009            &mut ctx,
2010            EthernetIIHdr {
2011                da: *CLIENT_ADDR,
2012                sa: *CLIENT_ADDR2,
2013                ether_type: BigEndianU16::new(0x1234),
2014            },
2015            &[1, 2, 3, 4, 5][..],
2016            0.into(),
2017        )
2018        .expect("expected OK");
2019        assert_eq!(fake_device_state.lock().wlan_queue.len(), 0);
2020
2021        bss.handle_ctrl_frame(
2022            &mut ctx,
2023            mac::CtrlFrame {
2024                frame_ctrl: mac::FrameControl(0)
2025                    .with_frame_type(mac::FrameType::CTRL)
2026                    .with_ctrl_subtype(mac::CtrlSubtype::PS_POLL),
2027                body: &[
2028                    0b00000001, 0b11000000, // Masked AID
2029                    2, 2, 2, 2, 2, 2, // RA
2030                    4, 4, 4, 4, 4, 4, // TA
2031                ][..],
2032            },
2033        )
2034        .expect("expected OK");
2035        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2036        assert_eq!(
2037            &fake_device_state.lock().wlan_queue[0].0[..],
2038            &[
2039                // Mgmt header
2040                0b00001000, 0b00000010, // Frame Control
2041                0, 0, // Duration
2042                4, 4, 4, 4, 4, 4, // addr1
2043                2, 2, 2, 2, 2, 2, // addr2
2044                6, 6, 6, 6, 6, 6, // addr3
2045                0x30, 0, // Sequence Control
2046                0xAA, 0xAA, 0x03, // DSAP, SSAP, Control, OUI
2047                0, 0, 0, // OUI
2048                0x12, 0x34, // Protocol ID
2049                // Data
2050                1, 2, 3, 4, 5,
2051            ][..]
2052        );
2053    }
2054
2055    #[fuchsia::test(allow_stalls = false)]
2056    async fn handle_mlme_setkeys_req() {
2057        let (fake_device, fake_device_state) = FakeDevice::new().await;
2058        let (mut ctx, _) = make_context(fake_device);
2059        let mut bss = make_protected_infra_bss(&mut ctx).await;
2060        bss.handle_mlme_setkeys_req(
2061            &mut ctx,
2062            vec![fidl_mlme::SetKeyDescriptor {
2063                cipher_suite_oui: [1, 2, 3],
2064                cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
2065                key_type: fidl_mlme::KeyType::Pairwise,
2066                address: [5; 6].into(),
2067                key_id: 6,
2068                key: vec![1, 2, 3, 4, 5, 6, 7],
2069                rsc: 8,
2070            }],
2071        )
2072        .await
2073        .expect("expected InfraBss::handle_mlme_setkeys_req OK");
2074        assert_eq!(
2075            fake_device_state.lock().keys,
2076            vec![fidl_softmac::WlanKeyConfiguration {
2077                protection: Some(fidl_softmac::WlanProtection::RxTx),
2078                cipher_oui: Some([1, 2, 3]),
2079                cipher_type: Some(4),
2080                key_type: Some(fidl_ieee80211::KeyType::Pairwise),
2081                peer_addr: Some([5; 6]),
2082                key_idx: Some(6),
2083                key: Some(vec![1, 2, 3, 4, 5, 6, 7]),
2084                rsc: Some(8),
2085                ..Default::default()
2086            }]
2087        );
2088    }
2089
2090    #[fuchsia::test(allow_stalls = false)]
2091    async fn handle_mlme_setkeys_req_no_rsne() {
2092        let (fake_device, fake_device_state) = FakeDevice::new().await;
2093        let (mut ctx, _) = make_context(fake_device);
2094        let mut bss = make_infra_bss(&mut ctx).await;
2095        assert_matches!(
2096            bss.handle_mlme_setkeys_req(
2097                &mut ctx,
2098                vec![fidl_mlme::SetKeyDescriptor {
2099                    cipher_suite_oui: [1, 2, 3],
2100                    cipher_suite_type:
2101                        fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
2102                    key_type: fidl_mlme::KeyType::Pairwise,
2103                    address: [5; 6],
2104                    key_id: 6,
2105                    key: vec![1, 2, 3, 4, 5, 6, 7],
2106                    rsc: 8,
2107                }]
2108            )
2109            .await
2110            .expect_err("expected InfraBss::handle_mlme_setkeys_req error"),
2111            Error::Status(_, zx::Status::BAD_STATE)
2112        );
2113        assert!(fake_device_state.lock().keys.is_empty());
2114    }
2115
2116    #[fuchsia::test(allow_stalls = false)]
2117    async fn handle_probe_req() {
2118        let (fake_device, fake_device_state) = FakeDevice::new().await;
2119        let (mut ctx, _) = make_context(fake_device);
2120        let mut bss = InfraBss::new(
2121            &mut ctx,
2122            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2123            TimeUnit::DEFAULT_BEACON_INTERVAL,
2124            2,
2125            mac::CapabilityInfo(33),
2126            vec![248],
2127            1,
2128            Some(vec![48, 2, 77, 88]),
2129        )
2130        .await
2131        .expect("expected InfraBss::new ok");
2132
2133        bss.handle_probe_req(&mut ctx, *CLIENT_ADDR)
2134            .expect("expected InfraBss::handle_probe_req ok");
2135        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2136        assert_eq!(
2137            &fake_device_state.lock().wlan_queue[0].0[..],
2138            &[
2139                // Mgmt header
2140                0b01010000, 0, // Frame Control
2141                0, 0, // Duration
2142                4, 4, 4, 4, 4, 4, // addr1
2143                2, 2, 2, 2, 2, 2, // addr2
2144                2, 2, 2, 2, 2, 2, // addr3
2145                0x10, 0, // Sequence Control
2146                // Beacon header:
2147                0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
2148                100, 0, // Beacon interval
2149                33, 0, // Capabilities
2150                // IEs:
2151                0, 5, 1, 2, 3, 4, 5, // SSID
2152                1, 1, 248, // Supported rates
2153                3, 1, 1, // DSSS parameter set
2154                48, 2, 77, 88, // RSNE
2155            ][..]
2156        );
2157    }
2158
2159    #[fuchsia::test(allow_stalls = false)]
2160    async fn handle_probe_req_has_offload() {
2161        let (fake_device, _fake_device_state) = FakeDevice::new_with_config(
2162            FakeDeviceConfig::default().with_mock_probe_response_offload(
2163                fidl_softmac::ProbeResponseOffloadExtension { supported: true },
2164            ),
2165        )
2166        .await;
2167
2168        let (mut ctx, _) = make_context(fake_device);
2169        let mut bss = InfraBss::new(
2170            &mut ctx,
2171            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2172            TimeUnit::DEFAULT_BEACON_INTERVAL,
2173            2,
2174            CapabilityInfo(33),
2175            vec![0b11111000],
2176            1,
2177            Some(vec![48, 2, 77, 88]),
2178        )
2179        .await
2180        .expect("expected InfraBss::new ok");
2181
2182        bss.handle_mgmt_frame(
2183            &mut ctx,
2184            mac::MgmtFrame {
2185                mgmt_hdr: mac::MgmtHdr {
2186                    frame_ctrl: mac::FrameControl(0)
2187                        .with_frame_type(mac::FrameType::MGMT)
2188                        .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2189                    duration: 0,
2190                    addr1: (*BSSID).into(),
2191                    addr2: *CLIENT_ADDR,
2192                    addr3: (*BSSID).into(),
2193                    seq_ctrl: mac::SequenceControl(10),
2194                }
2195                .as_bytes_ref(),
2196                ht_ctrl: None,
2197                body: &[][..],
2198            },
2199        )
2200        .await
2201        .expect_err("expected InfraBss::handle_mgmt_frame error");
2202    }
2203
2204    #[fuchsia::test(allow_stalls = false)]
2205    async fn handle_probe_req_wildcard_ssid() {
2206        let (fake_device, fake_device_state) = FakeDevice::new().await;
2207        let (mut ctx, _) = make_context(fake_device);
2208        let mut bss = InfraBss::new(
2209            &mut ctx,
2210            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2211            TimeUnit::DEFAULT_BEACON_INTERVAL,
2212            2,
2213            CapabilityInfo(33),
2214            vec![0b11111000],
2215            1,
2216            Some(vec![48, 2, 77, 88]),
2217        )
2218        .await
2219        .expect("expected InfraBss::new ok");
2220
2221        bss.handle_mgmt_frame(
2222            &mut ctx,
2223            mac::MgmtFrame {
2224                mgmt_hdr: mac::MgmtHdr {
2225                    frame_ctrl: mac::FrameControl(0)
2226                        .with_frame_type(mac::FrameType::MGMT)
2227                        .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2228                    duration: 0,
2229                    addr1: (*BSSID).into(),
2230                    addr2: *CLIENT_ADDR,
2231                    addr3: (*BSSID).into(),
2232                    seq_ctrl: mac::SequenceControl(10),
2233                }
2234                .as_bytes_ref(),
2235                ht_ctrl: None,
2236                body: &[
2237                    0, 0, // Wildcard SSID
2238                ][..],
2239            },
2240        )
2241        .await
2242        .expect("expected InfraBss::handle_mgmt_frame ok");
2243
2244        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2245        assert_eq!(
2246            &fake_device_state.lock().wlan_queue[0].0[..],
2247            &[
2248                // Mgmt header
2249                0b01010000, 0, // Frame Control
2250                0, 0, // Duration
2251                4, 4, 4, 4, 4, 4, // addr1
2252                2, 2, 2, 2, 2, 2, // addr2
2253                2, 2, 2, 2, 2, 2, // addr3
2254                0x10, 0, // Sequence Control
2255                // Beacon header:
2256                0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
2257                100, 0, // Beacon interval
2258                33, 0, // Capabilities
2259                // IEs:
2260                0, 5, 1, 2, 3, 4, 5, // SSID
2261                1, 1, 248, // Supported rates
2262                3, 1, 1, // DSSS parameter set
2263                48, 2, 77, 88, // RSNE
2264            ][..]
2265        );
2266    }
2267
2268    #[fuchsia::test(allow_stalls = false)]
2269    async fn handle_probe_req_matching_ssid() {
2270        let (fake_device, fake_device_state) = FakeDevice::new().await;
2271        let (mut ctx, _) = make_context(fake_device);
2272        let mut bss = InfraBss::new(
2273            &mut ctx,
2274            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2275            TimeUnit::DEFAULT_BEACON_INTERVAL,
2276            2,
2277            CapabilityInfo(33),
2278            vec![0b11111000],
2279            1,
2280            Some(vec![48, 2, 77, 88]),
2281        )
2282        .await
2283        .expect("expected InfraBss::new ok");
2284
2285        bss.handle_mgmt_frame(
2286            &mut ctx,
2287            mac::MgmtFrame {
2288                mgmt_hdr: mac::MgmtHdr {
2289                    frame_ctrl: mac::FrameControl(0)
2290                        .with_frame_type(mac::FrameType::MGMT)
2291                        .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2292                    duration: 0,
2293                    addr1: (*BSSID).into(),
2294                    addr2: *CLIENT_ADDR,
2295                    addr3: (*BSSID).into(),
2296                    seq_ctrl: mac::SequenceControl(10),
2297                }
2298                .as_bytes_ref(),
2299                ht_ctrl: None,
2300                body: &[0, 5, 1, 2, 3, 4, 5][..],
2301            },
2302        )
2303        .await
2304        .expect("expected InfraBss::handle_mgmt_frame ok");
2305
2306        assert_eq!(fake_device_state.lock().wlan_queue.len(), 1);
2307        assert_eq!(
2308            &fake_device_state.lock().wlan_queue[0].0[..],
2309            &[
2310                // Mgmt header
2311                0b01010000, 0, // Frame Control
2312                0, 0, // Duration
2313                4, 4, 4, 4, 4, 4, // addr1
2314                2, 2, 2, 2, 2, 2, // addr2
2315                2, 2, 2, 2, 2, 2, // addr3
2316                0x10, 0, // Sequence Control
2317                // Beacon header:
2318                0, 0, 0, 0, 0, 0, 0, 0, // Timestamp
2319                100, 0, // Beacon interval
2320                33, 0, // Capabilities
2321                // IEs:
2322                0, 5, 1, 2, 3, 4, 5, // SSID
2323                1, 1, 248, // Supported rates
2324                3, 1, 1, // DSSS parameter set
2325                48, 2, 77, 88, // RSNE
2326            ][..]
2327        );
2328    }
2329
2330    #[fuchsia::test(allow_stalls = false)]
2331    async fn handle_probe_req_mismatching_ssid() {
2332        let (fake_device, _) = FakeDevice::new().await;
2333        let (mut ctx, _) = make_context(fake_device);
2334        let mut bss = InfraBss::new(
2335            &mut ctx,
2336            Ssid::try_from([1, 2, 3, 4, 5]).unwrap(),
2337            TimeUnit::DEFAULT_BEACON_INTERVAL,
2338            2,
2339            CapabilityInfo(33),
2340            vec![0b11111000],
2341            1,
2342            Some(vec![48, 2, 77, 88]),
2343        )
2344        .await
2345        .expect("expected InfraBss::new ok");
2346
2347        assert_matches!(
2348            bss.handle_mgmt_frame(
2349                &mut ctx,
2350                mac::MgmtFrame {
2351                    mgmt_hdr: mac::MgmtHdr {
2352                        frame_ctrl: mac::FrameControl(0)
2353                            .with_frame_type(mac::FrameType::MGMT)
2354                            .with_mgmt_subtype(mac::MgmtSubtype::PROBE_REQ),
2355                        duration: 0,
2356                        addr1: (*BSSID).into(),
2357                        addr2: *CLIENT_ADDR,
2358                        addr3: (*BSSID).into(),
2359                        seq_ctrl: mac::SequenceControl(10),
2360                    }
2361                    .as_bytes_ref(),
2362                    ht_ctrl: None,
2363                    body: &[0, 5, 1, 2, 3, 4, 6][..],
2364                },
2365            )
2366            .await
2367            .expect_err("expected InfraBss::handle_mgmt_frame error"),
2368            Rejection::OtherBss
2369        );
2370    }
2371
2372    #[fuchsia::test(allow_stalls = false)]
2373    async fn make_tim() {
2374        let (fake_device, _) = FakeDevice::new().await;
2375        let (mut ctx, _) = make_context(fake_device);
2376        let mut bss = make_infra_bss(&mut ctx).await;
2377        bss.clients.insert(*CLIENT_ADDR, RemoteClient::new(*CLIENT_ADDR));
2378
2379        let client = bss.clients.get_mut(&CLIENT_ADDR).unwrap();
2380        client
2381            .handle_mlme_auth_resp(&mut ctx, fidl_mlme::AuthenticateResultCode::Success)
2382            .await
2383            .expect("expected OK");
2384        client
2385            .handle_mlme_assoc_resp(
2386                &mut ctx,
2387                false,
2388                1,
2389                mac::CapabilityInfo(0),
2390                fidl_mlme::AssociateResultCode::Success,
2391                1,
2392                &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..],
2393            )
2394            .await
2395            .expect("expected OK");
2396        client.set_power_state(&mut ctx, mac::PowerState::DOZE).expect("expected doze OK");
2397
2398        bss.handle_eth_frame(
2399            &mut ctx,
2400            EthernetIIHdr {
2401                da: *CLIENT_ADDR,
2402                sa: *CLIENT_ADDR2,
2403                ether_type: BigEndianU16::new(0x1234),
2404            },
2405            &[1, 2, 3, 4, 5][..],
2406            0.into(),
2407        )
2408        .expect("expected OK");
2409
2410        let tim = bss.make_tim();
2411        let (pvb_offset, pvb_bitmap) = tim.make_partial_virtual_bitmap();
2412        assert_eq!(pvb_offset, 0);
2413        assert_eq!(pvb_bitmap, &[0b00000010][..]);
2414    }
2415
2416    #[fuchsia::test(allow_stalls = false)]
2417    async fn make_tim_empty() {
2418        let (fake_device, _) = FakeDevice::new().await;
2419        let (mut ctx, _) = make_context(fake_device);
2420        let bss = make_infra_bss(&mut ctx).await;
2421
2422        let tim = bss.make_tim();
2423        let (pvb_offset, pvb_bitmap) = tim.make_partial_virtual_bitmap();
2424        assert_eq!(pvb_offset, 0);
2425        assert_eq!(pvb_bitmap, &[0b00000000][..]);
2426    }
2427}