1use anyhow::{bail, Context};
48use ieee80211::{Bssid, MacAddrBytes, Ssid};
49use log::{debug, info};
50use wlan_common::bss::Protection;
51use wlan_common::channel::Channel;
52use wlan_common::mac;
53use wlan_rsn::rsna::UpdateSink;
54use wlan_rsn::{auth, Authenticator};
55use {
56 fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme,
57 fidl_fuchsia_wlan_tap as fidl_tap,
58};
59
60use crate::event::buffered::{AssocReqFrame, AuthFrame, Buffered, DataFrame, ProbeReqFrame};
61use crate::event::{self, branch, Handler, Stateful};
62use crate::{ApAdvertisement, ProbeResponse, CLIENT_MAC_ADDR};
63
64pub type ActionResult = Result<(), anyhow::Error>;
68
69#[derive(Debug)]
76pub struct AuthenticationTap<'a, H> {
77 pub control: &'a mut AuthenticationControl,
78 pub handler: H,
86}
87
88impl<'a, H> AuthenticationTap<'a, H> {
89 fn call<'e>(&mut self, event: &AuthenticationEvent<'e>) -> ActionResult
90 where
91 H: Handler<AuthenticationControl, AuthenticationEvent<'e>, Output = ActionResult>,
92 {
93 self.handler
94 .by_ref()
95 .context("failed to handle authentication event")
96 .call(self.control, event)
97 .matched()
98 .unwrap_or(Ok(()))
99 }
100}
101
102#[derive(Debug)]
109pub struct AuthenticationEvent<'a> {
110 pub phy: &'a fidl_tap::WlantapPhyProxy,
111 pub bssid: &'a Bssid,
112 pub channel: &'a Channel,
113 pub is_ready_for_sae: bool,
114 pub is_ready_for_eapol: bool,
115}
116
117#[derive(Debug)]
119pub struct AuthenticationControl {
120 pub authenticator: Authenticator,
121 pub updates: UpdateSink,
122}
123
124pub fn send_advertisements<'h, S, E, I>(
125 phy: &'h fidl_tap::WlantapPhyProxy,
126 advertisements: I,
127) -> impl Handler<S, E, Output = ActionResult> + 'h
128where
129 S: 'h,
130 E: 'h,
131 I: IntoIterator,
132 I::Item: ApAdvertisement + 'h,
133{
134 let advertisements: Vec<_> = advertisements.into_iter().collect();
135 event::matched(move |_: &mut S, _: &E| {
136 for advertisement in advertisements.iter() {
137 advertisement.send(phy).context("failed to send AP advertisement")?;
138 }
139 Ok(())
140 })
141}
142
143pub fn send_scan_completion<'h, S>(
144 phy: &'h fidl_tap::WlantapPhyProxy,
145 status: i32,
146) -> impl Handler<S, fidl_tap::StartScanArgs, Output = ActionResult> + 'h
147where
148 S: 'h,
149{
150 use zx::MonotonicDuration;
151
152 const SCAN_COMPLETION_DELAY: MonotonicDuration = MonotonicDuration::from_seconds(2i64);
153
154 event::matched(move |_: &mut S, event: &fidl_tap::StartScanArgs| {
155 log::info!(
156 "TODO(https://fxbug.dev/42060050): Sleeping for {} second(s) before sending scan completion.",
157 SCAN_COMPLETION_DELAY.into_seconds()
158 );
159 SCAN_COMPLETION_DELAY.sleep();
160 phy.scan_complete(event.scan_id, status).context("failed to send scan completion")
161 })
162}
163
164pub fn send_advertisements_and_scan_completion<'h, S, I>(
165 phy: &'h fidl_tap::WlantapPhyProxy,
166 advertisements: I,
167) -> impl Handler<S, fidl_tap::StartScanArgs, Output = ActionResult> + 'h
168where
169 S: 'h,
170 I: IntoIterator,
171 I::Item: ApAdvertisement + 'h,
172{
173 event::matched(|_, event: &fidl_tap::StartScanArgs| {
174 debug!(
175 "Sending AP advertisements and scan completion for scan event with ID: {:?}",
176 event.scan_id,
177 );
178 })
179 .and(send_advertisements(phy, advertisements))
180 .try_and(send_scan_completion(phy, 0))
181}
182
183pub fn send_probe_response<'h, S>(
184 phy: &'h fidl_tap::WlantapPhyProxy,
185 ssid: &'h Ssid,
186 bssid: &'h Bssid,
187 channel: &'h Channel,
188 protection: &'h Protection,
189) -> impl Handler<S, fidl_tap::TxArgs, Output = ActionResult> + 'h
190where
191 S: 'h,
192{
193 event::extract(|_: Buffered<ProbeReqFrame>| {
194 ProbeResponse {
197 channel: *channel,
198 bssid: *bssid,
199 ssid: ssid.clone(),
200 protection: *protection,
201 rssi_dbm: -10,
202 wsc_ie: None,
203 }
204 .send(phy)
205 .context("failed to send probe response frame")
206 })
207}
208
209pub fn send_packet<'h, S>(
210 phy: &'h fidl_tap::WlantapPhyProxy,
211 rx_info: fidl_tap::WlanRxInfo,
212) -> impl Handler<S, fidl_tap::TxArgs, Output = ActionResult> + 'h
213where
214 S: 'h,
215{
216 event::matched(move |_: &mut S, event: &fidl_tap::TxArgs| {
217 phy.rx(&event.packet.data, &rx_info).context("failed to send packet")
218 })
219}
220
221pub fn send_open_authentication<'h, S>(
222 phy: &'h fidl_tap::WlantapPhyProxy,
223 bssid: &'h Bssid,
224 channel: &'h Channel,
225 status: impl Into<mac::StatusCode>,
226) -> impl Handler<S, fidl_tap::TxArgs, Output = ActionResult> + 'h
227where
228 S: 'h,
229{
230 let status = status.into();
231 event::extract(move |_: Buffered<AuthFrame>| {
232 crate::send_open_authentication(channel, bssid, status, phy)
233 .context("failed to send authentication frame")
234 })
235}
236
237pub fn authenticate_with_control_state<'h>(
239) -> impl Handler<AuthenticationControl, AuthenticationEvent<'h>, Output = ActionResult> + 'h {
240 event::matched(|control: &mut AuthenticationControl, event: &AuthenticationEvent<'_>| {
241 use wlan_rsn::rsna::SecAssocUpdate::{TxEapolKeyFrame, TxSaeFrame};
242
243 let mut index = 0;
244 while index < control.updates.len() {
245 match &control.updates[index] {
246 TxSaeFrame(ref frame) => {
247 if event.is_ready_for_sae {
248 crate::send_sae_authentication_frame(
249 &frame,
250 &event.channel,
251 &event.bssid,
252 event.phy,
253 )
254 .context("failed to send SAE authentication frame")?;
255 control.updates.remove(index);
256 continue; } else {
258 debug!("authentication: received unexpected SAE frame");
259 }
260 }
261 TxEapolKeyFrame { ref frame, .. } => {
262 if event.is_ready_for_eapol {
263 crate::rx_wlan_data_frame(
264 &event.channel,
265 &CLIENT_MAC_ADDR,
266 &event.bssid.clone().into(),
267 &event.bssid.clone().into(),
268 &frame[..],
269 mac::ETHER_TYPE_EAPOL,
270 event.phy,
271 )?;
272 control.updates.remove(index);
273 control
274 .authenticator
275 .on_eapol_conf(
276 &mut control.updates,
277 fidl_mlme::EapolResultCode::Success,
278 )
279 .context("failed to send EAPOL confirm")?;
280 continue; } else {
282 debug!("authentication: received unexpected EAPOL key frame");
283 }
284 }
285 _ => {}
286 }
287 index += 1;
288 }
289 Ok(())
290 })
291}
292
293pub fn send_association_response<'h, S>(
294 phy: &'h fidl_tap::WlantapPhyProxy,
295 bssid: &'h Bssid,
296 channel: &'h Channel,
297 status: impl Into<mac::StatusCode>,
298) -> impl Handler<S, fidl_tap::TxArgs, Output = ActionResult> + 'h
299where
300 S: 'h,
301{
302 let status = status.into();
303 event::extract(move |_: Buffered<AssocReqFrame>| {
304 crate::send_association_response(channel, bssid, status, phy)
305 .context("failed to send association response frame")
306 })
307}
308
309pub fn connect_with_open_authentication<'h, S>(
310 phy: &'h fidl_tap::WlantapPhyProxy,
311 ssid: &'h Ssid,
312 bssid: &'h Bssid,
313 channel: &'h Channel,
314 protection: &'h Protection,
315) -> impl Handler<S, fidl_tap::TxArgs, Output = ActionResult> + 'h
316where
317 S: 'h,
318{
319 branch::or((
320 send_open_authentication(phy, bssid, channel, fidl_ieee80211::StatusCode::Success),
321 send_association_response(phy, bssid, channel, fidl_ieee80211::StatusCode::Success),
322 send_probe_response(phy, ssid, bssid, channel, protection),
323 ))
324}
325
326pub fn connect_with_authentication_tap<'h, H, S>(
327 phy: &'h fidl_tap::WlantapPhyProxy,
328 ssid: &'h Ssid,
329 bssid: &'h Bssid,
330 channel: &'h Channel,
331 protection: &'h Protection,
332 tap: AuthenticationTap<'h, H>,
333) -> impl Handler<S, fidl_tap::TxArgs, Output = ActionResult> + 'h
334where
335 H: Handler<AuthenticationControl, AuthenticationEvent<'h>, Output = ActionResult> + 'h,
336 S: 'h,
337{
338 type Tap<'a, H> = AuthenticationTap<'a, H>;
339
340 let authenticate =
341 event::extract(Stateful(|tap: &mut Tap<'_, H>, frame: Buffered<AuthFrame>| {
342 let frame = frame.get();
343 match frame.auth_hdr.auth_alg_num {
344 mac::AuthAlgorithmNumber::OPEN => {
345 if matches!(
346 tap.control.authenticator,
347 Authenticator { auth_cfg: auth::Config::ComputedPsk(_), .. },
348 ) {
349 crate::send_open_authentication(
350 channel,
351 bssid,
352 fidl_ieee80211::StatusCode::Success,
353 phy,
354 )
355 .context("failed to send open authentication frame")?;
356 tap.call(&AuthenticationEvent {
357 phy,
358 bssid,
359 channel,
360 is_ready_for_sae: false,
361 is_ready_for_eapol: false,
362 })
363 } else {
364 bail!("open authentication frame is incompatible with authenticator");
365 }
366 }
367 mac::AuthAlgorithmNumber::SAE => {
368 info!("auth_txn_seq_num: {}", { frame.auth_hdr.auth_txn_seq_num });
369 if frame.auth_hdr.auth_txn_seq_num == 1 {
375 tap.control.authenticator.reset();
376 tap.control.updates.clear();
377 }
378
379 tap.control
380 .authenticator
381 .on_sae_frame_rx(
382 &mut tap.control.updates,
383 fidl_mlme::SaeFrame {
384 peer_sta_address: bssid.to_array(),
385 status_code: frame
386 .auth_hdr
387 .status_code
388 .into_fidl_or_refused_unspecified(),
389 seq_num: frame.auth_hdr.auth_txn_seq_num,
390 sae_fields: frame.elements.to_vec(),
391 },
392 )
393 .context("failed to process SAE frame with authenticator")?;
394 tap.call(&AuthenticationEvent {
395 phy,
396 bssid,
397 channel,
398 is_ready_for_sae: true,
399 is_ready_for_eapol: false,
400 })
401 }
402 auth_alg_num => {
403 bail!("unexpected authentication algorithm: {:?}", auth_alg_num);
404 }
405 }
406 }));
407 let associate = event::extract(Stateful(|tap: &mut Tap<'_, H>, _: Buffered<AssocReqFrame>| {
408 crate::send_association_response(channel, bssid, fidl_ieee80211::StatusCode::Success, phy)
409 .context("failed to send association response frame")?;
410 match tap.control.authenticator.auth_cfg {
411 auth::Config::ComputedPsk(_) => tap.control.authenticator.reset(),
412 auth::Config::DriverSae { .. } => {
413 bail!("hardware simulator does not support driver SAE");
414 }
415 auth::Config::Sae { .. } => {}
416 }
417 tap.control
418 .authenticator
419 .initiate(&mut tap.control.updates)
420 .context("failed to initiate authenticator")?;
421 tap.call(&AuthenticationEvent {
422 phy,
423 bssid,
424 channel,
425 is_ready_for_sae: true,
426 is_ready_for_eapol: true,
427 })
428 }));
429 let eapol = event::extract(Stateful(|tap: &mut Tap<'_, H>, frame: Buffered<DataFrame>| {
430 for mac::Msdu { llc_frame, .. } in frame.get() {
432 assert_eq!(llc_frame.hdr.protocol_id.to_native(), mac::ETHER_TYPE_EAPOL);
433 let mic_size = tap.control.authenticator.get_negotiated_protection().mic_size;
434 let key_frame_rx = eapol::KeyFrameRx::parse(mic_size as usize, llc_frame.body)
435 .context("failed to parse EAPOL frame")?;
436 tap.control
437 .authenticator
438 .on_eapol_frame(&mut tap.control.updates, eapol::Frame::Key(key_frame_rx))
439 .context("failed to process EAPOL frame with authenticator")?;
440 tap.call(&AuthenticationEvent {
441 phy,
442 bssid,
443 channel,
444 is_ready_for_sae: true,
445 is_ready_for_eapol: true,
446 })?;
447 }
448 Ok(())
449 }));
450
451 event::with_state(
452 tap,
453 branch::or((
454 authenticate,
455 associate,
456 send_probe_response(phy, ssid, bssid, channel, protection),
457 eapol,
458 )),
459 )
460}