1use anyhow::{Context as _, Error, format_err};
6use fidl::endpoints;
7use fidl_fuchsia_wlan_common as fidl_common;
8use fidl_fuchsia_wlan_common::WlanMacRole;
9use fidl_fuchsia_wlan_device_service::DeviceMonitorProxy;
10use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
11use fidl_fuchsia_wlan_internal as fidl_internal;
12use fidl_fuchsia_wlan_sme as fidl_sme;
13use futures::stream::TryStreamExt;
14use ieee80211::Ssid;
15use wlan_common::bss::{BssDescription, Protection};
16use wlan_common::security::SecurityError;
17use wlan_common::security::wep::WepKey;
18use wlan_common::security::wpa::credential::{Passphrase, Psk};
19
20type WlanService = DeviceMonitorProxy;
21
22#[derive(Clone, Debug)]
27struct SecurityContext {
28 pub bss: BssDescription,
29 pub unparsed_credential_bytes: Vec<u8>,
30}
31
32impl TryFrom<SecurityContext> for fidl_internal::Authentication {
39 type Error = SecurityError;
40
41 fn try_from(context: SecurityContext) -> Result<Self, SecurityError> {
42 let SecurityContext { bss, unparsed_credential_bytes } = context;
43 fn parse_wpa_credentials(
44 unparsed_credential_bytes: Vec<u8>,
45 ) -> Result<fidl_internal::Credentials, SecurityError> {
46 if let Ok(psk) = Psk::try_from(unparsed_credential_bytes.as_slice()) {
48 Ok(fidl_internal::Credentials::Wpa(fidl_internal::WpaCredentials::Psk(psk.into())))
49 } else {
50 let passphrase = Passphrase::try_from(unparsed_credential_bytes)?;
51 Ok(fidl_internal::Credentials::Wpa(fidl_internal::WpaCredentials::Passphrase(
52 passphrase.into(),
53 )))
54 }
55 }
56
57 match bss.protection() {
58 Protection::Unknown | Protection::Wpa2Enterprise | Protection::Wpa3Enterprise => {
61 Err(SecurityError::Unsupported)
62 }
63 Protection::Open | Protection::Owe | Protection::OpenOweTransition
64 if unparsed_credential_bytes.len() > 0 =>
65 {
66 Err(SecurityError::Incompatible)
67 }
68 Protection::Open | Protection::OpenOweTransition => Ok(fidl_internal::Authentication {
69 protocol: fidl_internal::Protocol::Open,
70 credentials: None,
71 }),
72 Protection::Owe => Ok(fidl_internal::Authentication {
73 protocol: fidl_internal::Protocol::Owe,
74 credentials: None,
75 }),
76 Protection::Wep => WepKey::parse(unparsed_credential_bytes.as_slice())
77 .map(|key| fidl_internal::Authentication {
78 protocol: fidl_internal::Protocol::Wep,
79 credentials: Some(Box::new(fidl_internal::Credentials::Wep(
80 fidl_internal::WepCredentials { key: key.into() },
81 ))),
82 })
83 .map_err(From::from),
84 Protection::Wpa1 => {
85 parse_wpa_credentials(unparsed_credential_bytes).map(|credentials| {
86 fidl_internal::Authentication {
87 protocol: fidl_internal::Protocol::Wpa1,
88 credentials: Some(Box::new(credentials)),
89 }
90 })
91 }
92 Protection::Wpa1Wpa2PersonalTkipOnly
93 | Protection::Wpa1Wpa2Personal
94 | Protection::Wpa2PersonalTkipOnly
95 | Protection::Wpa2Personal => {
96 parse_wpa_credentials(unparsed_credential_bytes).map(|credentials| {
97 fidl_internal::Authentication {
98 protocol: fidl_internal::Protocol::Wpa2Personal,
99 credentials: Some(Box::new(credentials)),
100 }
101 })
102 }
103 Protection::Wpa2Wpa3Personal => {
104 if let Ok(psk) = Psk::try_from(unparsed_credential_bytes.as_slice()) {
106 Ok(fidl_internal::Authentication {
107 protocol: fidl_internal::Protocol::Wpa2Personal,
108 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
109 fidl_internal::WpaCredentials::Psk(psk.into()),
110 ))),
111 })
112 } else {
113 Passphrase::try_from(unparsed_credential_bytes)
114 .map(|passphrase| fidl_internal::Authentication {
115 protocol: fidl_internal::Protocol::Wpa3Personal,
116 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
117 fidl_internal::WpaCredentials::Passphrase(passphrase.into()),
118 ))),
119 })
120 .map_err(From::from)
121 }
122 }
123 Protection::Wpa3Personal => Passphrase::try_from(unparsed_credential_bytes)
124 .map(|passphrase| fidl_internal::Authentication {
125 protocol: fidl_internal::Protocol::Wpa3Personal,
126 credentials: Some(Box::new(fidl_internal::Credentials::Wpa(
127 fidl_internal::WpaCredentials::Passphrase(passphrase.into()),
128 ))),
129 })
130 .map_err(From::from),
131 }
132 }
133}
134
135pub async fn get_sme_proxy(
136 wlan_svc: &WlanService,
137 iface_id: u16,
138) -> Result<fidl_sme::ClientSmeProxy, Error> {
139 let (sme_proxy, sme_remote) = endpoints::create_proxy();
140 let result = wlan_svc
141 .get_client_sme(iface_id, sme_remote)
142 .await
143 .context("error sending GetClientSme request")?;
144 match result {
145 Ok(()) => Ok(sme_proxy),
146 Err(e) => Err(format_err!(
147 "Failed to get client sme proxy for interface id {} with error {}",
148 iface_id,
149 e
150 )),
151 }
152}
153
154pub async fn get_first_sme(wlan_svc: &WlanService) -> Result<fidl_sme::ClientSmeProxy, Error> {
155 let iface_id = super::get_first_iface(wlan_svc, WlanMacRole::Client)
156 .await
157 .context("failed to get iface")?;
158 get_sme_proxy(&wlan_svc, iface_id).await
159}
160
161pub async fn connect(
162 iface_sme_proxy: &fidl_sme::ClientSmeProxy,
163 target_ssid: Ssid,
164 target_pwd: Vec<u8>,
165 target_bss_desc: fidl_ieee80211::BssDescription,
166) -> Result<bool, Error> {
167 let (connection_proxy, connection_remote) = endpoints::create_proxy();
168
169 let authentication = fidl_internal::Authentication::try_from(SecurityContext {
173 bss: BssDescription::try_from(target_bss_desc.clone())?,
174 unparsed_credential_bytes: target_pwd,
175 })?;
176 let req = fidl_sme::ConnectRequest {
177 ssid: target_ssid.clone().into(),
178 bss_description: target_bss_desc,
179 authentication,
180 deprecated_scan_type: fidl_common::ScanType::Passive,
181 multiple_bss_candidates: false, };
183
184 let _result = iface_sme_proxy.connect(&req, Some(connection_remote))?;
185
186 let connection_result_code = handle_connect_transaction(connection_proxy).await?;
187
188 if !matches!(connection_result_code, fidl_ieee80211::StatusCode::Success) {
189 log::error!("Failed to connect to network: {:?}", connection_result_code);
190 return Ok(false);
191 }
192
193 let client_status_response =
194 iface_sme_proxy.status().await.context("failed to check status from sme_proxy")?;
195 Ok(match client_status_response {
196 fidl_sme::ClientStatusResponse::Connected(serving_ap_info) => {
197 if serving_ap_info.ssid != target_ssid {
198 log::error!(
199 "Connected to wrong network: {:?}. Expected: {:?}.",
200 serving_ap_info.ssid.as_slice(),
201 target_ssid
202 );
203 false
204 } else {
205 true
206 }
207 }
208 fidl_sme::ClientStatusResponse::Connecting(_)
209 | fidl_sme::ClientStatusResponse::Roaming(_)
210 | fidl_sme::ClientStatusResponse::Idle(_) => {
211 log::error!(
212 "Unexpected status {:?} after {:?}",
213 client_status_response,
214 connection_result_code
215 );
216 false
217 }
218 })
219}
220
221async fn handle_connect_transaction(
222 connect_transaction: fidl_sme::ConnectTransactionProxy,
223) -> Result<fidl_ieee80211::StatusCode, Error> {
224 let mut event_stream = connect_transaction.take_event_stream();
225 let mut result_code = fidl_ieee80211::StatusCode::RefusedReasonUnspecified;
226
227 if let Some(evt) = event_stream
228 .try_next()
229 .await
230 .context("failed to receive connect result before the channel was closed")?
231 {
232 match evt {
233 fidl_sme::ConnectTransactionEvent::OnConnectResult { result } => {
234 result_code = result.code;
235 }
236 other => {
237 return Err(format_err!(
238 "Expected ConnectTransactionEvent::OnConnectResult event, got {:?}",
239 other
240 ));
241 }
242 }
243 }
244
245 Ok(result_code)
246}
247
248pub async fn disconnect(iface_sme_proxy: &fidl_sme::ClientSmeProxy) -> Result<(), Error> {
249 iface_sme_proxy
250 .disconnect(fidl_sme::UserDisconnectReason::WlanServiceUtilTesting)
251 .await
252 .context("failed to trigger disconnect")?;
253
254 let client_status_response =
256 iface_sme_proxy.status().await.context("failed to check status from sme_proxy")?;
257 match client_status_response {
258 fidl_sme::ClientStatusResponse::Connected(_)
259 | fidl_sme::ClientStatusResponse::Connecting(_)
260 | fidl_sme::ClientStatusResponse::Roaming(_) => {
261 Err(format_err!("Disconnect confirmation failed: {:?}", client_status_response))
262 }
263 fidl_sme::ClientStatusResponse::Idle(_) => Ok(()),
264 }
265}
266
267pub async fn disconnect_all(wlan_svc: &WlanService) -> Result<(), Error> {
268 let wlan_iface_ids =
269 super::get_iface_list(wlan_svc).await.context("Connect: failed to get wlan iface list")?;
270
271 let mut error_msg = format!("");
272 for iface_id in wlan_iface_ids {
273 let result = wlan_svc.query_iface(iface_id).await.context("querying iface info")?;
274
275 match result {
276 Ok(query_info) => {
277 if query_info.role == WlanMacRole::Client {
278 let sme_proxy = get_sme_proxy(&wlan_svc, iface_id)
279 .await
280 .context("Disconnect all: failed to get iface sme proxy")?;
281 if let Err(e) = disconnect(&sme_proxy).await {
282 error_msg =
283 format!("{}Error disconnecting iface {}: {}\n", error_msg, iface_id, e);
284 log::error!("disconnect_all: disconnect err on iface {}: {}", iface_id, e);
285 }
286 }
287 }
288 Err(zx::sys::ZX_ERR_NOT_FOUND) => {
289 error_msg = format!("{}no query response on iface {}\n", error_msg, iface_id);
290 log::error!("disconnect_all: iface query empty on iface {}", iface_id);
291 }
292 Err(e) => {
293 error_msg = format!("{}failed querying iface {}: {}\n", error_msg, iface_id, e);
294 log::error!("disconnect_all: query err on iface {}: {}", iface_id, e);
295 }
296 }
297 }
298 if error_msg.is_empty() { Ok(()) } else { Err(format_err!("{}", error_msg)) }
299}
300
301pub async fn passive_scan(
302 iface_sme_proxy: &fidl_sme::ClientSmeProxy,
303) -> Result<Vec<fidl_sme::ScanResult>, Error> {
304 let vmo = iface_sme_proxy
305 .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest { channels: vec![] }))
306 .await
307 .context("error sending scan request")?
308 .map_err(|scan_error_code| format_err!("Scan error: {:?}", scan_error_code))?;
309 wlan_common::scan::read_vmo(vmo)
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315 use crate::*;
316 use fidl::endpoints::RequestStream;
317 use fidl_fuchsia_wlan_common as fidl_common;
318 use fidl_fuchsia_wlan_device_service::{
319 self as wlan_service, DeviceMonitorMarker, DeviceMonitorProxy, DeviceMonitorRequest,
320 DeviceMonitorRequestStream,
321 };
322 use fidl_fuchsia_wlan_internal as fidl_internal;
323 use fidl_fuchsia_wlan_sme::{
324 ClientSmeMarker, ClientSmeRequest, ClientSmeRequestStream, Protection,
325 };
326 use fuchsia_async::TestExecutor;
327 use futures::stream::{StreamExt, StreamFuture};
328 use futures::task::Poll;
329 use ieee80211::Ssid;
330 use rand::Rng as _;
331 use std::convert::{TryFrom, TryInto};
332 use std::pin::pin;
333 use wlan_common::channel::{Cbw, Channel};
334 use wlan_common::scan::write_vmo;
335 use wlan_common::{assert_variant, fake_fidl_bss_description};
336
337 fn generate_random_wpa2_bss_description() -> fidl_fuchsia_wlan_internal::BssDescription {
338 let mut rng = rand::rng();
339 fidl_fuchsia_wlan_internal::BssDescription {
340 bssid: (0..6).map(|_| rng.r#gen::<u8>()).collect::<Vec<u8>>().try_into().unwrap(),
341 beacon_period: rng.r#gen::<u16>(),
342 rssi_dbm: rng.r#gen::<i8>(),
343 channel: fidl_ieee80211::WlanChannel {
344 primary: rng.r#gen::<u8>(),
345 cbw: match rng.r#gen_range(0..5) {
346 0 => fidl_ieee80211::ChannelBandwidth::Cbw20,
347 1 => fidl_ieee80211::ChannelBandwidth::Cbw40,
348 2 => fidl_ieee80211::ChannelBandwidth::Cbw40Below,
349 3 => fidl_ieee80211::ChannelBandwidth::Cbw80,
350 4 => fidl_ieee80211::ChannelBandwidth::Cbw160,
351 5 => fidl_ieee80211::ChannelBandwidth::Cbw80P80,
352 _ => panic!(),
353 },
354 secondary80: rng.r#gen::<u8>(),
355 },
356 snr_db: rng.r#gen::<i8>(),
357 ..fake_fidl_bss_description!(Wpa2)
358 }
359 }
360
361 fn extract_sme_server_from_get_client_sme_req_and_respond(
362 exec: &mut TestExecutor,
363 req_stream: &mut DeviceMonitorRequestStream,
364 result: Result<(), zx::Status>,
365 ) -> fidl_sme::ClientSmeRequestStream {
366 let req = exec.run_until_stalled(&mut req_stream.next());
367
368 let (responder, fake_sme_server) = assert_variant !(
369 req,
370 Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme{ iface_id:_, sme_server, responder})))
371 => (responder, sme_server));
372
373 responder
375 .send(result.map_err(|e| e.into_raw()))
376 .expect("fake sme proxy response: send failed");
377
378 fake_sme_server.into_stream()
382 }
383
384 fn respond_to_get_client_sme_request(
385 exec: &mut TestExecutor,
386 req_stream: &mut DeviceMonitorRequestStream,
387 result: Result<(), zx::Status>,
388 ) {
389 let req = exec.run_until_stalled(&mut req_stream.next());
390
391 let responder = assert_variant !(
392 req,
393 Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme{ responder, ..})))
394 => responder);
395
396 responder
398 .send(result.map_err(|e| e.into_raw()))
399 .expect("fake sme proxy response: send failed")
400 }
401
402 fn respond_to_client_sme_disconnect_request(
403 exec: &mut TestExecutor,
404 req_stream: &mut ClientSmeRequestStream,
405 ) {
406 let req = exec.run_until_stalled(&mut req_stream.next());
407 let responder = assert_variant !(
408 req,
409 Poll::Ready(Some(Ok(ClientSmeRequest::Disconnect{ responder, .. })))
410 => responder);
411
412 responder.send().expect("fake disconnect response: send failed")
414 }
415
416 fn respond_to_client_sme_status_request(
417 exec: &mut TestExecutor,
418 req_stream: &mut ClientSmeRequestStream,
419 status: &StatusResponse,
420 ) {
421 let req = exec.run_until_stalled(&mut req_stream.next());
422 let responder = assert_variant !(
423 req,
424 Poll::Ready(Some(Ok(ClientSmeRequest::Status{ responder})))
425 => responder);
426
427 match status {
429 StatusResponse::Idle => {
430 let response = fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {});
431 responder.send(&response).expect("Failed to send StatusResponse.");
432 }
433 StatusResponse::Connected => {
434 let serving_ap_info =
435 create_serving_ap_info_using_ssid(Ssid::try_from([1, 2, 3, 4]).unwrap());
436 let response = fidl_sme::ClientStatusResponse::Connected(serving_ap_info);
437 responder.send(&response).expect("Failed to send StatusResponse.");
438 }
439 StatusResponse::Connecting => {
440 let response = fidl_sme::ClientStatusResponse::Connecting(vec![1, 2, 3, 4]);
441 responder.send(&response).expect("Failed to send StatusResponse.");
442 }
443 }
444 }
445
446 fn test_get_first_sme(iface_list: &[WlanMacRole]) -> Result<(), Error> {
447 let (mut exec, proxy, mut req_stream) =
448 crate::tests::setup_fake_service::<DeviceMonitorMarker>();
449 let fut = get_first_sme(&proxy);
450 let mut fut = pin!(fut);
451
452 let ifaces = (0..iface_list.len() as u16).collect();
453
454 assert!(exec.run_until_stalled(&mut fut).is_pending());
455 crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
456
457 for mac_role in iface_list {
458 assert!(exec.run_until_stalled(&mut fut).is_pending());
460
461 crate::tests::respond_to_query_iface_request(
462 &mut exec,
463 &mut req_stream,
464 *mac_role,
465 Some([1, 2, 3, 4, 5, 6]),
466 );
467
468 if *mac_role == WlanMacRole::Client {
469 assert!(exec.run_until_stalled(&mut fut).is_pending());
471 respond_to_get_client_sme_request(&mut exec, &mut req_stream, Ok(()));
472 break;
473 }
474 }
475
476 let _proxy = exec.run_singlethreaded(&mut fut)?;
477 Ok(())
478 }
479
480 fn test_disconnect_all(iface_list: &[(WlanMacRole, StatusResponse)]) -> Result<(), Error> {
481 let (mut exec, proxy, mut req_stream) =
482 crate::tests::setup_fake_service::<DeviceMonitorMarker>();
483 let fut = disconnect_all(&proxy);
484 let mut fut = pin!(fut);
485
486 let ifaces = (0..iface_list.len() as u16).collect();
487
488 assert!(exec.run_until_stalled(&mut fut).is_pending());
489 crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
490
491 for (mac_role, status) in iface_list {
492 assert!(exec.run_until_stalled(&mut fut).is_pending());
494 crate::tests::respond_to_query_iface_request(
495 &mut exec,
496 &mut req_stream,
497 *mac_role,
498 Some([1, 2, 3, 4, 5, 6]),
499 );
500
501 if *mac_role == WlanMacRole::Client {
502 assert!(exec.run_until_stalled(&mut fut).is_pending());
504 let mut fake_sme_server_stream =
505 extract_sme_server_from_get_client_sme_req_and_respond(
506 &mut exec,
507 &mut req_stream,
508 Ok(()),
509 );
510
511 assert!(exec.run_until_stalled(&mut fut).is_pending());
513 respond_to_client_sme_disconnect_request(&mut exec, &mut fake_sme_server_stream);
514
515 assert!(exec.run_until_stalled(&mut fut).is_pending());
516
517 respond_to_client_sme_status_request(
519 &mut exec,
520 &mut fake_sme_server_stream,
521 status,
522 );
523 }
524 }
525 exec.run_singlethreaded(&mut fut)
526 }
527
528 #[test]
530 fn check_get_client_sme_success() {
531 let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Client];
532 test_get_first_sme(&iface_list).expect("expect success but failed");
533 }
534
535 #[test]
537 fn check_get_client_sme_no_devices() {
538 let iface_list: Vec<WlanMacRole> = Vec::new();
539 test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
540 }
541
542 #[test]
544 fn check_get_client_sme_no_clients() {
545 let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Ap];
546 test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
547 }
548
549 #[test]
552 fn check_disconnect_all_client_and_ap_success() {
553 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
554 (WlanMacRole::Ap, StatusResponse::Idle),
555 (WlanMacRole::Client, StatusResponse::Idle),
556 ];
557 test_disconnect_all(&iface_list).expect("Expect success but failed")
558 }
559
560 #[test]
563 fn check_disconnect_all_all_clients_success() {
564 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
565 (WlanMacRole::Client, StatusResponse::Idle),
566 (WlanMacRole::Client, StatusResponse::Idle),
567 ];
568 test_disconnect_all(&iface_list).expect("Expect success but failed");
569 }
570
571 #[test]
573 fn check_disconnect_all_all_clients_fail() {
574 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
575 (WlanMacRole::Ap, StatusResponse::Connected),
576 (WlanMacRole::Client, StatusResponse::Connected),
577 ];
578 test_disconnect_all(&iface_list).expect_err("Expect fail but succeeded");
579 }
580
581 #[test]
583 fn check_disconnect_all_no_clients_success() {
584 let iface_list: Vec<(WlanMacRole, StatusResponse)> =
585 vec![(WlanMacRole::Ap, StatusResponse::Idle), (WlanMacRole::Ap, StatusResponse::Idle)];
586 test_disconnect_all(&iface_list).expect("Expect success but failed");
587 }
588
589 #[test]
590 fn list_ifaces_returns_iface_id_vector() {
591 let mut exec = TestExecutor::new();
592 let (wlan_monitor, server) = create_wlan_monitor_util();
593 let mut next_device_monitor_req = server.into_future();
594
595 let ifaces: Vec<u16> = vec![0, 1, 35, 36];
596
597 let fut = get_iface_list(&wlan_monitor);
598 let mut fut = pin!(fut);
599 assert!(exec.run_until_stalled(&mut fut).is_pending());
600
601 send_iface_list_response(&mut exec, &mut next_device_monitor_req, ifaces.clone());
602
603 let complete = exec.run_until_stalled(&mut fut);
604
605 let list_response = match complete {
606 Poll::Ready(result) => result,
607 _ => panic!("Expected an iface list response"),
608 };
609
610 let response = match list_response {
611 Ok(response) => response,
612 Err(_) => panic!("Expected a valid list response"),
613 };
614
615 assert_eq!(response, ifaces)
617 }
618
619 #[test]
620 fn list_ifaces_properly_handles_zero_ifaces() {
621 let mut exec = TestExecutor::new();
622 let (wlan_monitor, server) = create_wlan_monitor_util();
623 let mut next_device_monitor_req = server.into_future();
624
625 let iface_id_list: Vec<u16> = vec![];
627 let iface_list_vec = vec![];
628
629 let fut = get_iface_list(&wlan_monitor);
630 let mut fut = pin!(fut);
631 assert!(exec.run_until_stalled(&mut fut).is_pending());
632
633 send_iface_list_response(&mut exec, &mut next_device_monitor_req, iface_list_vec);
634
635 let complete = exec.run_until_stalled(&mut fut);
636
637 let list_response = match complete {
638 Poll::Ready(result) => result,
639 _ => panic!("Expected an iface list response"),
640 };
641
642 let response = match list_response {
643 Ok(response) => response,
644 Err(_) => panic!("Expected a valid list response"),
645 };
646
647 assert_eq!(response, iface_id_list)
649 }
650
651 #[test]
652 fn list_phys_returns_iface_id_vector() {
653 let mut exec = TestExecutor::new();
654 let (monitor_service, server) = create_wlan_monitor_util();
655 let mut next_device_service_req = server.into_future();
656
657 let phy_id_list: Vec<u16> = vec![0, 1, 35, 36];
659 let mut phy_list_vec = vec![];
660 for id in &phy_id_list {
661 phy_list_vec.push(*id);
662 }
663
664 let fut = get_phy_list(&monitor_service);
665 let mut fut = pin!(fut);
666 assert!(exec.run_until_stalled(&mut fut).is_pending());
667
668 send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
669
670 let complete = exec.run_until_stalled(&mut fut);
671
672 let list_response = match complete {
673 Poll::Ready(result) => result,
674 _ => panic!("Expected an phy list response"),
675 };
676
677 let response = match list_response {
678 Ok(response) => response,
679 Err(_) => panic!("Expected a valid list response"),
680 };
681
682 assert_eq!(response, phy_id_list)
684 }
685
686 #[test]
687 fn list_phys_properly_handles_zero_phys() {
688 let mut exec = TestExecutor::new();
689 let (monitor_service, server) = create_wlan_monitor_util();
690 let mut next_device_service_req = server.into_future();
691
692 let phy_id_list: Vec<u16> = vec![];
694 let phy_list_vec = vec![];
695
696 let fut = get_phy_list(&monitor_service);
697 let mut fut = pin!(fut);
698 assert!(exec.run_until_stalled(&mut fut).is_pending());
699
700 send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
701
702 let complete = exec.run_until_stalled(&mut fut);
703
704 let list_response = match complete {
705 Poll::Ready(result) => result,
706 _ => panic!("Expected an phy list response"),
707 };
708
709 let response = match list_response {
710 Ok(response) => response,
711 Err(_) => panic!("Expected a valid list response"),
712 };
713
714 assert_eq!(response, phy_id_list)
716 }
717
718 fn poll_device_monitor_req(
719 exec: &mut TestExecutor,
720 next_device_monitor_req: &mut StreamFuture<DeviceMonitorRequestStream>,
721 ) -> Poll<DeviceMonitorRequest> {
722 exec.run_until_stalled(next_device_monitor_req).map(|(req, stream)| {
723 *next_device_monitor_req = stream.into_future();
724 req.expect("did not expect the DeviceMonitorRequestStream to end")
725 .expect("error polling device service request stream")
726 })
727 }
728
729 fn send_iface_list_response(
730 exec: &mut TestExecutor,
731 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
732 ifaces: Vec<u16>,
733 ) {
734 let responder = match poll_device_monitor_req(exec, server) {
735 Poll::Ready(DeviceMonitorRequest::ListIfaces { responder }) => responder,
736 Poll::Pending => panic!("expected a request to be available"),
737 _ => panic!("expected a ListIfaces request"),
738 };
739
740 let _result = responder.send(&ifaces[..]);
742 }
743
744 fn send_phy_list_response(
745 exec: &mut TestExecutor,
746 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
747 phy_list_vec: Vec<u16>,
748 ) {
749 let responder = match poll_device_monitor_req(exec, server) {
750 Poll::Ready(DeviceMonitorRequest::ListPhys { responder }) => responder,
751 Poll::Pending => panic!("expected a request to be available"),
752 _ => panic!("expected a ListPhys request"),
753 };
754
755 let _result = responder.send(&phy_list_vec);
757 }
758
759 #[test]
760 fn get_client_sme_valid_iface() {
761 let mut exec = TestExecutor::new();
762 let (wlan_monitor, server) = create_wlan_monitor_util();
763 let mut next_device_monitor_req = server.into_future();
764
765 let fut = get_sme_proxy(&wlan_monitor, 1);
766 let mut fut = pin!(fut);
767 assert!(exec.run_until_stalled(&mut fut).is_pending());
768
769 send_sme_proxy_response(&mut exec, &mut next_device_monitor_req, Ok(()));
771
772 let () = match exec.run_until_stalled(&mut fut) {
773 Poll::Ready(Ok(_)) => (),
774 _ => panic!("Expected a status response"),
775 };
776 }
777
778 fn send_sme_proxy_response(
779 exec: &mut TestExecutor,
780 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
781 result: Result<(), zx::Status>,
782 ) {
783 let responder = match poll_device_monitor_req(exec, server) {
784 Poll::Ready(DeviceMonitorRequest::GetClientSme { responder, .. }) => responder,
785 Poll::Pending => panic!("expected a request to be available"),
786 _ => panic!("expected a GetClientSme request"),
787 };
788
789 let _result = responder.send(result.map_err(|e| e.into_raw()));
791 }
792
793 #[test]
794 fn get_client_sme_invalid_iface() {
795 let mut exec = TestExecutor::new();
796 let (wlan_monitor, server) = create_wlan_monitor_util();
797 let mut next_device_monitor_req = server.into_future();
798
799 let fut = get_sme_proxy(&wlan_monitor, 1);
800 let mut fut = pin!(fut);
801 assert!(exec.run_until_stalled(&mut fut).is_pending());
802
803 send_sme_proxy_response(
805 &mut exec,
806 &mut next_device_monitor_req,
807 Err(zx::Status::NOT_FOUND),
808 );
809
810 let complete = exec.run_until_stalled(&mut fut);
811
812 match complete {
813 Poll::Ready(Err(_)) => (),
814 _ => panic!("Expected a status response"),
815 };
816 }
817
818 #[test]
819 fn connect_success_returns_true() {
820 let connect_result =
821 test_wpa2_connect("TestAp", "password", "TestAp", fidl_ieee80211::StatusCode::Success);
822 assert!(connect_result);
823 }
824
825 #[test]
826 fn connect_failed_returns_false() {
827 let connect_result = test_wpa2_connect(
828 "TestAp",
829 "password",
830 "",
831 fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
832 );
833 assert!(!connect_result);
834 }
835
836 #[test]
837 fn connect_different_ssid_returns_false() {
838 let connect_result = test_wpa2_connect(
839 "TestAp1",
840 "password",
841 "TestAp2",
842 fidl_ieee80211::StatusCode::Success,
843 );
844 assert!(!connect_result);
845 }
846
847 fn test_wpa2_connect(
848 target_ssid: &str,
849 password: &str,
850 connected_to_ssid: &str,
851 result_code: fidl_ieee80211::StatusCode,
852 ) -> bool {
853 let target_ssid = Ssid::try_from(target_ssid).unwrap();
854 let connected_to_ssid = Ssid::try_from(connected_to_ssid).unwrap();
855
856 let mut exec = TestExecutor::new();
857 let (client_sme, server) = create_client_sme_proxy();
858 let mut next_client_sme_req = server.into_future();
859
860 let target_password = password.as_bytes();
861 let target_bss_desc = generate_random_wpa2_bss_description();
862
863 let fut = connect(
864 &client_sme,
865 target_ssid.clone(),
866 target_password.to_vec(),
867 target_bss_desc.clone(),
868 );
869 let mut fut = pin!(fut);
870 assert!(exec.run_until_stalled(&mut fut).is_pending());
871
872 send_connect_request_response(
874 &mut exec,
875 &mut next_client_sme_req,
876 &target_ssid,
877 fidl_internal::Authentication::try_from(SecurityContext {
878 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
879 unparsed_credential_bytes: target_password.to_vec(),
880 })
881 .unwrap(),
882 result_code,
883 );
884
885 if result_code == fidl_ieee80211::StatusCode::Success {
887 assert!(exec.run_until_stalled(&mut fut).is_pending());
888 send_status_response(
889 &mut exec,
890 &mut next_client_sme_req,
891 Some(connected_to_ssid),
892 None,
893 );
894 }
895
896 let complete = exec.run_until_stalled(&mut fut);
897
898 let connection_result = match complete {
899 Poll::Ready(result) => result,
900 _ => panic!("Expected a connect response"),
901 };
902
903 let returned_bool = match connection_result {
904 Ok(response) => response,
905 _ => panic!("Expected a valid connection result"),
906 };
907
908 returned_bool
909 }
910
911 #[test]
912 fn connect_properly_passes_network_info_with_password() {
913 let mut exec = TestExecutor::new();
914 let (client_sme, server) = create_client_sme_proxy();
915 let mut next_client_sme_req = server.into_future();
916
917 let target_ssid = Ssid::try_from("TestAp").unwrap();
918 let target_password = "password".as_bytes();
919 let target_bss_desc = generate_random_wpa2_bss_description();
920
921 let fut = connect(
922 &client_sme,
923 target_ssid.clone(),
924 target_password.to_vec(),
925 target_bss_desc.clone(),
926 );
927 let mut fut = pin!(fut);
928 assert!(exec.run_until_stalled(&mut fut).is_pending());
929
930 verify_connect_request_info(
932 &mut exec,
933 &mut next_client_sme_req,
934 &target_ssid,
935 fidl_internal::Authentication::try_from(SecurityContext {
936 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
937 unparsed_credential_bytes: target_password.to_vec(),
938 })
939 .unwrap(),
940 target_bss_desc,
941 );
942 }
943
944 #[test]
945 fn connect_properly_passes_network_info_open() {
946 let mut exec = TestExecutor::new();
947 let (client_sme, server) = create_client_sme_proxy();
948 let mut next_client_sme_req = server.into_future();
949
950 let target_ssid = Ssid::try_from("TestAp").unwrap();
951 let target_password = "".as_bytes();
952 let target_bss_desc = fake_fidl_bss_description!(Open);
953
954 let fut = connect(
955 &client_sme,
956 target_ssid.clone(),
957 target_password.to_vec(),
958 target_bss_desc.clone(),
959 );
960 let mut fut = pin!(fut);
961 assert!(exec.run_until_stalled(&mut fut).is_pending());
962
963 verify_connect_request_info(
965 &mut exec,
966 &mut next_client_sme_req,
967 &target_ssid,
968 fidl_internal::Authentication::try_from(SecurityContext {
969 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
970 unparsed_credential_bytes: vec![],
971 })
972 .unwrap(),
973 target_bss_desc,
974 );
975 }
976
977 fn verify_connect_request_info(
978 exec: &mut TestExecutor,
979 server: &mut StreamFuture<ClientSmeRequestStream>,
980 expected_ssid: &Ssid,
981 expected_authentication: fidl_internal::Authentication,
982 expected_bss_desc: fidl_ieee80211::BssDescription,
983 ) {
984 match poll_client_sme_request(exec, server) {
985 Poll::Ready(ClientSmeRequest::Connect { req, .. }) => {
986 assert_eq!(expected_ssid, &req.ssid);
987 assert_eq!(req.authentication, expected_authentication);
988 assert_eq!(req.bss_description, expected_bss_desc);
989 }
990 _ => panic!("expected a Connect request"),
991 }
992 }
993
994 fn send_connect_request_response(
995 exec: &mut TestExecutor,
996 server: &mut StreamFuture<ClientSmeRequestStream>,
997 expected_ssid: &Ssid,
998 expected_authentication: fidl_internal::Authentication,
999 connect_result: fidl_ieee80211::StatusCode,
1000 ) {
1001 let responder = match poll_client_sme_request(exec, server) {
1002 Poll::Ready(ClientSmeRequest::Connect { req, txn, .. }) => {
1003 assert_eq!(expected_ssid, &req.ssid[..]);
1004 assert_eq!(req.authentication, expected_authentication);
1005 txn.expect("expected a Connect transaction channel")
1006 }
1007 Poll::Pending => panic!("expected a request to be available"),
1008 _ => panic!("expected a Connect request"),
1009 };
1010 let connect_transaction = responder
1011 .into_stream()
1012 .expect("failed to create a connect transaction stream")
1013 .control_handle();
1014 connect_transaction
1015 .send_on_connect_result(&fidl_sme::ConnectResult {
1016 code: connect_result,
1017 is_credential_rejected: false,
1018 is_reconnect: false,
1019 })
1020 .expect("failed to send OnConnectResult to ConnectTransaction");
1021 }
1022
1023 fn poll_client_sme_request(
1024 exec: &mut TestExecutor,
1025 next_client_sme_req: &mut StreamFuture<ClientSmeRequestStream>,
1026 ) -> Poll<ClientSmeRequest> {
1027 exec.run_until_stalled(next_client_sme_req).map(|(req, stream)| {
1028 *next_client_sme_req = stream.into_future();
1029 req.expect("did not expect the ClientSmeRequestStream to end")
1030 .expect("error polling client sme request stream")
1031 })
1032 }
1033
1034 fn create_client_sme_proxy() -> (fidl_sme::ClientSmeProxy, ClientSmeRequestStream) {
1035 let (proxy, server) = endpoints::create_proxy::<ClientSmeMarker>();
1036 let server = server.into_stream();
1037 (proxy, server)
1038 }
1039
1040 fn create_wlan_monitor_util() -> (DeviceMonitorProxy, DeviceMonitorRequestStream) {
1041 let (proxy, server) = endpoints::create_proxy::<DeviceMonitorMarker>();
1042 let server = server.into_stream();
1043 (proxy, server)
1044 }
1045
1046 enum StatusResponse {
1047 Idle,
1048 Connected,
1049 Connecting,
1050 }
1051
1052 #[test]
1053 fn disconnect_with_empty_status_response() {
1054 if let Poll::Ready(result) = test_disconnect(StatusResponse::Idle) {
1055 return assert!(result.is_ok());
1056 }
1057 panic!("disconnect did not return a Poll::Ready")
1058 }
1059
1060 #[test]
1061 fn disconnect_fail_because_connected() {
1062 if let Poll::Ready(result) = test_disconnect(StatusResponse::Connected) {
1063 return assert!(result.is_err());
1064 }
1065 panic!("disconnect did not return a Poll::Ready")
1066 }
1067
1068 #[test]
1069 fn disconnect_fail_because_connecting() {
1070 if let Poll::Ready(result) = test_disconnect(StatusResponse::Connecting) {
1071 return assert!(result.is_err());
1072 }
1073 panic!("disconnect did not return a Poll::Ready")
1074 }
1075
1076 fn test_disconnect(status: StatusResponse) -> Poll<Result<(), Error>> {
1077 let mut exec = TestExecutor::new();
1078 let (client_sme, server) = create_client_sme_proxy();
1079 let mut client_sme_req = server.into_future();
1080
1081 let fut = disconnect(&client_sme);
1082 let mut fut = pin!(fut);
1083 assert!(exec.run_until_stalled(&mut fut).is_pending());
1084
1085 send_disconnect_request_response(&mut exec, &mut client_sme_req);
1086
1087 assert!(exec.run_until_stalled(&mut fut).is_pending());
1088
1089 match status {
1090 StatusResponse::Idle => {
1091 send_status_response(&mut exec, &mut client_sme_req, None, None)
1092 }
1093 StatusResponse::Connected => send_status_response(
1094 &mut exec,
1095 &mut client_sme_req,
1096 Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1097 None,
1098 ),
1099 StatusResponse::Connecting => send_status_response(
1100 &mut exec,
1101 &mut client_sme_req,
1102 None,
1103 Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1104 ),
1105 }
1106
1107 exec.run_until_stalled(&mut fut)
1108 }
1109
1110 fn send_disconnect_request_response(
1111 exec: &mut TestExecutor,
1112 server: &mut StreamFuture<ClientSmeRequestStream>,
1113 ) {
1114 let rsp = match poll_client_sme_request(exec, server) {
1115 Poll::Ready(ClientSmeRequest::Disconnect { responder, .. }) => responder,
1116 Poll::Pending => panic!("Expected a DisconnectRequest"),
1117 _ => panic!("Expected a DisconnectRequest"),
1118 };
1119 rsp.send().expect("Failed to send DisconnectResponse.");
1120 }
1121
1122 fn create_serving_ap_info_using_ssid(ssid: Ssid) -> fidl_sme::ServingApInfo {
1123 fidl_sme::ServingApInfo {
1124 bssid: [0, 1, 2, 3, 4, 5],
1125 ssid: ssid.into(),
1126 rssi_dbm: -30,
1127 snr_db: 10,
1128 channel: fidl_ieee80211::WlanChannel {
1129 primary: 1,
1130 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
1131 secondary80: 0,
1132 },
1133 protection: Protection::Wpa2Personal,
1134 }
1135 }
1136
1137 fn send_status_response(
1138 exec: &mut TestExecutor,
1139 server: &mut StreamFuture<ClientSmeRequestStream>,
1140 connected_to_ssid: Option<Ssid>,
1141 connecting_to_ssid: Option<Ssid>,
1142 ) {
1143 let rsp = match poll_client_sme_request(exec, server) {
1144 Poll::Ready(ClientSmeRequest::Status { responder }) => responder,
1145 Poll::Pending => panic!("Expected a StatusRequest"),
1146 _ => panic!("Expected a StatusRequest"),
1147 };
1148
1149 let response = match (connected_to_ssid, connecting_to_ssid) {
1150 (Some(_), Some(_)) => panic!("SME cannot simultaneously be Connected and Connecting."),
1151 (Some(ssid), None) => {
1152 let serving_ap_info = create_serving_ap_info_using_ssid(ssid);
1153 fidl_sme::ClientStatusResponse::Connected(serving_ap_info)
1154 }
1155 (None, Some(ssid)) => fidl_sme::ClientStatusResponse::Connecting(ssid.to_vec()),
1156 (None, None) => fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {}),
1157 };
1158
1159 rsp.send(&response).expect("Failed to send StatusResponse.");
1160 }
1161
1162 #[test]
1163 fn scan_success_returns_empty_results() {
1164 assert_eq!(test_scan(&[]), &[]);
1165 }
1166
1167 #[test]
1168 fn scan_success_returns_results() {
1169 let scan_results = &[
1170 create_scan_result(
1171 [0, 1, 2, 3, 4, 5],
1172 Ssid::try_from("foo").unwrap(),
1173 -30,
1174 20,
1175 Channel::new(1, Cbw::Cbw20),
1176 Protection::Wpa2Personal,
1177 Some(fidl_sme::Compatibility {
1178 mutual_security_protocols: vec![fidl_internal::Protocol::Wpa2Personal],
1179 }),
1180 ),
1181 create_scan_result(
1182 [1, 2, 3, 4, 5, 6],
1183 Ssid::try_from("hello").unwrap(),
1184 -60,
1185 10,
1186 Channel::new(2, Cbw::Cbw20),
1187 Protection::Wpa2Personal,
1188 None,
1189 ),
1190 ];
1191
1192 assert_eq!(test_scan(scan_results), scan_results);
1193 }
1194
1195 #[test]
1196 fn scan_error_correctly_handled() {
1197 assert!(test_scan_error().is_err())
1199 }
1200
1201 fn test_scan(scan_results: &[fidl_sme::ScanResult]) -> Vec<fidl_sme::ScanResult> {
1202 let mut exec = TestExecutor::new();
1203 let (client_sme, server) = create_client_sme_proxy();
1204 let mut client_sme_req = server.into_future();
1205
1206 let fut = passive_scan(&client_sme);
1207 let mut fut = pin!(fut);
1208 assert!(exec.run_until_stalled(&mut fut).is_pending());
1209
1210 send_scan_result_response(&mut exec, &mut client_sme_req, scan_results);
1211
1212 let complete = exec.run_until_stalled(&mut fut);
1213 let request_result = match complete {
1214 Poll::Ready(result) => result,
1215 _ => panic!("Expected a scan request result"),
1216 };
1217 let returned_scan_results = request_result.expect("failed to get scan results");
1218
1219 returned_scan_results
1220 }
1221
1222 fn send_scan_result_response(
1223 exec: &mut TestExecutor,
1224 server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1225 scan_results: &[fidl_sme::ScanResult],
1226 ) {
1227 match poll_client_sme_request(exec, server) {
1228 Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => {
1229 let vmo = write_vmo(scan_results.to_vec()).expect("failed to write VMO");
1230 responder.send(Ok(vmo)).expect("failed to send scan results")
1231 }
1232 Poll::Pending => panic!("expected a request to be available"),
1233 _ => panic!("expected a scan request"),
1234 }
1235 }
1236
1237 fn test_scan_error() -> Result<(), Error> {
1238 let mut exec = TestExecutor::new();
1239 let (client_sme, server) = create_client_sme_proxy();
1240 let mut client_sme_req = server.into_future();
1241
1242 let fut = passive_scan(&client_sme);
1243 let mut fut = pin!(fut);
1244 assert!(exec.run_until_stalled(&mut fut).is_pending());
1245
1246 send_scan_error_response(&mut exec, &mut client_sme_req);
1247 let _ = exec.run_until_stalled(&mut fut)?;
1248 Ok(())
1249 }
1250
1251 fn send_scan_error_response(
1252 exec: &mut TestExecutor,
1253 server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1254 ) {
1255 match poll_client_sme_request(exec, server) {
1256 Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => responder
1257 .send(Err(fidl_sme::ScanErrorCode::InternalError))
1258 .expect("failed to send ScanError"),
1259 Poll::Pending => panic!("expected a request to be available"),
1260 _ => panic!("expected a scan request"),
1261 };
1262 }
1263
1264 fn create_scan_result(
1265 bssid: [u8; 6],
1266 ssid: Ssid,
1267 rssi_dbm: i8,
1268 snr_db: i8,
1269 channel: Channel,
1270 protection: Protection,
1271 compatibility: Option<fidl_sme::Compatibility>,
1272 ) -> fidl_sme::ScanResult {
1273 fidl_sme::ScanResult {
1274 compatibility: compatibility.map(Box::new),
1275 timestamp_nanos: zx::MonotonicInstant::get().into_nanos(),
1276 bss_description: fake_fidl_bss_description!(
1277 protection => protection,
1278 bssid: bssid,
1279 ssid: ssid,
1280 rssi_dbm: rssi_dbm,
1281 snr_db: snr_db,
1282 channel: channel,
1283 ),
1284 }
1285 }
1286
1287 fn send_destroy_iface_response(
1288 exec: &mut TestExecutor,
1289 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
1290 status: zx::Status,
1291 ) {
1292 let responder = match poll_device_monitor_req(exec, server) {
1293 Poll::Ready(DeviceMonitorRequest::DestroyIface { responder, .. }) => responder,
1294 Poll::Pending => panic!("expected a request to be available"),
1295 _ => panic!("expected a destroy iface request"),
1296 };
1297
1298 let _result = responder.send(status.into_raw());
1300 }
1301
1302 #[test]
1303 fn test_destroy_single_iface_ok() {
1304 let mut exec = TestExecutor::new();
1305 let (monitor_service, server) = create_wlan_monitor_util();
1306 let mut next_device_service_req = server.into_future();
1307
1308 let fut = destroy_iface(&monitor_service, 0);
1309 let mut fut = pin!(fut);
1310 assert!(exec.run_until_stalled(&mut fut).is_pending());
1311
1312 send_destroy_iface_response(&mut exec, &mut next_device_service_req, zx::Status::OK);
1313
1314 match exec.run_until_stalled(&mut fut) {
1315 Poll::Ready(Ok(_)) => (),
1316 _ => panic!("Expected a status response"),
1317 };
1318 }
1319}