wlan_sme/ap/
mod.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5mod aid;
6mod authenticator;
7mod event;
8mod remote_client;
9#[cfg(test)]
10pub mod test_utils;
11
12use event::*;
13use remote_client::*;
14
15use crate::responder::Responder;
16use crate::{mlme_event_name, MlmeRequest, MlmeSink};
17use fidl_fuchsia_wlan_mlme::{self as fidl_mlme, DeviceInfo, MlmeEvent};
18use futures::channel::{mpsc, oneshot};
19use ieee80211::{MacAddr, MacAddrBytes, Ssid};
20use log::{debug, error, info, warn};
21use std::collections::HashMap;
22use wlan_common::capabilities::get_device_band_cap;
23use wlan_common::channel::{Cbw, Channel};
24use wlan_common::ie::rsn::rsne::{RsnCapabilities, Rsne};
25use wlan_common::ie::{parse_ht_capabilities, ChanWidthSet, SupportedRate};
26use wlan_common::timer::{self, EventHandle, Timer};
27use wlan_common::{mac, RadioConfig};
28use wlan_rsn::psk;
29use {
30    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
31    fidl_fuchsia_wlan_internal as fidl_internal, fidl_fuchsia_wlan_sme as fidl_sme,
32};
33
34const DEFAULT_BEACON_PERIOD: u16 = 100;
35const DEFAULT_DTIM_PERIOD: u8 = 2;
36
37#[derive(Clone, Debug, PartialEq)]
38pub struct Config {
39    pub ssid: Ssid,
40    pub password: Vec<u8>,
41    pub radio_cfg: RadioConfig,
42}
43
44// OpRadioConfig keeps admitted configuration and operation state
45#[derive(Clone, Debug, PartialEq)]
46pub struct OpRadioConfig {
47    phy: fidl_common::WlanPhyType,
48    channel: Channel,
49}
50
51#[allow(clippy::large_enum_variant)] // TODO(https://fxbug.dev/401087337)
52enum State {
53    Idle {
54        ctx: Context,
55    },
56    Starting {
57        ctx: Context,
58        ssid: Ssid,
59        rsn_cfg: Option<RsnCfg>,
60        capabilities: mac::CapabilityInfo,
61        rates: Vec<SupportedRate>,
62        start_responder: Responder<StartResult>,
63        stop_responders: Vec<Responder<fidl_sme::StopApResultCode>>,
64        start_timeout: EventHandle,
65        op_radio_cfg: OpRadioConfig,
66    },
67    Stopping {
68        ctx: Context,
69        stop_req: fidl_mlme::StopRequest,
70        responders: Vec<Responder<fidl_sme::StopApResultCode>>,
71        stop_timeout: Option<EventHandle>,
72    },
73    Started {
74        bss: InfraBss,
75    },
76}
77
78#[derive(Clone)]
79pub struct RsnCfg {
80    psk: psk::Psk,
81    rsne: Rsne,
82}
83
84struct InfraBss {
85    ssid: Ssid,
86    rsn_cfg: Option<RsnCfg>,
87    capabilities: mac::CapabilityInfo,
88    rates: Vec<SupportedRate>,
89    clients: HashMap<MacAddr, RemoteClient>,
90    aid_map: aid::Map,
91    op_radio_cfg: OpRadioConfig,
92    ctx: Context,
93}
94
95pub struct Context {
96    device_info: DeviceInfo,
97    mac_sublayer_support: fidl_common::MacSublayerSupport,
98    mlme_sink: MlmeSink,
99    timer: Timer<Event>,
100}
101
102pub struct ApSme {
103    state: Option<State>,
104}
105
106#[derive(Debug, PartialEq)]
107pub enum StartResult {
108    Success,
109    Canceled,
110    TimedOut,
111    InvalidArguments(String),
112    PreviousStartInProgress,
113    AlreadyStarted,
114    InternalError,
115}
116
117impl ApSme {
118    pub fn new(
119        device_info: DeviceInfo,
120        mac_sublayer_support: fidl_common::MacSublayerSupport,
121    ) -> (Self, crate::MlmeSink, crate::MlmeStream, timer::EventStream<Event>) {
122        let (mlme_sink, mlme_stream) = mpsc::unbounded();
123        let (timer, time_stream) = timer::create_timer();
124        let sme = ApSme {
125            state: Some(State::Idle {
126                ctx: Context {
127                    device_info,
128                    mac_sublayer_support,
129                    mlme_sink: MlmeSink::new(mlme_sink.clone()),
130                    timer,
131                },
132            }),
133        };
134        (sme, MlmeSink::new(mlme_sink), mlme_stream, time_stream)
135    }
136
137    pub fn on_start_command(&mut self, config: Config) -> oneshot::Receiver<StartResult> {
138        let (responder, receiver) = Responder::new();
139        self.state = self.state.take().map(|state| match state {
140            State::Idle { mut ctx } => {
141                let band_cap =
142                    match get_device_band_cap(&ctx.device_info, config.radio_cfg.channel.primary) {
143                        None => {
144                            responder.respond(StartResult::InvalidArguments(format!(
145                                "Device has not band capabilities for channel {}",
146                                config.radio_cfg.channel.primary,
147                            )));
148                            return State::Idle { ctx };
149                        }
150                        Some(bc) => bc,
151                    };
152
153                let op_radio_cfg = match validate_radio_cfg(band_cap, &config.radio_cfg) {
154                    Err(result) => {
155                        responder.respond(result);
156                        return State::Idle { ctx };
157                    }
158                    Ok(op_radio_cfg) => op_radio_cfg,
159                };
160
161                let rsn_cfg_result = create_rsn_cfg(&config.ssid, &config.password[..]);
162                let rsn_cfg = match rsn_cfg_result {
163                    Err(e) => {
164                        responder.respond(e);
165                        return State::Idle { ctx };
166                    }
167                    Ok(rsn_cfg) => rsn_cfg,
168                };
169
170                let capabilities =
171                    mac::CapabilityInfo(ctx.device_info.softmac_hardware_capability as u16)
172                        // IEEE Std 802.11-2016, 9.4.1.4: An AP sets the ESS subfield to 1 and the IBSS
173                        // subfield to 0 within transmitted Beacon or Probe Response frames.
174                        .with_ess(true)
175                        .with_ibss(false)
176                        // IEEE Std 802.11-2016, 9.4.1.4: An AP sets the Privacy subfield to 1 within
177                        // transmitted Beacon, Probe Response, (Re)Association Response frames if data
178                        // confidentiality is required for all Data frames exchanged within the BSS.
179                        .with_privacy(rsn_cfg.is_some());
180
181                let req = match create_start_request(
182                    &op_radio_cfg,
183                    &config.ssid,
184                    rsn_cfg.as_ref(),
185                    capabilities,
186                    // The max length of fuchsia.wlan.mlme/BandCapability.basic_rates is
187                    // less than fuchsia.wlan.mlme/StartRequest.rates.
188                    &band_cap.basic_rates,
189                ) {
190                    Ok(req) => req,
191                    Err(result) => {
192                        responder.respond(result);
193                        return State::Idle { ctx };
194                    }
195                };
196
197                // TODO(https://fxbug.dev/42103581): Select which rates are mandatory here.
198                let rates = band_cap.basic_rates.iter().map(|r| SupportedRate(*r)).collect();
199
200                ctx.mlme_sink.send(MlmeRequest::Start(req));
201                let event = Event::Sme { event: SmeEvent::StartTimeout };
202                let start_timeout = ctx.timer.schedule(event);
203
204                State::Starting {
205                    ctx,
206                    ssid: config.ssid,
207                    rsn_cfg,
208                    capabilities,
209                    rates,
210                    start_responder: responder,
211                    stop_responders: vec![],
212                    start_timeout,
213                    op_radio_cfg,
214                }
215            }
216            s @ State::Starting { .. } => {
217                responder.respond(StartResult::PreviousStartInProgress);
218                s
219            }
220            s @ State::Stopping { .. } => {
221                responder.respond(StartResult::Canceled);
222                s
223            }
224            s @ State::Started { .. } => {
225                responder.respond(StartResult::AlreadyStarted);
226                s
227            }
228        });
229        receiver
230    }
231
232    pub fn on_stop_command(&mut self) -> oneshot::Receiver<fidl_sme::StopApResultCode> {
233        let (responder, receiver) = Responder::new();
234        self.state = self.state.take().map(|mut state| match state {
235            State::Idle { mut ctx } => {
236                // We don't have an SSID, so just do a best-effort StopAP request with no SSID
237                // filled in
238                let stop_req = fidl_mlme::StopRequest { ssid: Ssid::empty().into() };
239                let timeout = send_stop_req(&mut ctx, stop_req.clone());
240                State::Stopping {
241                    ctx,
242                    stop_req,
243                    responders: vec![responder],
244                    stop_timeout: Some(timeout),
245                }
246            }
247            State::Starting { ref mut stop_responders, .. } => {
248                stop_responders.push(responder);
249                state
250            }
251            State::Stopping { mut ctx, stop_req, mut responders, mut stop_timeout } => {
252                responders.push(responder);
253                // No stop request is ongoing, so forward this stop request.
254                // The previous stop request may have timed out or failed and we are in an
255                // unclean state where we don't know whether the AP has stopped or not.
256                stop_timeout =
257                    stop_timeout.or_else(|| Some(send_stop_req(&mut ctx, stop_req.clone())));
258                State::Stopping { ctx, stop_req, responders, stop_timeout }
259            }
260            State::Started { mut bss } => {
261                // IEEE Std 802.11-2016, 6.3.12.2.3: The SME should notify associated non-AP STAs of
262                // imminent infrastructure BSS termination before issuing the MLME-STOP.request
263                // primitive.
264                for client_addr in bss.clients.keys() {
265                    bss.ctx.mlme_sink.send(MlmeRequest::Deauthenticate(
266                        fidl_mlme::DeauthenticateRequest {
267                            peer_sta_address: client_addr.to_array(),
268                            // This seems to be the most appropriate reason code (IEEE Std
269                            // 802.11-2016, Table 9-45): Requesting STA is leaving the BSS (or
270                            // resetting). The spec doesn't seem to mandate a choice of reason code
271                            // here, so Fuchsia picks STA_LEAVING.
272                            reason_code: fidl_ieee80211::ReasonCode::StaLeaving,
273                        },
274                    ));
275                }
276
277                let stop_req = fidl_mlme::StopRequest { ssid: bss.ssid.to_vec() };
278                let timeout = send_stop_req(&mut bss.ctx, stop_req.clone());
279                State::Stopping {
280                    ctx: bss.ctx,
281                    stop_req,
282                    responders: vec![responder],
283                    stop_timeout: Some(timeout),
284                }
285            }
286        });
287        receiver
288    }
289
290    pub fn get_running_ap(&self) -> Option<fidl_sme::Ap> {
291        match self.state.as_ref() {
292            Some(State::Started { bss: InfraBss { ssid, op_radio_cfg, clients, .. }, .. }) => {
293                Some(fidl_sme::Ap {
294                    ssid: ssid.to_vec(),
295                    channel: op_radio_cfg.channel.primary,
296                    num_clients: clients.len() as u16,
297                })
298            }
299            _ => None,
300        }
301    }
302}
303
304fn send_stop_req(ctx: &mut Context, stop_req: fidl_mlme::StopRequest) -> EventHandle {
305    let event = Event::Sme { event: SmeEvent::StopTimeout };
306    let stop_timeout = ctx.timer.schedule(event);
307    ctx.mlme_sink.send(MlmeRequest::Stop(stop_req));
308    stop_timeout
309}
310
311impl super::Station for ApSme {
312    type Event = Event;
313
314    fn on_mlme_event(&mut self, event: MlmeEvent) {
315        debug!("received MLME event: {:?}", &event);
316        self.state = self.state.take().map(|state| match state {
317            State::Idle { .. } => {
318                warn!("received MlmeEvent while ApSme is idle {:?}", mlme_event_name(&event));
319                state
320            }
321            State::Starting {
322                ctx,
323                ssid,
324                rsn_cfg,
325                capabilities,
326                rates,
327                start_responder,
328                stop_responders,
329                start_timeout,
330                op_radio_cfg,
331            } => match event {
332                MlmeEvent::StartConf { resp } => handle_start_conf(
333                    resp,
334                    ctx,
335                    ssid,
336                    rsn_cfg,
337                    capabilities,
338                    rates,
339                    op_radio_cfg,
340                    start_responder,
341                    stop_responders,
342                ),
343                _ => {
344                    warn!(
345                        "received MlmeEvent while ApSme is starting {:?}",
346                        mlme_event_name(&event)
347                    );
348                    State::Starting {
349                        ctx,
350                        ssid,
351                        rsn_cfg,
352                        capabilities,
353                        rates,
354                        start_responder,
355                        stop_responders,
356                        start_timeout,
357                        op_radio_cfg,
358                    }
359                }
360            },
361            State::Stopping { ctx, stop_req, mut responders, stop_timeout } => match event {
362                MlmeEvent::StopConf { resp } => match resp.result_code {
363                    fidl_mlme::StopResultCode::Success
364                    | fidl_mlme::StopResultCode::BssAlreadyStopped => {
365                        for responder in responders.drain(..) {
366                            responder.respond(fidl_sme::StopApResultCode::Success);
367                        }
368                        State::Idle { ctx }
369                    }
370                    fidl_mlme::StopResultCode::InternalError => {
371                        for responder in responders.drain(..) {
372                            responder.respond(fidl_sme::StopApResultCode::InternalError);
373                        }
374                        State::Stopping { ctx, stop_req, responders, stop_timeout: None }
375                    }
376                },
377                _ => {
378                    warn!(
379                        "received MlmeEvent while ApSme is stopping {:?}",
380                        mlme_event_name(&event)
381                    );
382                    State::Stopping { ctx, stop_req, responders, stop_timeout }
383                }
384            },
385            State::Started { mut bss } => {
386                match event {
387                    MlmeEvent::OnChannelSwitched { info } => bss.handle_channel_switch(info),
388                    MlmeEvent::AuthenticateInd { ind } => bss.handle_auth_ind(ind),
389                    MlmeEvent::DeauthenticateInd { ind } => {
390                        bss.handle_deauth(&ind.peer_sta_address.into())
391                    }
392                    // TODO(https://fxbug.dev/42113580): This path should never be taken, as the MLME will never send
393                    // this. Make sure this is the case.
394                    MlmeEvent::DeauthenticateConf { resp } => {
395                        bss.handle_deauth(&resp.peer_sta_address.into())
396                    }
397                    MlmeEvent::AssociateInd { ind } => bss.handle_assoc_ind(ind),
398                    MlmeEvent::DisassociateInd { ind } => bss.handle_disassoc_ind(ind),
399                    MlmeEvent::EapolInd { ind } => bss.handle_eapol_ind(ind),
400                    MlmeEvent::EapolConf { resp } => bss.handle_eapol_conf(resp),
401                    _ => {
402                        warn!("unsupported MlmeEvent type {:?}; ignoring", mlme_event_name(&event))
403                    }
404                }
405                State::Started { bss }
406            }
407        });
408    }
409
410    fn on_timeout(&mut self, timed_event: timer::Event<Event>) {
411        self.state = self.state.take().map(|mut state| match state {
412            State::Idle { .. } => state,
413            State::Starting {
414                start_timeout,
415                mut ctx,
416                start_responder,
417                stop_responders,
418                capabilities,
419                rates,
420                ssid,
421                rsn_cfg,
422                op_radio_cfg,
423            } => match timed_event.event {
424                Event::Sme { event: SmeEvent::StartTimeout } => {
425                    warn!("Timed out waiting for MLME to start");
426                    start_responder.respond(StartResult::TimedOut);
427                    if stop_responders.is_empty() {
428                        State::Idle { ctx }
429                    } else {
430                        let stop_req = fidl_mlme::StopRequest { ssid: ssid.to_vec() };
431                        let timeout = send_stop_req(&mut ctx, stop_req.clone());
432                        State::Stopping {
433                            ctx,
434                            stop_req,
435                            responders: stop_responders,
436                            stop_timeout: Some(timeout),
437                        }
438                    }
439                }
440                _ => State::Starting {
441                    start_timeout,
442                    ctx,
443                    start_responder,
444                    stop_responders,
445                    capabilities,
446                    rates,
447                    ssid,
448                    rsn_cfg,
449                    op_radio_cfg,
450                },
451            },
452            State::Stopping { ctx, stop_req, mut responders, mut stop_timeout } => {
453                if let Event::Sme { event: SmeEvent::StopTimeout } = timed_event.event {
454                    for responder in responders.drain(..) {
455                        responder.respond(fidl_sme::StopApResultCode::TimedOut);
456                    }
457                    stop_timeout = None;
458                }
459                // If timeout triggered, then the responders and the timeout are cleared, and
460                // we are left in an unclean stopping state
461                State::Stopping { ctx, stop_req, responders, stop_timeout }
462            }
463            State::Started { ref mut bss } => {
464                bss.handle_timeout(timed_event);
465                state
466            }
467        });
468    }
469}
470
471/// Validate the channel, PHY type, bandwidth, and band capabilities, in that order.
472fn validate_radio_cfg(
473    band_cap: &fidl_mlme::BandCapability,
474    radio_cfg: &RadioConfig,
475) -> Result<OpRadioConfig, StartResult> {
476    let channel = radio_cfg.channel;
477    // TODO(https://fxbug.dev/42174927): We shouldn't expect to only start an AP in the US. The regulatory
478    // enforcement for the channel should apply at a lower layer.
479    if !channel.is_valid_in_us() {
480        return Err(StartResult::InvalidArguments(format!("Invalid US channel {}", channel)));
481    }
482    if channel.is_dfs() {
483        return Err(StartResult::InvalidArguments(format!(
484            "DFS channels not supported: {}",
485            channel
486        )));
487    }
488
489    let phy = radio_cfg.phy;
490    match phy {
491        fidl_common::WlanPhyType::Dsss
492        | fidl_common::WlanPhyType::Hr
493        | fidl_common::WlanPhyType::Ofdm
494        | fidl_common::WlanPhyType::Erp => match channel.cbw {
495            Cbw::Cbw20 => (),
496            _ => {
497                return Err(StartResult::InvalidArguments(format!(
498                    "PHY type {:?} not supported on channel {}",
499                    phy, channel
500                )))
501            }
502        },
503        fidl_common::WlanPhyType::Ht => {
504            match channel.cbw {
505                Cbw::Cbw20 | Cbw::Cbw40 | Cbw::Cbw40Below => (),
506                _ => {
507                    return Err(StartResult::InvalidArguments(format!(
508                        "HT-mode not supported for channel {}",
509                        channel
510                    )))
511                }
512            }
513
514            match band_cap.ht_cap.as_ref() {
515                None => {
516                    return Err(StartResult::InvalidArguments(format!(
517                        "No HT capabilities: {}",
518                        channel
519                    )))
520                }
521                Some(ht_cap) => {
522                    let ht_cap = parse_ht_capabilities(&ht_cap.bytes[..]).map_err(|e| {
523                        error!("failed to parse HT capability bytes: {:?}", e);
524                        StartResult::InternalError
525                    })?;
526                    let ht_cap_info = ht_cap.ht_cap_info;
527                    if ht_cap_info.chan_width_set() == ChanWidthSet::TWENTY_ONLY
528                        && channel.cbw != Cbw::Cbw20
529                    {
530                        return Err(StartResult::InvalidArguments(format!(
531                            "20 MHz band capabilities does not support channel {}",
532                            channel
533                        )));
534                    }
535                }
536            }
537        }
538        fidl_common::WlanPhyType::Vht => {
539            match channel.cbw {
540                Cbw::Cbw160 | Cbw::Cbw80P80 { .. } => {
541                    return Err(StartResult::InvalidArguments(format!(
542                        "Supported for channel {} in VHT mode not available",
543                        channel
544                    )))
545                }
546                _ => (),
547            }
548
549            if !channel.is_5ghz() {
550                return Err(StartResult::InvalidArguments(format!(
551                    "VHT only supported on 5 GHz channels: {}",
552                    channel
553                )));
554            }
555
556            if band_cap.vht_cap.is_none() {
557                return Err(StartResult::InvalidArguments(format!(
558                    "No VHT capabilities: {}",
559                    channel
560                )));
561            }
562        }
563        fidl_common::WlanPhyType::Dmg
564        | fidl_common::WlanPhyType::Tvht
565        | fidl_common::WlanPhyType::S1G
566        | fidl_common::WlanPhyType::Cdmg
567        | fidl_common::WlanPhyType::Cmmg
568        | fidl_common::WlanPhyType::He => {
569            return Err(StartResult::InvalidArguments(format!("Unsupported PHY type: {:?}", phy)))
570        }
571        fidl_common::WlanPhyTypeUnknown!() => {
572            return Err(StartResult::InvalidArguments(format!("Unknown PHY type: {:?}", phy)))
573        }
574    }
575
576    Ok(OpRadioConfig { phy, channel })
577}
578
579#[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
580fn handle_start_conf(
581    conf: fidl_mlme::StartConfirm,
582    mut ctx: Context,
583    ssid: Ssid,
584    rsn_cfg: Option<RsnCfg>,
585    capabilities: mac::CapabilityInfo,
586    rates: Vec<SupportedRate>,
587    op_radio_cfg: OpRadioConfig,
588    start_responder: Responder<StartResult>,
589    stop_responders: Vec<Responder<fidl_sme::StopApResultCode>>,
590) -> State {
591    if stop_responders.is_empty() {
592        match conf.result_code {
593            fidl_mlme::StartResultCode::Success => {
594                start_responder.respond(StartResult::Success);
595                State::Started {
596                    bss: InfraBss {
597                        ssid,
598                        rsn_cfg,
599                        clients: HashMap::new(),
600                        aid_map: aid::Map::default(),
601                        capabilities,
602                        rates,
603                        op_radio_cfg,
604                        ctx,
605                    },
606                }
607            }
608            result_code => {
609                error!("failed to start BSS: {:?}", result_code);
610                start_responder.respond(StartResult::InternalError);
611                State::Idle { ctx }
612            }
613        }
614    } else {
615        start_responder.respond(StartResult::Canceled);
616        let stop_req = fidl_mlme::StopRequest { ssid: ssid.to_vec() };
617        let timeout = send_stop_req(&mut ctx, stop_req.clone());
618        State::Stopping { ctx, stop_req, responders: stop_responders, stop_timeout: Some(timeout) }
619    }
620}
621
622impl InfraBss {
623    /// Removes a client from the map.
624    ///
625    /// A client may only be removed via |remove_client| if:
626    ///
627    /// - MLME-DEAUTHENTICATE.request has been issued for the client, or,
628    /// - MLME-DEAUTHENTICATE.indication or MLME-DEAUTHENTICATE.confirm has been received for the
629    ///   client, or,
630    /// - MLME-AUTHENTICATE.indication is being handled (see comment in |handle_auth_ind| for
631    ///   details).
632    ///
633    /// If the client has an AID, its AID will be released from the AID map.
634    ///
635    /// Returns true if a client was removed, otherwise false.
636    fn remove_client(&mut self, addr: &MacAddr) -> bool {
637        if let Some(client) = self.clients.remove(addr) {
638            if let Some(aid) = client.aid() {
639                self.aid_map.release_aid(aid);
640            }
641            true
642        } else {
643            false
644        }
645    }
646
647    fn handle_channel_switch(&mut self, info: fidl_internal::ChannelSwitchInfo) {
648        info!("Channel switch for AP {:?}", info);
649        self.op_radio_cfg.channel.primary = info.new_channel;
650    }
651
652    fn handle_auth_ind(&mut self, ind: fidl_mlme::AuthenticateIndication) {
653        let peer_addr: MacAddr = ind.peer_sta_address.into();
654        if self.remove_client(&peer_addr) {
655            // This may occur if an already authenticated client on the SME receives a fresh
656            // MLME-AUTHENTICATE.indication from the MLME.
657            //
658            // This is safe, as we will make a fresh the client state and return an appropriate
659            // MLME-AUTHENTICATE.response to the MLME, indicating whether it should deauthenticate
660            // the client or not.
661            warn!(
662                "client {} is trying to reauthenticate; removing client and starting again",
663                peer_addr
664            );
665        }
666        let mut client = RemoteClient::new(peer_addr);
667        client.handle_auth_ind(&mut self.ctx, ind.auth_type);
668        if !client.authenticated() {
669            info!("client {} was not authenticated", peer_addr);
670            return;
671        }
672
673        info!("client {} authenticated", peer_addr);
674        let _ = self.clients.insert(peer_addr, client);
675    }
676
677    fn handle_deauth(&mut self, peer_addr: &MacAddr) {
678        if !self.remove_client(peer_addr) {
679            warn!("client {} never authenticated, ignoring deauthentication request", peer_addr);
680            return;
681        }
682
683        info!("client {} deauthenticated", peer_addr);
684    }
685
686    fn handle_assoc_ind(&mut self, ind: fidl_mlme::AssociateIndication) {
687        let peer_addr: MacAddr = ind.peer_sta_address.into();
688
689        let client = match self.clients.get_mut(&peer_addr) {
690            None => {
691                warn!("client {} never authenticated, ignoring association indication", peer_addr);
692                return;
693            }
694            Some(client) => client,
695        };
696
697        client.handle_assoc_ind(
698            &mut self.ctx,
699            &mut self.aid_map,
700            self.capabilities,
701            ind.capability_info,
702            &self.rates,
703            &ind.rates.into_iter().map(SupportedRate).collect::<Vec<_>>()[..],
704            &self.rsn_cfg,
705            ind.rsne,
706        );
707        if !client.authenticated() {
708            warn!("client {} failed to associate and was deauthenticated", peer_addr);
709            let _ = self.remove_client(&peer_addr);
710        } else if !client.associated() {
711            warn!("client {} failed to associate but did not deauthenticate", peer_addr);
712        } else {
713            info!("client {} associated", peer_addr);
714        }
715    }
716
717    fn handle_disassoc_ind(&mut self, ind: fidl_mlme::DisassociateIndication) {
718        let peer_addr: MacAddr = ind.peer_sta_address.into();
719
720        let client = match self.clients.get_mut(&peer_addr) {
721            None => {
722                warn!(
723                    "client {} never authenticated, ignoring disassociation indication",
724                    peer_addr
725                );
726                return;
727            }
728            Some(client) => client,
729        };
730
731        client.handle_disassoc_ind(&mut self.ctx, &mut self.aid_map);
732        if client.associated() {
733            panic!("client {} didn't disassociate? this should never happen!", peer_addr)
734        } else {
735            info!("client {} disassociated", peer_addr);
736        }
737    }
738
739    fn handle_timeout(&mut self, timed_event: timer::Event<Event>) {
740        match timed_event.event {
741            Event::Sme { .. } => (),
742            Event::Client { addr, event } => {
743                let client = match self.clients.get_mut(&addr) {
744                    None => {
745                        return;
746                    }
747                    Some(client) => client,
748                };
749
750                client.handle_timeout(&mut self.ctx, event);
751                if !client.authenticated() {
752                    if !self.remove_client(&addr) {
753                        error!("failed to remove client {} from AID map", addr);
754                    }
755                    info!("client {} lost authentication", addr);
756                }
757            }
758        }
759    }
760
761    fn handle_eapol_ind(&mut self, ind: fidl_mlme::EapolIndication) {
762        let peer_addr: MacAddr = ind.src_addr.into();
763        let client = match self.clients.get_mut(&peer_addr) {
764            None => {
765                warn!("client {} never authenticated, ignoring EAPoL indication", peer_addr);
766                return;
767            }
768            Some(client) => client,
769        };
770
771        client.handle_eapol_ind(&mut self.ctx, &ind.data[..]);
772    }
773
774    fn handle_eapol_conf(&mut self, resp: fidl_mlme::EapolConfirm) {
775        let dst_addr: MacAddr = resp.dst_addr.into();
776        let client = match self.clients.get_mut(&dst_addr) {
777            None => {
778                warn!("never sent EAPOL frame to client {}, ignoring confirm", dst_addr);
779                return;
780            }
781            Some(client) => client,
782        };
783
784        client.handle_eapol_conf(&mut self.ctx, resp.result_code);
785    }
786}
787
788fn create_rsn_cfg(ssid: &Ssid, password: &[u8]) -> Result<Option<RsnCfg>, StartResult> {
789    if password.is_empty() {
790        Ok(None)
791    } else {
792        let psk_result = psk::compute(password, ssid);
793        let psk = match psk_result {
794            Err(e) => {
795                return Err(StartResult::InvalidArguments(e.to_string()));
796            }
797            Ok(o) => o,
798        };
799
800        // Note: TKIP is legacy and considered insecure. Only allow CCMP usage
801        // for group and pairwise ciphers.
802        Ok(Some(RsnCfg { psk, rsne: Rsne::wpa2_rsne_with_caps(RsnCapabilities(0)) }))
803    }
804}
805
806fn create_start_request(
807    op_radio_cfg: &OpRadioConfig,
808    ssid: &Ssid,
809    ap_rsn: Option<&RsnCfg>,
810    capabilities: mac::CapabilityInfo,
811    basic_rates: &[u8],
812) -> Result<fidl_mlme::StartRequest, StartResult> {
813    let rsne_bytes = ap_rsn.as_ref().map(|RsnCfg { rsne, .. }| {
814        let mut buf = Vec::with_capacity(rsne.len());
815        if let Err(e) = rsne.write_into(&mut buf) {
816            error!("error writing RSNE into MLME-START.request: {}", e);
817        }
818        buf
819    });
820
821    let (channel_bandwidth, _secondary80) = op_radio_cfg.channel.cbw.to_fidl();
822
823    if basic_rates.len() > fidl_internal::MAX_ASSOC_BASIC_RATES as usize {
824        error!(
825            "Too many basic rates ({}). Max is {}.",
826            basic_rates.len(),
827            fidl_internal::MAX_ASSOC_BASIC_RATES
828        );
829        return Err(StartResult::InternalError);
830    }
831
832    Ok(fidl_mlme::StartRequest {
833        ssid: ssid.to_vec(),
834        bss_type: fidl_common::BssType::Infrastructure,
835        beacon_period: DEFAULT_BEACON_PERIOD,
836        dtim_period: DEFAULT_DTIM_PERIOD,
837        channel: op_radio_cfg.channel.primary,
838        capability_info: capabilities.raw(),
839        rates: basic_rates.to_vec(),
840        country: fidl_mlme::Country {
841            // TODO(https://fxbug.dev/42104247): Get config from wlancfg
842            alpha2: [b'U', b'S'],
843            suffix: fidl_mlme::COUNTRY_ENVIRON_ALL,
844        },
845        rsne: rsne_bytes,
846        mesh_id: vec![],
847        phy: op_radio_cfg.phy,
848        channel_bandwidth,
849    })
850}
851
852#[cfg(test)]
853mod tests {
854    use super::*;
855    use crate::test_utils::*;
856    use crate::{MlmeStream, Station};
857    use fidl_fuchsia_wlan_mlme as fidl_mlme;
858    use lazy_static::lazy_static;
859    use test_case::test_case;
860    use wlan_common::assert_variant;
861    use wlan_common::channel::Cbw;
862    use wlan_common::mac::Aid;
863    use wlan_common::test_utils::fake_capabilities::{
864        fake_2ghz_band_capability_vht, fake_5ghz_band_capability, fake_5ghz_band_capability_ht_cbw,
865    };
866    use wlan_common::test_utils::fake_features::fake_mac_sublayer_support;
867
868    lazy_static! {
869        static ref AP_ADDR: MacAddr = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66].into();
870        static ref CLIENT_ADDR: MacAddr = [0x7A, 0xE7, 0x76, 0xD9, 0xF2, 0x67].into();
871        static ref CLIENT_ADDR2: MacAddr = [0x22, 0x22, 0x22, 0x22, 0x22, 0x22].into();
872        static ref SSID: Ssid = Ssid::try_from([0x46, 0x55, 0x43, 0x48, 0x53, 0x49, 0x41]).unwrap();
873    }
874
875    const RSNE: &[u8] = &[
876        0x30, // element id
877        0x2A, // length
878        0x01, 0x00, // version
879        0x00, 0x0f, 0xac, 0x04, // group data cipher suite -- CCMP-128
880        0x01, 0x00, // pairwise cipher suite count
881        0x00, 0x0f, 0xac, 0x04, // pairwise cipher suite list -- CCMP-128
882        0x01, 0x00, // akm suite count
883        0x00, 0x0f, 0xac, 0x02, // akm suite list -- PSK
884        0xa8, 0x04, // rsn capabilities
885        0x01, 0x00, // pmk id count
886        // pmk id list
887        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
888        0x11, 0x00, 0x0f, 0xac, 0x04, // group management cipher suite -- CCMP-128
889    ];
890
891    fn radio_cfg(primary_channel: u8) -> RadioConfig {
892        RadioConfig::new(fidl_common::WlanPhyType::Ht, Cbw::Cbw20, primary_channel)
893    }
894
895    fn unprotected_config() -> Config {
896        Config { ssid: SSID.clone(), password: vec![], radio_cfg: radio_cfg(11) }
897    }
898
899    fn protected_config() -> Config {
900        Config {
901            ssid: SSID.clone(),
902            password: vec![0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68],
903            radio_cfg: radio_cfg(11),
904        }
905    }
906
907    fn create_channel_switch_ind(channel: u8) -> MlmeEvent {
908        MlmeEvent::OnChannelSwitched {
909            info: fidl_internal::ChannelSwitchInfo { new_channel: channel },
910        }
911    }
912
913    #[test_case(false, None, fidl_common::WlanPhyType::Ht, 15, Cbw::Cbw20; "invalid US channel")]
914    #[test_case(false, None, fidl_common::WlanPhyType::Ht, 52, Cbw::Cbw20; "DFS channel")]
915    #[test_case(false, None, fidl_common::WlanPhyType::Dmg, 1, Cbw::Cbw20; "DMG not supported")]
916    #[test_case(false, None, fidl_common::WlanPhyType::Tvht, 1, Cbw::Cbw20; "TVHT not supported")]
917    #[test_case(false, None, fidl_common::WlanPhyType::S1G, 1, Cbw::Cbw20; "S1G not supported")]
918    #[test_case(false, None, fidl_common::WlanPhyType::Cdmg, 1, Cbw::Cbw20; "CDMG not supported")]
919    #[test_case(false, None, fidl_common::WlanPhyType::Cmmg, 1, Cbw::Cbw20; "CMMG not supported")]
920    #[test_case(false, None, fidl_common::WlanPhyType::He, 1, Cbw::Cbw20; "HE not supported")]
921    #[test_case(false, None, fidl_common::WlanPhyType::Ht, 36, Cbw::Cbw80; "invalid HT width")]
922    #[test_case(false, None, fidl_common::WlanPhyType::Erp, 1, Cbw::Cbw40; "non-HT greater than 20 MHz")]
923    #[test_case(false, None, fidl_common::WlanPhyType::Ht, 36, Cbw::Cbw80; "HT greater than 40 MHz")]
924    #[test_case(false, None, fidl_common::WlanPhyType::unknown(), 36, Cbw::Cbw40; "Unknown PHY type")]
925    #[test_case(false, Some(fake_5ghz_band_capability_ht_cbw(ChanWidthSet::TWENTY_ONLY)),
926                fidl_common::WlanPhyType::Ht, 44, Cbw::Cbw40; "HT 20 MHz only")]
927    #[test_case(false, Some(fidl_mlme::BandCapability {
928                    ht_cap: None, ..fake_5ghz_band_capability()
929                }),
930                fidl_common::WlanPhyType::Ht, 48, Cbw::Cbw40; "No HT capabilities")]
931    #[test_case(false, None, fidl_common::WlanPhyType::Vht, 36, Cbw::Cbw160; "160 MHz not supported")]
932    #[test_case(false, None, fidl_common::WlanPhyType::Vht, 36, Cbw::Cbw80P80 { secondary80: 106 }; "80+80 MHz not supported")]
933    #[test_case(false, None, fidl_common::WlanPhyType::Vht, 1, Cbw::Cbw20; "VHT 2.4 GHz not supported")]
934    #[test_case(false, Some(fidl_mlme::BandCapability {
935                    vht_cap: None,
936                    ..fake_5ghz_band_capability()
937                }),
938                fidl_common::WlanPhyType::Vht, 149, Cbw::Cbw40; "no VHT capabilities")]
939    #[test_case(true, None, fidl_common::WlanPhyType::Hr, 1, Cbw::Cbw20)]
940    #[test_case(true, None, fidl_common::WlanPhyType::Erp, 1, Cbw::Cbw20)]
941    #[test_case(true, None, fidl_common::WlanPhyType::Ht, 1, Cbw::Cbw20)]
942    #[test_case(true, None, fidl_common::WlanPhyType::Ht, 1, Cbw::Cbw40)]
943    #[test_case(true, None, fidl_common::WlanPhyType::Ht, 11, Cbw::Cbw40Below)]
944    #[test_case(true, None, fidl_common::WlanPhyType::Ht, 36, Cbw::Cbw20)]
945    #[test_case(true, None, fidl_common::WlanPhyType::Ht, 36, Cbw::Cbw40)]
946    #[test_case(true, None, fidl_common::WlanPhyType::Ht, 40, Cbw::Cbw40Below)]
947    #[test_case(true, None, fidl_common::WlanPhyType::Vht, 36, Cbw::Cbw20)]
948    #[test_case(true, None, fidl_common::WlanPhyType::Vht, 36, Cbw::Cbw40)]
949    #[test_case(true, None, fidl_common::WlanPhyType::Vht, 40, Cbw::Cbw40Below)]
950    #[test_case(true, None, fidl_common::WlanPhyType::Vht, 36, Cbw::Cbw80)]
951    fn test_validate_radio_cfg(
952        valid: bool,
953        band_cap: Option<fidl_mlme::BandCapability>,
954        phy: fidl_common::WlanPhyType,
955        primary: u8,
956        cbw: Cbw,
957    ) {
958        let channel = Channel::new(primary, cbw);
959        #[allow(
960            clippy::redundant_field_names,
961            reason = "mass allow for https://fxbug.dev/381896734"
962        )]
963        let radio_cfg = RadioConfig { phy: phy, channel: channel };
964        #[allow(
965            clippy::redundant_field_names,
966            reason = "mass allow for https://fxbug.dev/381896734"
967        )]
968        let expected_op_radio_cfg = OpRadioConfig { phy: phy, channel: channel };
969        let band_cap = match band_cap {
970            Some(band_cap) => band_cap,
971            None => fake_2ghz_band_capability_vht(),
972        };
973
974        match validate_radio_cfg(&band_cap, &radio_cfg) {
975            Ok(op_radio_cfg) => {
976                if valid {
977                    assert_eq!(op_radio_cfg, expected_op_radio_cfg);
978                } else {
979                    panic!("Unexpected successful validation");
980                }
981            }
982            Err(StartResult::InvalidArguments { .. }) => {
983                if valid {
984                    panic!("Unexpected failure to validate.");
985                }
986            }
987            Err(other) => {
988                panic!("Unexpected StartResult value: {:?}", other);
989            }
990        }
991    }
992
993    #[fuchsia::test(allow_stalls = false)]
994    async fn authenticate_while_sme_is_idle() {
995        let (mut sme, mut mlme_stream, _) = create_sme().await;
996        let client = Client::default();
997        sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
998
999        assert_variant!(mlme_stream.try_next(), Err(e) => {
1000            assert_eq!(e.to_string(), "receiver channel is empty");
1001        });
1002    }
1003
1004    // Check status when sme is idle
1005    #[fuchsia::test(allow_stalls = false)]
1006    async fn status_when_sme_is_idle() {
1007        let (sme, _, _) = create_sme().await;
1008        assert_eq!(None, sme.get_running_ap());
1009    }
1010
1011    #[fuchsia::test(allow_stalls = false)]
1012    async fn ap_starts_success() {
1013        let (mut sme, mut mlme_stream, _) = create_sme().await;
1014        let mut receiver = sme.on_start_command(unprotected_config());
1015
1016        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(start_req))) => {
1017            assert_eq!(start_req.ssid, SSID.to_vec());
1018            assert_eq!(
1019                start_req.capability_info,
1020                mac::CapabilityInfo(0).with_short_preamble(true).with_ess(true).raw(),
1021            );
1022            assert_eq!(start_req.bss_type, fidl_common::BssType::Infrastructure);
1023            assert_ne!(start_req.beacon_period, 0);
1024            assert_eq!(start_req.dtim_period, DEFAULT_DTIM_PERIOD);
1025            assert_eq!(
1026                start_req.channel,
1027                unprotected_config().radio_cfg.channel.primary,
1028            );
1029            assert!(start_req.rsne.is_none());
1030        });
1031
1032        assert_eq!(Ok(None), receiver.try_recv());
1033        sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1034        assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1035    }
1036
1037    // Check status when Ap starting and started
1038    #[fuchsia::test(allow_stalls = false)]
1039    async fn ap_starts_success_get_running_ap() {
1040        let (mut sme, mut mlme_stream, _) = create_sme().await;
1041        let mut receiver = sme.on_start_command(unprotected_config());
1042        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_start_req))) => {});
1043        // status should be Starting
1044        assert_eq!(None, sme.get_running_ap());
1045        assert_eq!(Ok(None), receiver.try_recv());
1046        sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1047        assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1048        assert_eq!(
1049            Some(fidl_sme::Ap {
1050                ssid: SSID.to_vec(),
1051                channel: unprotected_config().radio_cfg.channel.primary,
1052                num_clients: 0,
1053            }),
1054            sme.get_running_ap()
1055        );
1056    }
1057
1058    // Check status after channel change
1059    #[fuchsia::test(allow_stalls = false)]
1060    async fn ap_check_status_after_channel_change() {
1061        let (mut sme, _, _) = start_unprotected_ap().await;
1062        // Check status
1063        assert_eq!(
1064            Some(fidl_sme::Ap {
1065                ssid: SSID.to_vec(),
1066                channel: unprotected_config().radio_cfg.channel.primary,
1067                num_clients: 0,
1068            }),
1069            sme.get_running_ap()
1070        );
1071        sme.on_mlme_event(create_channel_switch_ind(6));
1072        // Check status
1073        assert_eq!(
1074            Some(fidl_sme::Ap { ssid: SSID.to_vec(), channel: 6, num_clients: 0 }),
1075            sme.get_running_ap()
1076        );
1077    }
1078
1079    #[fuchsia::test(allow_stalls = false)]
1080    async fn ap_starts_timeout() {
1081        let (mut sme, _, mut time_stream) = create_sme().await;
1082        let mut receiver = sme.on_start_command(unprotected_config());
1083
1084        let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1085        sme.on_timeout(event);
1086
1087        assert_eq!(Ok(Some(StartResult::TimedOut)), receiver.try_recv());
1088        // Check status
1089        assert_eq!(None, sme.get_running_ap());
1090    }
1091
1092    // Disable logging to prevent failure from emitted error logs.
1093    #[fuchsia::test(allow_stalls = false, logging = false)]
1094    async fn ap_starts_fails() {
1095        let (mut sme, _, _) = create_sme().await;
1096        let mut receiver = sme.on_start_command(unprotected_config());
1097
1098        sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::NotSupported));
1099        assert_eq!(Ok(Some(StartResult::InternalError)), receiver.try_recv());
1100        // Check status
1101        assert_eq!(None, sme.get_running_ap());
1102    }
1103
1104    #[fuchsia::test(allow_stalls = false)]
1105    async fn start_req_while_ap_is_starting() {
1106        let (mut sme, _, _) = create_sme().await;
1107        let mut receiver_one = sme.on_start_command(unprotected_config());
1108
1109        // While SME is starting, any start request receives an error immediately
1110        let mut receiver_two = sme.on_start_command(unprotected_config());
1111        assert_eq!(Ok(Some(StartResult::PreviousStartInProgress)), receiver_two.try_recv());
1112
1113        // Start confirmation for first request should still have an affect
1114        sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1115        assert_eq!(Ok(Some(StartResult::Success)), receiver_one.try_recv());
1116    }
1117
1118    #[fuchsia::test(allow_stalls = false)]
1119    async fn start_req_while_ap_is_stopping() {
1120        let (mut sme, _, _) = start_unprotected_ap().await;
1121        let mut stop_receiver = sme.on_stop_command();
1122        let mut start_receiver = sme.on_start_command(unprotected_config());
1123        assert_eq!(Ok(None), stop_receiver.try_recv());
1124        assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1125    }
1126
1127    #[fuchsia::test(allow_stalls = false)]
1128    async fn ap_stops_while_idle() {
1129        let (mut sme, mut mlme_stream, _) = create_sme().await;
1130        let mut receiver = sme.on_stop_command();
1131        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1132            assert!(stop_req.ssid.is_empty());
1133        });
1134
1135        // Respond with a successful stop result code
1136        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1137        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1138    }
1139
1140    #[fuchsia::test(allow_stalls = false)]
1141    async fn stop_req_while_ap_is_starting_then_succeeds() {
1142        let (mut sme, mut mlme_stream, _) = create_sme().await;
1143        let mut start_receiver = sme.on_start_command(unprotected_config());
1144        let mut stop_receiver = sme.on_stop_command();
1145        assert_eq!(Ok(None), start_receiver.try_recv());
1146        assert_eq!(Ok(None), stop_receiver.try_recv());
1147
1148        // Verify start request is sent to MLME but not stop request yet
1149        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_))));
1150        assert_variant!(mlme_stream.try_next(), Err(e) => {
1151            assert_eq!(e.to_string(), "receiver channel is empty");
1152        });
1153
1154        // Once start confirmation is finished, then stop request is sent out
1155        sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1156        assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1157        assert_eq!(Ok(None), stop_receiver.try_recv());
1158        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1159            assert_eq!(stop_req.ssid, SSID.to_vec());
1160        });
1161
1162        // Respond with a successful stop result code
1163        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1164        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver.try_recv());
1165    }
1166
1167    #[fuchsia::test(allow_stalls = false)]
1168    async fn stop_req_while_ap_is_starting_then_times_out() {
1169        let (mut sme, mut mlme_stream, mut time_stream) = create_sme().await;
1170        let mut start_receiver = sme.on_start_command(unprotected_config());
1171        let mut stop_receiver = sme.on_stop_command();
1172        assert_eq!(Ok(None), start_receiver.try_recv());
1173        assert_eq!(Ok(None), stop_receiver.try_recv());
1174
1175        // Verify start request is sent to MLME but not stop request yet
1176        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(_))));
1177        assert_variant!(mlme_stream.try_next(), Err(e) => {
1178            assert_eq!(e.to_string(), "receiver channel is empty");
1179        });
1180
1181        // Time out the start request. Then stop request is sent out
1182        let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1183        sme.on_timeout(event);
1184        assert_eq!(Ok(Some(StartResult::TimedOut)), start_receiver.try_recv());
1185        assert_eq!(Ok(None), stop_receiver.try_recv());
1186        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1187            assert_eq!(stop_req.ssid, SSID.to_vec());
1188        });
1189
1190        // Respond with a successful stop result code
1191        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1192        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver.try_recv());
1193    }
1194
1195    #[fuchsia::test(allow_stalls = false)]
1196    async fn ap_stops_after_started() {
1197        let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1198        let mut receiver = sme.on_stop_command();
1199
1200        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1201            assert_eq!(stop_req.ssid, SSID.to_vec());
1202        });
1203        assert_eq!(Ok(None), receiver.try_recv());
1204        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::BssAlreadyStopped));
1205        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1206    }
1207
1208    #[fuchsia::test(allow_stalls = false)]
1209    async fn ap_stops_after_started_and_deauths_all_clients() {
1210        let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1211        let client = Client::default();
1212        sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1213        client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1214
1215        // Check status
1216        assert_eq!(
1217            Some(fidl_sme::Ap {
1218                ssid: SSID.to_vec(),
1219                channel: unprotected_config().radio_cfg.channel.primary,
1220                num_clients: 1,
1221            }),
1222            sme.get_running_ap()
1223        );
1224        let mut receiver = sme.on_stop_command();
1225        assert_variant!(
1226        mlme_stream.try_next(),
1227        Ok(Some(MlmeRequest::Deauthenticate(deauth_req))) => {
1228            assert_eq!(&deauth_req.peer_sta_address, client.addr.as_array());
1229            assert_eq!(deauth_req.reason_code, fidl_ieee80211::ReasonCode::StaLeaving);
1230        });
1231
1232        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1233            assert_eq!(stop_req.ssid, SSID.to_vec());
1234        });
1235        assert_eq!(Ok(None), receiver.try_recv());
1236        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1237        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver.try_recv());
1238
1239        // Check status
1240        assert_eq!(None, sme.get_running_ap());
1241    }
1242
1243    #[fuchsia::test(allow_stalls = false)]
1244    async fn ap_queues_concurrent_stop_requests() {
1245        let (mut sme, _, _) = start_unprotected_ap().await;
1246        let mut receiver1 = sme.on_stop_command();
1247        let mut receiver2 = sme.on_stop_command();
1248
1249        assert_eq!(Ok(None), receiver1.try_recv());
1250        assert_eq!(Ok(None), receiver2.try_recv());
1251
1252        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1253        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver1.try_recv());
1254        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), receiver2.try_recv());
1255    }
1256
1257    #[fuchsia::test(allow_stalls = false)]
1258    async fn uncleaned_stopping_state() {
1259        let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1260        let mut stop_receiver1 = sme.on_stop_command();
1261        // Clear out the stop request
1262        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1263            assert_eq!(stop_req.ssid, SSID.to_vec());
1264        });
1265
1266        assert_eq!(Ok(None), stop_receiver1.try_recv());
1267        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::InternalError));
1268        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::InternalError)), stop_receiver1.try_recv());
1269
1270        // While in unclean stopping state, no start request can be made
1271        let mut start_receiver = sme.on_start_command(unprotected_config());
1272        assert_eq!(Ok(Some(StartResult::Canceled)), start_receiver.try_recv());
1273        assert_variant!(mlme_stream.try_next(), Err(e) => {
1274            assert_eq!(e.to_string(), "receiver channel is empty");
1275        });
1276
1277        // SME will forward another stop request to lower layer
1278        let mut stop_receiver2 = sme.on_stop_command();
1279        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Stop(stop_req))) => {
1280            assert_eq!(stop_req.ssid, SSID.to_vec());
1281        });
1282
1283        // Respond successful this time
1284        assert_eq!(Ok(None), stop_receiver2.try_recv());
1285        sme.on_mlme_event(create_stop_conf(fidl_mlme::StopResultCode::Success));
1286        assert_eq!(Ok(Some(fidl_sme::StopApResultCode::Success)), stop_receiver2.try_recv());
1287    }
1288
1289    #[fuchsia::test(allow_stalls = false)]
1290    async fn client_authenticates_supported_authentication_type() {
1291        let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1292        let client = Client::default();
1293        sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1294        client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1295    }
1296
1297    // Disable logging to prevent failure from emitted error logs.
1298    #[fuchsia::test(allow_stalls = false, logging = false)]
1299    async fn client_authenticates_unsupported_authentication_type() {
1300        let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1301        let client = Client::default();
1302        let auth_ind = client.create_auth_ind(fidl_mlme::AuthenticationTypes::FastBssTransition);
1303        sme.on_mlme_event(auth_ind);
1304        client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Refused);
1305    }
1306
1307    #[fuchsia::test(allow_stalls = false)]
1308    async fn client_associates_unprotected_network() {
1309        let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1310        let client = Client::default();
1311        sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1312        client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1313
1314        sme.on_mlme_event(client.create_assoc_ind(None));
1315        client.verify_assoc_resp(
1316            &mut mlme_stream,
1317            1,
1318            fidl_mlme::AssociateResultCode::Success,
1319            false,
1320        );
1321    }
1322
1323    #[fuchsia::test(allow_stalls = false)]
1324    async fn client_associates_valid_rsne() {
1325        let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1326        let client = Client::default();
1327        client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1328
1329        sme.on_mlme_event(client.create_assoc_ind(Some(RSNE.to_vec())));
1330        client.verify_assoc_resp(
1331            &mut mlme_stream,
1332            1,
1333            fidl_mlme::AssociateResultCode::Success,
1334            true,
1335        );
1336        client.verify_eapol_req(&mut mlme_stream);
1337    }
1338
1339    // Disable logging to prevent failure from emitted error logs.
1340    #[fuchsia::test(allow_stalls = false, logging = false)]
1341    async fn client_associates_invalid_rsne() {
1342        let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1343        let client = Client::default();
1344        client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1345
1346        sme.on_mlme_event(client.create_assoc_ind(None));
1347        client.verify_refused_assoc_resp(
1348            &mut mlme_stream,
1349            fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch,
1350        );
1351    }
1352
1353    #[fuchsia::test(allow_stalls = false)]
1354    async fn rsn_handshake_timeout() {
1355        let (mut sme, mut mlme_stream, mut time_stream) = start_protected_ap().await;
1356        let client = Client::default();
1357        client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1358
1359        // Drain the association timeout message.
1360        assert_variant!(time_stream.try_next(), Ok(Some(_)));
1361
1362        sme.on_mlme_event(client.create_assoc_ind(Some(RSNE.to_vec())));
1363        client.verify_assoc_resp(
1364            &mut mlme_stream,
1365            1,
1366            fidl_mlme::AssociateResultCode::Success,
1367            true,
1368        );
1369
1370        // Drain the RSNA negotiation timeout message.
1371        assert_variant!(time_stream.try_next(), Ok(Some(_)));
1372
1373        for _i in 0..4 {
1374            client.verify_eapol_req(&mut mlme_stream);
1375            let (_, event, _) = time_stream.try_next().unwrap().expect("expect timer message");
1376            sme.on_timeout(event);
1377        }
1378
1379        client.verify_deauth_req(
1380            &mut mlme_stream,
1381            fidl_ieee80211::ReasonCode::FourwayHandshakeTimeout,
1382        );
1383    }
1384
1385    #[fuchsia::test(allow_stalls = false)]
1386    async fn client_restarts_authentication_flow() {
1387        let (mut sme, mut mlme_stream, _) = start_unprotected_ap().await;
1388        let client = Client::default();
1389        client.authenticate_and_drain_mlme(&mut sme, &mut mlme_stream);
1390        client.associate_and_drain_mlme(&mut sme, &mut mlme_stream, None);
1391
1392        sme.on_mlme_event(client.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1393        client.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1394
1395        sme.on_mlme_event(client.create_assoc_ind(None));
1396        client.verify_assoc_resp(
1397            &mut mlme_stream,
1398            1,
1399            fidl_mlme::AssociateResultCode::Success,
1400            false,
1401        );
1402    }
1403
1404    #[fuchsia::test(allow_stalls = false)]
1405    async fn multiple_clients_associate() {
1406        let (mut sme, mut mlme_stream, _) = start_protected_ap().await;
1407        let client1 = Client::default();
1408        let client2 = Client { addr: *CLIENT_ADDR2 };
1409
1410        sme.on_mlme_event(client1.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1411        client1.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1412
1413        sme.on_mlme_event(client2.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1414        client2.verify_auth_resp(&mut mlme_stream, fidl_mlme::AuthenticateResultCode::Success);
1415
1416        sme.on_mlme_event(client1.create_assoc_ind(Some(RSNE.to_vec())));
1417        client1.verify_assoc_resp(
1418            &mut mlme_stream,
1419            1,
1420            fidl_mlme::AssociateResultCode::Success,
1421            true,
1422        );
1423        client1.verify_eapol_req(&mut mlme_stream);
1424
1425        sme.on_mlme_event(client2.create_assoc_ind(Some(RSNE.to_vec())));
1426        client2.verify_assoc_resp(
1427            &mut mlme_stream,
1428            2,
1429            fidl_mlme::AssociateResultCode::Success,
1430            true,
1431        );
1432        client2.verify_eapol_req(&mut mlme_stream);
1433    }
1434
1435    fn create_start_conf(result_code: fidl_mlme::StartResultCode) -> MlmeEvent {
1436        MlmeEvent::StartConf { resp: fidl_mlme::StartConfirm { result_code } }
1437    }
1438
1439    fn create_stop_conf(result_code: fidl_mlme::StopResultCode) -> MlmeEvent {
1440        MlmeEvent::StopConf { resp: fidl_mlme::StopConfirm { result_code } }
1441    }
1442
1443    struct Client {
1444        addr: MacAddr,
1445    }
1446
1447    impl Client {
1448        fn default() -> Self {
1449            Client { addr: *CLIENT_ADDR }
1450        }
1451
1452        fn authenticate_and_drain_mlme(
1453            &self,
1454            sme: &mut ApSme,
1455            mlme_stream: &mut crate::MlmeStream,
1456        ) {
1457            sme.on_mlme_event(self.create_auth_ind(fidl_mlme::AuthenticationTypes::OpenSystem));
1458            assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::AuthResponse(..))));
1459        }
1460
1461        fn associate_and_drain_mlme(
1462            &self,
1463            sme: &mut ApSme,
1464            mlme_stream: &mut crate::MlmeStream,
1465            rsne: Option<Vec<u8>>,
1466        ) {
1467            sme.on_mlme_event(self.create_assoc_ind(rsne));
1468            assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::AssocResponse(..))));
1469        }
1470
1471        fn create_auth_ind(&self, auth_type: fidl_mlme::AuthenticationTypes) -> MlmeEvent {
1472            MlmeEvent::AuthenticateInd {
1473                ind: fidl_mlme::AuthenticateIndication {
1474                    peer_sta_address: self.addr.to_array(),
1475                    auth_type,
1476                },
1477            }
1478        }
1479
1480        fn create_assoc_ind(&self, rsne: Option<Vec<u8>>) -> MlmeEvent {
1481            MlmeEvent::AssociateInd {
1482                ind: fidl_mlme::AssociateIndication {
1483                    peer_sta_address: self.addr.to_array(),
1484                    listen_interval: 100,
1485                    ssid: Some(SSID.to_vec()),
1486                    rsne,
1487                    capability_info: mac::CapabilityInfo(0).with_short_preamble(true).raw(),
1488                    rates: vec![
1489                        0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
1490                    ],
1491                },
1492            }
1493        }
1494
1495        fn verify_auth_resp(
1496            &self,
1497            mlme_stream: &mut MlmeStream,
1498            result_code: fidl_mlme::AuthenticateResultCode,
1499        ) {
1500            let msg = mlme_stream.try_next();
1501            assert_variant!(msg, Ok(Some(MlmeRequest::AuthResponse(auth_resp))) => {
1502                assert_eq!(&auth_resp.peer_sta_address, self.addr.as_array());
1503                assert_eq!(auth_resp.result_code, result_code);
1504            });
1505        }
1506
1507        fn verify_assoc_resp(
1508            &self,
1509            mlme_stream: &mut MlmeStream,
1510            aid: Aid,
1511            result_code: fidl_mlme::AssociateResultCode,
1512            privacy: bool,
1513        ) {
1514            let msg = mlme_stream.try_next();
1515            assert_variant!(msg, Ok(Some(MlmeRequest::AssocResponse(assoc_resp))) => {
1516                assert_eq!(&assoc_resp.peer_sta_address, self.addr.as_array());
1517                assert_eq!(assoc_resp.association_id, aid);
1518                assert_eq!(assoc_resp.result_code, result_code);
1519                assert_eq!(
1520                    assoc_resp.capability_info,
1521                    mac::CapabilityInfo(0).with_short_preamble(true).with_privacy(privacy).raw(),
1522                );
1523            });
1524        }
1525
1526        fn verify_refused_assoc_resp(
1527            &self,
1528            mlme_stream: &mut MlmeStream,
1529            result_code: fidl_mlme::AssociateResultCode,
1530        ) {
1531            let msg = mlme_stream.try_next();
1532            assert_variant!(msg, Ok(Some(MlmeRequest::AssocResponse(assoc_resp))) => {
1533                assert_eq!(&assoc_resp.peer_sta_address, self.addr.as_array());
1534                assert_eq!(assoc_resp.association_id, 0);
1535                assert_eq!(assoc_resp.result_code, result_code);
1536                assert_eq!(assoc_resp.capability_info, 0);
1537            });
1538        }
1539
1540        fn verify_eapol_req(&self, mlme_stream: &mut MlmeStream) {
1541            assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Eapol(eapol_req))) => {
1542                assert_eq!(&eapol_req.src_addr, AP_ADDR.as_array());
1543                assert_eq!(&eapol_req.dst_addr, self.addr.as_array());
1544                assert!(!eapol_req.data.is_empty());
1545            });
1546        }
1547
1548        fn verify_deauth_req(
1549            &self,
1550            mlme_stream: &mut MlmeStream,
1551            reason_code: fidl_ieee80211::ReasonCode,
1552        ) {
1553            let msg = mlme_stream.try_next();
1554            assert_variant!(msg, Ok(Some(MlmeRequest::Deauthenticate(deauth_req))) => {
1555                assert_eq!(&deauth_req.peer_sta_address, self.addr.as_array());
1556                assert_eq!(deauth_req.reason_code, reason_code);
1557            });
1558        }
1559    }
1560
1561    // TODO(https://fxbug.dev/327499461): This function is async to ensure SME functions will
1562    // run in an async context and not call `wlan_common::timer::Timer::now` without an
1563    // executor.
1564    async fn start_protected_ap() -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1565        start_ap(true).await
1566    }
1567
1568    // TODO(https://fxbug.dev/327499461): This function is async to ensure SME functions will
1569    // run in an async context and not call `wlan_common::timer::Timer::now` without an
1570    // executor.
1571    async fn start_unprotected_ap() -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1572        start_ap(false).await
1573    }
1574
1575    // TODO(https://fxbug.dev/327499461): This function is async to ensure SME functions will
1576    // run in an async context and not call `wlan_common::timer::Timer::now` without an
1577    // executor.
1578    async fn start_ap(protected: bool) -> (ApSme, crate::MlmeStream, timer::EventStream<Event>) {
1579        let (mut sme, mut mlme_stream, mut time_stream) = create_sme().await;
1580        let config = if protected { protected_config() } else { unprotected_config() };
1581        let mut receiver = sme.on_start_command(config);
1582        assert_eq!(Ok(None), receiver.try_recv());
1583        assert_variant!(mlme_stream.try_next(), Ok(Some(MlmeRequest::Start(..))));
1584        // drain time stream
1585        while time_stream.try_next().is_ok() {}
1586        sme.on_mlme_event(create_start_conf(fidl_mlme::StartResultCode::Success));
1587
1588        assert_eq!(Ok(Some(StartResult::Success)), receiver.try_recv());
1589        (sme, mlme_stream, time_stream)
1590    }
1591
1592    // TODO(https://fxbug.dev/327499461): This function is async to ensure SME functions will
1593    // run in an async context and not call `wlan_common::timer::Timer::now` without an
1594    // executor.
1595    async fn create_sme() -> (ApSme, MlmeStream, timer::EventStream<Event>) {
1596        let (ap_sme, _mlme_sink, mlme_stream, time_stream) =
1597            ApSme::new(fake_device_info(*AP_ADDR), fake_mac_sublayer_support());
1598        (ap_sme, mlme_stream, time_stream)
1599    }
1600}