1mod state;
6
7use state::*;
8
9use crate::ap::event::{ClientEvent, Event};
10use crate::ap::{aid, Context, MlmeRequest, RsnCfg};
11use ieee80211::{MacAddr, MacAddrBytes};
12use log::error;
13use wlan_common::ie::SupportedRate;
14use wlan_common::mac::{Aid, CapabilityInfo};
15use wlan_common::timer::EventHandle;
16use wlan_rsn::key::exchange::Key;
17use wlan_rsn::key::Tk;
18use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme};
19
20pub struct RemoteClient {
21 pub addr: MacAddr,
22 state: Option<States>,
23}
24
25impl RemoteClient {
26 pub fn new(addr: MacAddr) -> Self {
27 Self { addr, state: Some(States::new_initial()) }
28 }
29
30 pub fn aid(&self) -> Option<Aid> {
31 #[expect(clippy::unwrap_used)]
33 let aid = self.state.as_ref().unwrap().aid();
34 aid
35 }
36
37 pub fn authenticated(&self) -> bool {
38 #[expect(clippy::unwrap_used)]
40 let authenticated = self.state.as_ref().unwrap().authenticated();
41 authenticated
42 }
43
44 pub fn associated(&self) -> bool {
45 self.aid().is_some()
46 }
47
48 pub fn handle_auth_ind(
49 &mut self,
50 ctx: &mut Context,
51 auth_type: fidl_mlme::AuthenticationTypes,
52 ) {
53 self.state = self.state.take().map(|state| state.handle_auth_ind(self, ctx, auth_type));
55 }
56
57 #[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
58 pub fn handle_assoc_ind(
59 &mut self,
60 ctx: &mut Context,
61 aid_map: &mut aid::Map,
62 ap_capabilities: CapabilityInfo,
63 client_capabilities: u16,
64 ap_rates: &[SupportedRate],
65 client_rates: &[SupportedRate],
66 rsn_cfg: &Option<RsnCfg>,
67 s_rsne: Option<Vec<u8>>,
68 ) {
69 self.state = self.state.take().map(|state| {
71 state.handle_assoc_ind(
72 self,
73 ctx,
74 aid_map,
75 ap_capabilities,
76 client_capabilities,
77 ap_rates,
78 client_rates,
79 rsn_cfg,
80 s_rsne,
81 )
82 });
83 }
84
85 pub fn handle_disassoc_ind(&mut self, ctx: &mut Context, aid_map: &mut aid::Map) {
86 self.state = self.state.take().map(|state| state.handle_disassoc_ind(self, ctx, aid_map));
88 }
89
90 pub fn handle_eapol_ind(&mut self, ctx: &mut Context, data: &[u8]) {
91 self.state = self.state.take().map(|state| state.handle_eapol_ind(self, ctx, data));
93 }
94
95 pub fn handle_eapol_conf(&mut self, ctx: &mut Context, result: fidl_mlme::EapolResultCode) {
96 self.state = self.state.take().map(|state| state.handle_eapol_conf(self, ctx, result));
98 }
99
100 pub fn handle_timeout(&mut self, ctx: &mut Context, event: ClientEvent) {
101 self.state = self.state.take().map(|state| state.handle_timeout(self, ctx, event));
103 }
104
105 pub fn send_authenticate_resp(
107 &mut self,
108 ctx: &mut Context,
109 result_code: fidl_mlme::AuthenticateResultCode,
110 ) {
111 log::info!("Sending fidl_mlme::AuthenticateResponse - result code: {:?}", result_code);
113 ctx.mlme_sink.send(MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
114 peer_sta_address: self.addr.to_array(),
115 result_code,
116 }))
117 }
118
119 pub fn send_deauthenticate_req(
121 &mut self,
122 ctx: &mut Context,
123 reason_code: fidl_ieee80211::ReasonCode,
124 ) {
125 ctx.mlme_sink.send(MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
126 peer_sta_address: self.addr.to_array(),
127 reason_code,
128 }))
129 }
130
131 pub fn send_associate_resp(
133 &mut self,
134 ctx: &mut Context,
135 result_code: fidl_mlme::AssociateResultCode,
136 aid: Aid,
137 capabilities: CapabilityInfo,
138 rates: Vec<SupportedRate>,
139 ) {
140 ctx.mlme_sink.send(MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
141 peer_sta_address: self.addr.to_array(),
142 result_code,
143 association_id: aid,
144 capability_info: capabilities.0,
145 rates: rates.into_iter().map(|r| r.0).collect(),
146 }))
147 }
148
149 pub fn send_eapol_req(&mut self, ctx: &mut Context, frame: eapol::KeyFrameBuf) {
151 ctx.mlme_sink.send(MlmeRequest::Eapol(fidl_mlme::EapolRequest {
152 src_addr: ctx.device_info.sta_addr,
153 dst_addr: self.addr.to_array(),
154 data: frame.into(),
155 }));
156 }
157
158 pub fn send_set_controlled_port_req(
160 &mut self,
161 ctx: &mut Context,
162 port_state: fidl_mlme::ControlledPortState,
163 ) {
164 ctx.mlme_sink.send(MlmeRequest::SetCtrlPort(fidl_mlme::SetControlledPortRequest {
165 peer_sta_address: self.addr.to_array(),
166 state: port_state,
167 }));
168 }
169
170 pub fn send_key(&mut self, ctx: &mut Context, key: &Key) {
171 let set_key_descriptor = match key {
172 Key::Ptk(ptk) => fidl_mlme::SetKeyDescriptor {
173 key: ptk.tk().to_vec(),
174 key_id: 0,
175 key_type: fidl_mlme::KeyType::Pairwise,
176 address: self.addr.to_array(),
177 rsc: 0,
178 cipher_suite_oui: eapol::to_array(&ptk.cipher.oui[..]),
179 cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
180 ptk.cipher.suite_type.into(),
181 ),
182 },
183 Key::Gtk(gtk) => fidl_mlme::SetKeyDescriptor {
184 key: gtk.tk().to_vec(),
185 key_id: gtk.key_id() as u16,
186 key_type: fidl_mlme::KeyType::Group,
187 address: [0xFFu8; 6],
188 rsc: gtk.key_rsc(),
189 cipher_suite_oui: eapol::to_array(>k.cipher().oui[..]),
190 cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(
191 gtk.cipher().suite_type.into(),
192 ),
193 },
194 _ => {
195 error!("unsupported key type in UpdateSink");
196 return;
197 }
198 };
199 ctx.mlme_sink.send(MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest {
200 keylist: vec![set_key_descriptor],
201 }));
202 }
203
204 pub fn schedule_at(
205 &mut self,
206 ctx: &mut Context,
207 deadline: zx::MonotonicInstant,
208 event: ClientEvent,
209 ) -> EventHandle {
210 ctx.timer.schedule_at(deadline, Event::Client { addr: self.addr, event })
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::{test_utils, MlmeSink, MlmeStream};
218 use futures::channel::mpsc;
219 use lazy_static::lazy_static;
220 use wlan_common::test_utils::fake_features::fake_mac_sublayer_support;
221 use wlan_common::{assert_variant, timer};
222
223 lazy_static! {
224 static ref AP_ADDR: MacAddr = [6u8; 6].into();
225 static ref CLIENT_ADDR: MacAddr = [7u8; 6].into();
226 }
227
228 fn make_remote_client() -> RemoteClient {
229 RemoteClient::new(*CLIENT_ADDR)
230 }
231
232 fn make_env() -> (Context, MlmeStream, timer::EventStream<Event>) {
233 let device_info = test_utils::fake_device_info(*AP_ADDR);
234 let mac_sublayer_support = fake_mac_sublayer_support();
235 let (mlme_sink, mlme_stream) = mpsc::unbounded();
236 let (timer, time_stream) = timer::create_timer();
237 let ctx = Context {
238 device_info,
239 mac_sublayer_support,
240 mlme_sink: MlmeSink::new(mlme_sink),
241 timer,
242 };
243 (ctx, mlme_stream, time_stream)
244 }
245
246 #[test]
247 fn aid_when_not_associated() {
248 let r_sta = make_remote_client();
249 assert_eq!(r_sta.aid(), None);
250 }
251
252 #[test]
253 fn authenticated_when_not_authenticated() {
254 let r_sta = make_remote_client();
255 assert!(!r_sta.authenticated());
256 }
257
258 #[test]
259 fn authenticated_when_authenticated() {
260 let mut r_sta = make_remote_client();
261 let (mut ctx, _, _) = make_env();
262 r_sta.handle_auth_ind(&mut ctx, fidl_mlme::AuthenticationTypes::OpenSystem);
263 assert!(r_sta.authenticated());
264 }
265
266 #[test]
267 fn authenticated_when_associated() {
268 let mut r_sta = make_remote_client();
269 let (mut ctx, _, _) = make_env();
270 r_sta.handle_auth_ind(&mut ctx, fidl_mlme::AuthenticationTypes::OpenSystem);
271 let mut aid_map = aid::Map::default();
272 r_sta.handle_assoc_ind(
273 &mut ctx,
274 &mut aid_map,
275 CapabilityInfo(0).with_short_preamble(true),
276 CapabilityInfo(0).with_short_preamble(true).raw(),
277 &[SupportedRate(0b11111000)][..],
278 &[SupportedRate(0b11111000)][..],
279 &None,
280 None,
281 );
282 assert!(r_sta.authenticated());
283 }
284
285 #[test]
286 fn aid_when_associated() {
287 let mut r_sta = make_remote_client();
288 let (mut ctx, _, _) = make_env();
289 r_sta.handle_auth_ind(&mut ctx, fidl_mlme::AuthenticationTypes::OpenSystem);
290 let mut aid_map = aid::Map::default();
291 r_sta.handle_assoc_ind(
292 &mut ctx,
293 &mut aid_map,
294 CapabilityInfo(0).with_short_preamble(true),
295 CapabilityInfo(0).with_short_preamble(true).raw(),
296 &[SupportedRate(0b11111000)][..],
297 &[SupportedRate(0b11111000)][..],
298 &None,
299 None,
300 );
301 assert_eq!(r_sta.aid(), Some(1));
302 }
303
304 #[test]
305 fn aid_after_disassociation() {
306 let mut r_sta = make_remote_client();
307 let (mut ctx, _, _) = make_env();
308 r_sta.handle_auth_ind(&mut ctx, fidl_mlme::AuthenticationTypes::OpenSystem);
309 assert!(r_sta.authenticated());
310 let mut aid_map = aid::Map::default();
311 r_sta.handle_assoc_ind(
312 &mut ctx,
313 &mut aid_map,
314 CapabilityInfo(0).with_short_preamble(true),
315 CapabilityInfo(0).with_short_preamble(true).raw(),
316 &[SupportedRate(0b11111000)][..],
317 &[SupportedRate(0b11111000)][..],
318 &None,
319 None,
320 );
321 assert_variant!(r_sta.aid(), Some(_));
322 r_sta.handle_disassoc_ind(&mut ctx, &mut aid_map);
323 assert_eq!(r_sta.aid(), None);
324 }
325
326 #[test]
327 fn disassociate_does_nothing_when_not_associated() {
328 let mut r_sta = make_remote_client();
329 let (mut ctx, _, _) = make_env();
330 let mut aid_map = aid::Map::default();
331 r_sta.handle_disassoc_ind(&mut ctx, &mut aid_map);
332 }
333
334 #[test]
335 fn send_authenticate_resp() {
336 let mut r_sta = make_remote_client();
337 let (mut ctx, mut mlme_stream, _) = make_env();
338 r_sta.send_authenticate_resp(
339 &mut ctx,
340 fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired,
341 );
342 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
343 assert_variant!(mlme_event, MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
344 peer_sta_address,
345 result_code,
346 }) => {
347 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
348 assert_eq!(result_code, fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired);
349 });
350 }
351
352 #[test]
353 fn association_times_out() {
354 let mut r_sta = make_remote_client();
355 let (mut ctx, _, _) = make_env();
356 r_sta.handle_auth_ind(&mut ctx, fidl_mlme::AuthenticationTypes::OpenSystem);
357 assert!(r_sta.authenticated());
358 r_sta.handle_timeout(&mut ctx, ClientEvent::AssociationTimeout);
359 assert!(!r_sta.authenticated());
360 }
361
362 #[test]
363 fn send_associate_resp() {
364 let mut r_sta = make_remote_client();
365 let (mut ctx, mut mlme_stream, _) = make_env();
366 r_sta.send_associate_resp(
367 &mut ctx,
368 fidl_mlme::AssociateResultCode::RefusedApOutOfMemory,
369 1,
370 CapabilityInfo(0).with_short_preamble(true),
371 vec![SupportedRate(1), SupportedRate(2), SupportedRate(3)],
372 );
373 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
374 assert_variant!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
375 peer_sta_address,
376 result_code,
377 association_id,
378 capability_info,
379 rates,
380 }) => {
381 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
382 assert_eq!(result_code, fidl_mlme::AssociateResultCode::RefusedApOutOfMemory);
383 assert_eq!(association_id, 1);
384 assert_eq!(capability_info, CapabilityInfo(0).with_short_preamble(true).raw());
385 assert_eq!(rates, vec![1, 2, 3]);
386 });
387 }
388
389 #[test]
390 fn send_deauthenticate_req() {
391 let mut r_sta = make_remote_client();
392 let (mut ctx, mut mlme_stream, _) = make_env();
393 r_sta.send_deauthenticate_req(&mut ctx, fidl_ieee80211::ReasonCode::NoMoreStas);
394 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
395 assert_variant!(mlme_event, MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
396 peer_sta_address,
397 reason_code,
398 }) => {
399 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
400 assert_eq!(reason_code, fidl_ieee80211::ReasonCode::NoMoreStas);
401 });
402 }
403
404 #[test]
405 fn send_eapol_req() {
406 let mut r_sta = make_remote_client();
407 let (mut ctx, mut mlme_stream, _) = make_env();
408 r_sta.send_eapol_req(&mut ctx, test_utils::eapol_key_frame());
409 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
410 assert_variant!(mlme_event, MlmeRequest::Eapol(fidl_mlme::EapolRequest {
411 src_addr,
412 dst_addr,
413 data,
414 }) => {
415 assert_eq!(&src_addr, AP_ADDR.as_array());
416 assert_eq!(&dst_addr, CLIENT_ADDR.as_array());
417 assert_eq!(data, Vec::<u8>::from(test_utils::eapol_key_frame()));
418 });
419 }
420
421 #[test]
422 fn send_key_ptk() {
423 let mut r_sta = make_remote_client();
424 let (mut ctx, mut mlme_stream, _) = make_env();
425 r_sta.send_key(&mut ctx, &Key::Ptk(test_utils::ptk()));
426 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
427 assert_variant!(mlme_event, MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest { keylist }) => {
428 assert_eq!(keylist.len(), 1);
429 let k = keylist.first().expect("expect key descriptor");
430 assert_eq!(k.key, vec![0xCCu8; test_utils::cipher().tk_bytes().unwrap() as usize]);
431 assert_eq!(k.key_id, 0);
432 assert_eq!(k.key_type, fidl_mlme::KeyType::Pairwise);
433 assert_eq!(&k.address, CLIENT_ADDR.as_array());
434 assert_eq!(k.rsc, 0);
435 assert_eq!(k.cipher_suite_oui, [0x00, 0x0F, 0xAC]);
436 assert_eq!(k.cipher_suite_type, fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4));
437 });
438 }
439
440 #[test]
441 fn send_key_gtk() {
442 let mut r_sta = make_remote_client();
443 let (mut ctx, mut mlme_stream, _) = make_env();
444 r_sta.send_key(&mut ctx, &Key::Gtk(test_utils::gtk()));
445 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
446 assert_variant!(mlme_event, MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest { keylist }) => {
447 assert_eq!(keylist.len(), 1);
448 let k = keylist.first().expect("expect key descriptor");
449 assert_eq!(&k.key[..], &test_utils::gtk_bytes()[..]);
450 assert_eq!(k.key_id, 2);
451 assert_eq!(k.key_type, fidl_mlme::KeyType::Group);
452 assert_eq!(k.address, [0xFFu8; 6]);
453 assert_eq!(k.rsc, 0);
454 assert_eq!(k.cipher_suite_oui, [0x00, 0x0F, 0xAC]);
455 assert_eq!(k.cipher_suite_type, fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4));
456 });
457 }
458
459 #[test]
460 fn send_set_controlled_port_req() {
461 let mut r_sta = make_remote_client();
462 let (mut ctx, mut mlme_stream, _) = make_env();
463 r_sta.send_set_controlled_port_req(&mut ctx, fidl_mlme::ControlledPortState::Open);
464 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
465 assert_variant!(mlme_event, MlmeRequest::SetCtrlPort(fidl_mlme::SetControlledPortRequest {
466 peer_sta_address,
467 state,
468 }) => {
469 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
470 assert_eq!(state, fidl_mlme::ControlledPortState::Open);
471 });
472 }
473
474 #[test]
475 fn schedule_at() {
476 let mut r_sta = make_remote_client();
477 let (mut ctx, _, mut time_stream) = make_env();
478 let timeout_event = r_sta.schedule_at(
479 &mut ctx,
480 zx::MonotonicInstant::after(zx::MonotonicDuration::from_seconds(2)),
481 ClientEvent::AssociationTimeout,
482 );
483 let (_, timed_event, _) = time_stream.try_next().unwrap().expect("expected timed event");
484 assert_eq!(timed_event.id, timeout_event.id());
485 assert_variant!(timed_event.event, Event::Client { addr, event } => {
486 assert_eq!(addr, *CLIENT_ADDR);
487 assert_variant!(event, ClientEvent::AssociationTimeout);
488 });
489 }
490}