1use super::{now, Protection, StateChangeContext, StateChangeContextExt};
6use crate::client::event::{self, Event, RsnaCompletionTimeout, RsnaResponseTimeout};
7use crate::client::internal::Context;
8use crate::client::rsn::Rsna;
9use crate::client::EstablishRsnaFailureReason;
10use crate::{MlmeRequest, MlmeSink};
11use fuchsia_inspect_contrib::inspect_log;
12use fuchsia_inspect_contrib::log::InspectBytes;
13use ieee80211::{Bssid, MacAddr, MacAddrBytes, WILDCARD_BSSID};
14use log::{error, warn};
15use wlan_common::bss::BssDescription;
16use wlan_common::timer::EventHandle;
17use wlan_rsn::key::exchange::Key;
18use wlan_rsn::key::Tk;
19use wlan_rsn::rsna::{self, SecAssocStatus, SecAssocUpdate};
20use wlan_statemachine::*;
21use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme};
22
23#[derive(Debug)]
24pub struct Init;
25
26#[derive(Debug)]
27pub struct EstablishingRsna {
28 pub rsna: Rsna,
29 pub rsna_completion_timeout: Option<EventHandle>,
32 pub rsna_response_timeout: Option<EventHandle>,
35 pub rsna_retransmission_timeout: Option<EventHandle>,
39
40 pub handshake_complete: bool,
45 pub pending_key_ids: std::collections::HashSet<u16>,
47}
48
49#[derive(Debug)]
50pub struct LinkUp {
51 pub protection: Protection,
52 pub since: zx::MonotonicInstant,
53}
54
55statemachine!(
56 #[derive(Debug)]
57 pub enum LinkState,
58 () => Init,
59 Init => [EstablishingRsna, LinkUp],
61 EstablishingRsna => LinkUp,
62);
63
64#[derive(Debug)]
65enum RsnaStatus {
66 Failed(EstablishRsnaFailureReason),
67 Unchanged,
68 Progressed {
69 ap_responsive: Option<EventHandle>,
70 new_retransmission_timeout: Option<EventHandle>,
71 handshake_complete: bool,
72 sent_keys: Vec<u16>,
73 },
74}
75
76#[derive(Debug)]
77enum RsnaProgressed {
78 Complete(LinkUp),
79 InProgress(EstablishingRsna),
80}
81
82impl EstablishingRsna {
83 fn on_rsna_progressed(
84 mut self,
85 ap_responsive: Option<EventHandle>,
86 rsna_retransmission_timeout: Option<EventHandle>,
87 handshake_complete: bool,
88 sent_keys: Vec<u16>,
89 ) -> Self {
90 sent_keys.into_iter().for_each(|key| {
91 let _ = self.pending_key_ids.insert(key);
92 });
93 self.handshake_complete |= handshake_complete;
94
95 self.rsna_retransmission_timeout = rsna_retransmission_timeout;
99 self.rsna_response_timeout = ap_responsive.or(self.rsna_response_timeout);
101 self
102 }
103
104 fn try_establish(self, bss: &BssDescription, context: &mut Context) -> RsnaProgressed {
106 if self.handshake_complete && self.pending_key_ids.is_empty() {
107 context.mlme_sink.send(MlmeRequest::SetCtrlPort(fidl_mlme::SetControlledPortRequest {
108 peer_sta_address: bss.bssid.to_array(),
109 state: fidl_mlme::ControlledPortState::Open,
110 }));
111
112 let now = now();
113 RsnaProgressed::Complete(LinkUp { protection: Protection::Rsna(self.rsna), since: now })
114 } else {
115 RsnaProgressed::InProgress(self)
116 }
117 }
118
119 #[allow(clippy::result_large_err)] fn handle_rsna_response_timeout(mut self) -> Result<Self, EstablishRsnaFailureReason> {
121 warn!("RSNA response timeout expired: {}ms", event::RSNA_RESPONSE_TIMEOUT_MILLIS);
122 self.rsna_retransmission_timeout = None;
123 self.rsna_response_timeout = None;
124 self.rsna_completion_timeout = None;
125 Err(self.rsna.supplicant.on_rsna_response_timeout())
126 }
127
128 #[allow(clippy::result_large_err)] fn handle_rsna_completion_timeout(mut self) -> Result<Self, EstablishRsnaFailureReason> {
130 warn!("RSNA completion timeout expired: {}ms", event::RSNA_COMPLETION_TIMEOUT_MILLIS);
131 self.rsna_retransmission_timeout = None;
132 self.rsna_response_timeout = None;
133 self.rsna_completion_timeout = None;
134 Err(self.rsna.supplicant.on_rsna_completion_timeout())
135 }
136}
137
138impl LinkState {
139 #[allow(clippy::result_large_err)] pub fn new(
141 protection: Protection,
142 context: &mut Context,
143 ) -> Result<Self, EstablishRsnaFailureReason> {
144 match protection {
145 Protection::Open | Protection::Wep(_) => {
146 let now = now();
147 Ok(State::new(Init)
148 .transition_to(LinkUp { protection: Protection::Open, since: now })
149 .into())
150 }
151 Protection::Rsna(mut rsna) | Protection::LegacyWpa(mut rsna) => {
152 let mut update_sink = rsna::UpdateSink::default();
153 rsna.supplicant.start(&mut update_sink).map_err(|e| {
154 error!("could not start Supplicant: {}", e);
155 EstablishRsnaFailureReason::StartSupplicantFailed
156 })?;
157 let state = State::new(Init).transition_to(EstablishingRsna {
158 rsna,
159 rsna_completion_timeout: Some(
160 context.timer.schedule(event::RsnaCompletionTimeout),
161 ),
162 rsna_response_timeout: Some(context.timer.schedule(event::RsnaResponseTimeout)),
163 rsna_retransmission_timeout: None,
164 handshake_complete: false,
165 pending_key_ids: Default::default(),
166 });
167 let (transition, state) = state.release_data();
168 match process_rsna_updates(context, None, update_sink, None) {
169 RsnaStatus::Unchanged => Ok(transition.to(state).into()),
170 RsnaStatus::Progressed {
172 ap_responsive: None,
173 new_retransmission_timeout: None,
174 handshake_complete: false,
175 sent_keys,
176 } if sent_keys.is_empty() => {
177 let state = state.on_rsna_progressed(None, None, false, sent_keys);
181 Ok(transition.to(state).into())
182 }
183 RsnaStatus::Failed(reason) => Err(reason),
184 rsna_status => {
185 error!("Unexpected RsnaStatus upon Supplicant::start(): {:?}", rsna_status);
186 Err(EstablishRsnaFailureReason::StartSupplicantFailed)
187 }
188 }
189 }
190 }
191 }
192
193 pub fn disconnect(self) -> (Protection, Option<zx::MonotonicDuration>) {
194 match self {
195 Self::EstablishingRsna(state) => {
196 let (_, state) = state.release_data();
197 (Protection::Rsna(state.rsna), None)
198 }
199 Self::LinkUp(state) => {
200 let (_, state) = state.release_data();
201 let connected_duration = now() - state.since;
202 (state.protection, Some(connected_duration))
203 }
204 #[expect(clippy::unreachable)]
207 _ => unreachable!(),
208 }
209 }
210
211 #[allow(clippy::result_large_err)] fn on_eapol_event<T, H>(
213 self,
214 eapol_event: T,
215 process_eapol_event: H,
216 bss: &BssDescription,
217 state_change_msg: &mut Option<StateChangeContext>,
218 context: &mut Context,
219 ) -> Result<Self, EstablishRsnaFailureReason>
220 where
221 H: Fn(&mut Context, &mut Rsna, &T) -> RsnaStatus,
222 {
223 match self {
224 Self::EstablishingRsna(state) => {
225 let (transition, mut state) = state.release_data();
226 match process_eapol_event(context, &mut state.rsna, &eapol_event) {
227 RsnaStatus::Failed(failure_reason) => {
228 state_change_msg.set_msg(format!("{:?}", failure_reason));
229 Err(failure_reason)
230 }
231 RsnaStatus::Progressed {
232 ap_responsive,
233 new_retransmission_timeout,
234 sent_keys,
235 handshake_complete,
236 } => {
237 match state
238 .on_rsna_progressed(
239 ap_responsive,
240 new_retransmission_timeout,
241 handshake_complete,
242 sent_keys,
243 )
244 .try_establish(bss, context)
245 {
246 RsnaProgressed::Complete(link_up) => {
247 state_change_msg.set_msg("RSNA established".to_string());
248 Ok(transition.to(link_up).into())
249 }
250 RsnaProgressed::InProgress(still_establishing_rsna) => {
251 Ok(transition.to(still_establishing_rsna).into())
252 }
253 }
254 }
255 RsnaStatus::Unchanged => Ok(transition.to(state).into()),
256 }
257 }
258 Self::LinkUp(state) => {
259 let (transition, mut state) = state.release_data();
260 if let Protection::Rsna(rsna) = &mut state.protection {
262 match process_eapol_event(context, rsna, &eapol_event) {
263 RsnaStatus::Unchanged => {}
264 RsnaStatus::Progressed {
271 ap_responsive: _,
272 new_retransmission_timeout: _,
273 handshake_complete: _,
274 sent_keys: _,
275 } => {}
276 s => error!("unexpected RsnaStatus in LinkUp state: {:?}", s),
279 };
280 }
281 Ok(transition.to(state).into())
282 }
283 #[expect(clippy::unreachable)]
286 _ => unreachable!(),
287 }
288 }
289
290 #[allow(clippy::result_large_err)] pub fn on_eapol_ind(
292 self,
293 eapol_ind: fidl_mlme::EapolIndication,
294 bss: &BssDescription,
295 state_change_msg: &mut Option<StateChangeContext>,
296 context: &mut Context,
297 ) -> Result<Self, EstablishRsnaFailureReason> {
298 self.on_eapol_event(eapol_ind, process_eapol_ind, bss, state_change_msg, context)
299 }
300
301 #[allow(clippy::result_large_err)] pub fn on_eapol_conf(
303 self,
304 eapol_conf: fidl_mlme::EapolConfirm,
305 bss: &BssDescription,
306 state_change_msg: &mut Option<StateChangeContext>,
307 context: &mut Context,
308 ) -> Result<Self, EstablishRsnaFailureReason> {
309 self.on_eapol_event(eapol_conf, process_eapol_conf, bss, state_change_msg, context)
310 }
311
312 #[allow(clippy::result_large_err)] pub fn on_set_keys_conf(
314 self,
315 set_keys_conf: fidl_mlme::SetKeysConfirm,
316 bss: &BssDescription,
317 state_change_msg: &mut Option<StateChangeContext>,
318 context: &mut Context,
319 ) -> Result<Self, EstablishRsnaFailureReason> {
320 for key_result in &set_keys_conf.results {
321 if key_result.status != zx::Status::OK.into_raw() {
322 state_change_msg.set_msg("Failed to set key in driver".to_string());
323 return Err(EstablishRsnaFailureReason::InternalError);
324 }
325 }
326
327 match self {
328 Self::EstablishingRsna(state) => {
329 let (transition, mut state) = state.release_data();
330 for key_result in set_keys_conf.results {
331 let _ = state.pending_key_ids.remove(&key_result.key_id);
332 }
333 match state.try_establish(bss, context) {
334 RsnaProgressed::Complete(link_up) => {
335 state_change_msg.set_msg("RSNA established".to_string());
336 Ok(transition.to(link_up).into())
337 }
338 RsnaProgressed::InProgress(still_establishing_rsna) => {
339 Ok(transition.to(still_establishing_rsna).into())
340 }
341 }
342 }
343 _ => Ok(self),
344 }
345 }
346
347 #[allow(clippy::result_large_err)] pub fn handle_timeout(
349 self,
350 event: Event,
351 state_change_msg: &mut Option<StateChangeContext>,
352 context: &mut Context,
353 ) -> Result<Self, EstablishRsnaFailureReason> {
354 match self {
355 Self::EstablishingRsna(state) => match event {
356 Event::RsnaResponseTimeout(RsnaResponseTimeout {}) => {
357 let (transition, state) = state.release_data();
358 match state.handle_rsna_response_timeout() {
359 Ok(still_establishing_rsna) => {
360 Ok(transition.to(still_establishing_rsna).into())
361 }
362 Err(failure) => {
363 state_change_msg.set_msg("RSNA response timeout".to_string());
364 Err(failure)
365 }
366 }
367 }
368 Event::RsnaCompletionTimeout(RsnaCompletionTimeout {}) => {
369 let (transition, state) = state.release_data();
370 match state.handle_rsna_completion_timeout() {
371 Ok(still_establishing_rsna) => {
372 Ok(transition.to(still_establishing_rsna).into())
373 }
374 Err(failure) => {
375 state_change_msg.set_msg("RSNA completion timeout".to_string());
376 Err(failure)
377 }
378 }
379 }
380 Event::RsnaRetransmissionTimeout(timeout) => {
381 let (transition, mut state) = state.release_data();
382 match process_rsna_retransmission_timeout(context, timeout, &mut state.rsna) {
383 RsnaStatus::Failed(failure_reason) => Err(failure_reason),
384 RsnaStatus::Unchanged => Ok(transition.to(state).into()),
385 RsnaStatus::Progressed {
386 ap_responsive,
387 new_retransmission_timeout,
388 sent_keys,
389 handshake_complete,
390 } => {
391 let still_establishing_rsna = state.on_rsna_progressed(
392 ap_responsive,
393 new_retransmission_timeout,
394 handshake_complete,
395 sent_keys,
396 );
397 Ok(transition.to(still_establishing_rsna).into())
398 }
399 }
400 }
401 _ => Ok(state.into()),
402 },
403 Self::LinkUp(state) => Ok(state.into()),
404 #[expect(clippy::unreachable)]
407 _ => unreachable!(),
408 }
409 }
410}
411
412fn inspect_log_key(context: &mut Context, key: &Key) {
413 let (cipher, key_index) = match key {
414 Key::Ptk(ptk) => (Some(&ptk.cipher), None),
415 Key::Gtk(gtk) => (Some(gtk.cipher()), Some(gtk.key_id())),
416 _ => (None, None),
417 };
418 inspect_log!(context.inspect.rsn_events.lock(), {
419 derived_key: key.name(),
420 cipher?: cipher.map(|c| format!("{:?}", c)),
421 key_index?: key_index,
422 });
423}
424
425fn send_keys(mlme_sink: &MlmeSink, bssid: Bssid, key: Key) -> Option<u16> {
426 let key_descriptor = match key {
427 Key::Ptk(ptk) => fidl_mlme::SetKeyDescriptor {
428 key_type: fidl_mlme::KeyType::Pairwise,
429 key: ptk.tk().to_vec(),
430 key_id: 0,
431 address: bssid.to_array(),
432 cipher_suite_oui: eapol::to_array(&ptk.cipher.oui[..]),
433 cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
434 ptk.cipher.suite_type.into(),
435 ),
436 rsc: 0,
437 },
438 Key::Gtk(gtk) => fidl_mlme::SetKeyDescriptor {
439 key_type: fidl_mlme::KeyType::Group,
440 key: gtk.tk().to_vec(),
441 key_id: gtk.key_id() as u16,
442 address: WILDCARD_BSSID.to_array(),
443 cipher_suite_oui: eapol::to_array(>k.cipher().oui[..]),
444 cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
445 gtk.cipher().suite_type.into(),
446 ),
447 rsc: gtk.key_rsc(),
448 },
449 Key::Igtk(igtk) => {
450 let mut rsc = [0u8; 8];
451 rsc[2..].copy_from_slice(&igtk.ipn[..]);
452 fidl_mlme::SetKeyDescriptor {
453 key_type: fidl_mlme::KeyType::Igtk,
454 key: igtk.igtk,
455 key_id: igtk.key_id,
456 address: [0xFFu8; 6],
457 cipher_suite_oui: eapol::to_array(&igtk.cipher.oui[..]),
458 cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
459 igtk.cipher.suite_type.into(),
460 ),
461 rsc: u64::from_be_bytes(rsc),
462 }
463 }
464 _ => {
465 error!("derived unexpected key");
466 return None;
467 }
468 };
469 let key_id = key_descriptor.key_id;
470 mlme_sink
471 .send(MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest { keylist: vec![key_descriptor] }));
472 Some(key_id)
473}
474
475fn send_eapol_frame(
479 context: &mut Context,
480 bssid: Bssid,
481 sta_addr: MacAddr,
482 frame: eapol::KeyFrameBuf,
483 schedule_timeout: bool,
484) -> Option<EventHandle> {
485 let resp_timeout = if schedule_timeout {
486 Some(context.timer.schedule(event::RsnaRetransmissionTimeout { bssid, sta_addr }))
487 } else {
488 None
489 };
490 inspect_log!(context.inspect.rsn_events.lock(), tx_eapol_frame: InspectBytes(&frame[..]));
491 context.mlme_sink.send(MlmeRequest::Eapol(fidl_mlme::EapolRequest {
492 src_addr: sta_addr.to_array(),
493 dst_addr: bssid.to_array(),
494 data: frame.into(),
495 }));
496 resp_timeout
497}
498
499fn process_eapol_conf(
500 context: &mut Context,
501 rsna: &mut Rsna,
502 eapol_conf: &fidl_mlme::EapolConfirm,
503) -> RsnaStatus {
504 let mut update_sink = rsna::UpdateSink::default();
505 match rsna.supplicant.on_eapol_conf(&mut update_sink, eapol_conf.result_code) {
506 Err(e) => {
507 error!("error handling EAPOL confirm: {}", e);
508 RsnaStatus::Unchanged
509 }
510 Ok(()) => {
511 process_rsna_updates(context, Some(Bssid::from(eapol_conf.dst_addr)), update_sink, None)
512 }
513 }
514}
515
516fn process_rsna_retransmission_timeout(
517 context: &mut Context,
518 timeout: event::RsnaRetransmissionTimeout,
519 rsna: &mut Rsna,
520) -> RsnaStatus {
521 let mut update_sink = rsna::UpdateSink::default();
522 match rsna.supplicant.on_rsna_retransmission_timeout(&mut update_sink) {
523 Err(e) => {
524 error!("{:?}", e);
525 RsnaStatus::Failed(EstablishRsnaFailureReason::InternalError)
526 }
527 Ok(()) => process_rsna_updates(context, Some(timeout.bssid), update_sink, None),
528 }
529}
530
531fn process_eapol_ind(
532 context: &mut Context,
533 rsna: &mut Rsna,
534 ind: &fidl_mlme::EapolIndication,
535) -> RsnaStatus {
536 let mic_size = rsna.negotiated_protection.mic_size;
537 let eapol_pdu = &ind.data[..];
538 let eapol_frame = match eapol::KeyFrameRx::parse(mic_size as usize, eapol_pdu) {
539 Ok(key_frame) => eapol::Frame::Key(key_frame),
540 Err(e) => {
541 error!("received invalid EAPOL Key frame: {:?}", e);
542 inspect_log!(context.inspect.rsn_events.lock(), {
543 rx_eapol_frame: InspectBytes(&eapol_pdu),
544 status: format!("rejected (parse error): {:?}", e)
545 });
546 return RsnaStatus::Unchanged;
547 }
548 };
549
550 let mut update_sink = rsna::UpdateSink::default();
551 if let Err(e) = rsna.supplicant.on_eapol_frame(&mut update_sink, eapol_frame) {
552 error!("error processing EAPOL key frame: {}", e);
553 inspect_log!(context.inspect.rsn_events.lock(), {
554 rx_eapol_frame: InspectBytes(&eapol_pdu),
555 status: format!("rejected (processing error): {}", e)
556 });
557 return RsnaStatus::Unchanged;
558 }
559
560 inspect_log!(context.inspect.rsn_events.lock(), {
561 rx_eapol_frame: InspectBytes(&eapol_pdu),
562 status: "processed"
563 });
564 let ap_responsive =
565 (!update_sink.is_empty()).then(|| context.timer.schedule(event::RsnaResponseTimeout {}));
566 process_rsna_updates(context, Some(Bssid::from(ind.src_addr)), update_sink, ap_responsive)
567}
568
569fn process_rsna_updates(
570 context: &mut Context,
571 bssid: Option<Bssid>,
572 updates: rsna::UpdateSink,
573 ap_responsive: Option<EventHandle>,
574) -> RsnaStatus {
575 if updates.is_empty() {
576 return RsnaStatus::Unchanged;
577 }
578
579 let sta_addr = MacAddr::from(context.device_info.sta_addr);
580 let mut new_retransmission_timeout = None;
581 let mut handshake_complete = false;
582 let mut sent_keys = vec![];
583 for update in updates {
584 match update {
585 SecAssocUpdate::TxEapolKeyFrame { frame, expect_response } => {
588 new_retransmission_timeout = match bssid {
589 None => {
590 error!("No BSSID set to handle SecAssocUpdate::TxEapolKeyFrame");
591 return RsnaStatus::Failed(EstablishRsnaFailureReason::InternalError);
592 }
593 Some(bssid) => {
594 send_eapol_frame(context, bssid, sta_addr, frame, expect_response)
595 }
596 }
597 }
598 SecAssocUpdate::Key(key) => match bssid {
601 None => {
602 error!("No BSSID set to handle SecAssocUpdate::Key");
603 return RsnaStatus::Failed(EstablishRsnaFailureReason::InternalError);
604 }
605 Some(bssid) => {
606 inspect_log_key(context, &key);
607 if let Some(key_id) = send_keys(&context.mlme_sink, bssid, key) {
608 sent_keys.push(key_id);
609 }
610 }
611 },
612 SecAssocUpdate::Status(status) => {
614 inspect_log!(
615 context.inspect.rsn_events.lock(),
616 rsna_status: format!("{:?}", status)
617 );
618 match status {
619 SecAssocStatus::EssSaEstablished => {
621 handshake_complete = true;
622 }
623 SecAssocStatus::WrongPassword => {
624 return RsnaStatus::Failed(EstablishRsnaFailureReason::InternalError);
625 }
626 SecAssocStatus::PmkSaEstablished => (),
627 }
628 }
629 update => warn!("Unhandled association update: {:?}", update),
631 }
632 }
633
634 RsnaStatus::Progressed {
635 ap_responsive,
636 new_retransmission_timeout,
637 handshake_complete,
638 sent_keys,
639 }
640}