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