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 assert_matches::assert_matches;
317 use fidl::endpoints::RequestStream;
318
319 use fidl_fuchsia_wlan_device_service::{
320 self as wlan_service, DeviceMonitorMarker, DeviceMonitorProxy, DeviceMonitorRequest,
321 DeviceMonitorRequestStream,
322 };
323 use fidl_fuchsia_wlan_internal as fidl_internal;
324 use fidl_fuchsia_wlan_sme::{
325 ClientSmeMarker, ClientSmeRequest, ClientSmeRequestStream, Protection,
326 };
327 use fuchsia_async::TestExecutor;
328 use futures::stream::{StreamExt, StreamFuture};
329 use futures::task::Poll;
330 use ieee80211::Ssid;
331 use rand::Rng as _;
332 use std::convert::{TryFrom, TryInto};
333 use std::pin::pin;
334 use wlan_common::channel::{Cbw, Channel};
335 use wlan_common::fake_fidl_bss_description;
336 use wlan_common::scan::write_vmo;
337
338 fn generate_random_wpa2_bss_description() -> fidl_ieee80211::BssDescription {
339 let mut rng = rand::rng();
340 fidl_ieee80211::BssDescription {
341 bssid: (0..6).map(|_| rng.random::<u8>()).collect::<Vec<u8>>().try_into().unwrap(),
342 beacon_period: rng.random::<u16>(),
343 rssi_dbm: rng.random::<i8>(),
344 channel: fidl_ieee80211::ChannelNumber {
345 number: rng.random::<u8>(),
346 band: if rng.random() {
347 fidl_ieee80211::WlanBand::TwoGhz
348 } else {
349 fidl_ieee80211::WlanBand::FiveGhz
350 },
351 },
352 snr_db: rng.random::<i8>(),
353 ..fake_fidl_bss_description!(Wpa2)
354 }
355 }
356
357 fn extract_sme_server_from_get_client_sme_req_and_respond(
358 exec: &mut TestExecutor,
359 req_stream: &mut DeviceMonitorRequestStream,
360 result: Result<(), zx::Status>,
361 ) -> fidl_sme::ClientSmeRequestStream {
362 let req = exec.run_until_stalled(&mut req_stream.next());
363
364 let (responder, fake_sme_server) = assert_matches!(
365 req,
366 Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme { iface_id: _, sme_server, responder })))
367 => (responder, sme_server)
368 );
369
370 responder
372 .send(result.map_err(|e| e.into_raw()))
373 .expect("fake sme proxy response: send failed");
374
375 fake_sme_server.into_stream()
379 }
380
381 fn respond_to_get_client_sme_request(
382 exec: &mut TestExecutor,
383 req_stream: &mut DeviceMonitorRequestStream,
384 result: Result<(), zx::Status>,
385 ) {
386 let req = exec.run_until_stalled(&mut req_stream.next());
387
388 let responder = assert_matches!(
389 req,
390 Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme { responder, .. }))) => responder
391 );
392
393 responder
395 .send(result.map_err(|e| e.into_raw()))
396 .expect("fake sme proxy response: send failed")
397 }
398
399 fn respond_to_client_sme_disconnect_request(
400 exec: &mut TestExecutor,
401 req_stream: &mut ClientSmeRequestStream,
402 ) {
403 let req = exec.run_until_stalled(&mut req_stream.next());
404 let responder = assert_matches!(
405 req,
406 Poll::Ready(Some(Ok(ClientSmeRequest::Disconnect { responder, .. }))) => responder
407 );
408
409 responder.send().expect("fake disconnect response: send failed")
411 }
412
413 fn respond_to_client_sme_status_request(
414 exec: &mut TestExecutor,
415 req_stream: &mut ClientSmeRequestStream,
416 status: &StatusResponse,
417 ) {
418 let req = exec.run_until_stalled(&mut req_stream.next());
419 let responder = assert_matches!(
420 req,
421 Poll::Ready(Some(Ok(ClientSmeRequest::Status { responder }))) => responder
422 );
423
424 match status {
426 StatusResponse::Idle => {
427 let response = fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {});
428 responder.send(&response).expect("Failed to send StatusResponse.");
429 }
430 StatusResponse::Connected => {
431 let serving_ap_info =
432 create_serving_ap_info_using_ssid(Ssid::try_from([1, 2, 3, 4]).unwrap());
433 let response = fidl_sme::ClientStatusResponse::Connected(serving_ap_info);
434 responder.send(&response).expect("Failed to send StatusResponse.");
435 }
436 StatusResponse::Connecting => {
437 let response = fidl_sme::ClientStatusResponse::Connecting(vec![1, 2, 3, 4]);
438 responder.send(&response).expect("Failed to send StatusResponse.");
439 }
440 }
441 }
442
443 fn test_get_first_sme(iface_list: &[WlanMacRole]) -> Result<(), Error> {
444 let (mut exec, proxy, mut req_stream) =
445 crate::tests::setup_fake_service::<DeviceMonitorMarker>();
446 let fut = get_first_sme(&proxy);
447 let mut fut = pin!(fut);
448
449 let ifaces = (0..iface_list.len() as u16).collect();
450
451 assert!(exec.run_until_stalled(&mut fut).is_pending());
452 crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
453
454 for mac_role in iface_list {
455 assert!(exec.run_until_stalled(&mut fut).is_pending());
457
458 crate::tests::respond_to_query_iface_request(
459 &mut exec,
460 &mut req_stream,
461 *mac_role,
462 Some([1, 2, 3, 4, 5, 6]),
463 );
464
465 if *mac_role == WlanMacRole::Client {
466 assert!(exec.run_until_stalled(&mut fut).is_pending());
468 respond_to_get_client_sme_request(&mut exec, &mut req_stream, Ok(()));
469 break;
470 }
471 }
472
473 let _proxy = exec.run_singlethreaded(&mut fut)?;
474 Ok(())
475 }
476
477 fn test_disconnect_all(iface_list: &[(WlanMacRole, StatusResponse)]) -> Result<(), Error> {
478 let (mut exec, proxy, mut req_stream) =
479 crate::tests::setup_fake_service::<DeviceMonitorMarker>();
480 let fut = disconnect_all(&proxy);
481 let mut fut = pin!(fut);
482
483 let ifaces = (0..iface_list.len() as u16).collect();
484
485 assert!(exec.run_until_stalled(&mut fut).is_pending());
486 crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
487
488 for (mac_role, status) in iface_list {
489 assert!(exec.run_until_stalled(&mut fut).is_pending());
491 crate::tests::respond_to_query_iface_request(
492 &mut exec,
493 &mut req_stream,
494 *mac_role,
495 Some([1, 2, 3, 4, 5, 6]),
496 );
497
498 if *mac_role == WlanMacRole::Client {
499 assert!(exec.run_until_stalled(&mut fut).is_pending());
501 let mut fake_sme_server_stream =
502 extract_sme_server_from_get_client_sme_req_and_respond(
503 &mut exec,
504 &mut req_stream,
505 Ok(()),
506 );
507
508 assert!(exec.run_until_stalled(&mut fut).is_pending());
510 respond_to_client_sme_disconnect_request(&mut exec, &mut fake_sme_server_stream);
511
512 assert!(exec.run_until_stalled(&mut fut).is_pending());
513
514 respond_to_client_sme_status_request(
516 &mut exec,
517 &mut fake_sme_server_stream,
518 status,
519 );
520 }
521 }
522 exec.run_singlethreaded(&mut fut)
523 }
524
525 #[test]
527 fn check_get_client_sme_success() {
528 let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Client];
529 test_get_first_sme(&iface_list).expect("expect success but failed");
530 }
531
532 #[test]
534 fn check_get_client_sme_no_devices() {
535 let iface_list: Vec<WlanMacRole> = Vec::new();
536 test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
537 }
538
539 #[test]
541 fn check_get_client_sme_no_clients() {
542 let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Ap];
543 test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
544 }
545
546 #[test]
549 fn check_disconnect_all_client_and_ap_success() {
550 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
551 (WlanMacRole::Ap, StatusResponse::Idle),
552 (WlanMacRole::Client, StatusResponse::Idle),
553 ];
554 test_disconnect_all(&iface_list).expect("Expect success but failed")
555 }
556
557 #[test]
560 fn check_disconnect_all_all_clients_success() {
561 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
562 (WlanMacRole::Client, StatusResponse::Idle),
563 (WlanMacRole::Client, StatusResponse::Idle),
564 ];
565 test_disconnect_all(&iface_list).expect("Expect success but failed");
566 }
567
568 #[test]
570 fn check_disconnect_all_all_clients_fail() {
571 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
572 (WlanMacRole::Ap, StatusResponse::Connected),
573 (WlanMacRole::Client, StatusResponse::Connected),
574 ];
575 test_disconnect_all(&iface_list).expect_err("Expect fail but succeeded");
576 }
577
578 #[test]
580 fn check_disconnect_all_no_clients_success() {
581 let iface_list: Vec<(WlanMacRole, StatusResponse)> =
582 vec![(WlanMacRole::Ap, StatusResponse::Idle), (WlanMacRole::Ap, StatusResponse::Idle)];
583 test_disconnect_all(&iface_list).expect("Expect success but failed");
584 }
585
586 #[test]
587 fn list_ifaces_returns_iface_id_vector() {
588 let mut exec = TestExecutor::new();
589 let (wlan_monitor, server) = create_wlan_monitor_util();
590 let mut next_device_monitor_req = server.into_future();
591
592 let ifaces: Vec<u16> = vec![0, 1, 35, 36];
593
594 let fut = get_iface_list(&wlan_monitor);
595 let mut fut = pin!(fut);
596 assert!(exec.run_until_stalled(&mut fut).is_pending());
597
598 send_iface_list_response(&mut exec, &mut next_device_monitor_req, ifaces.clone());
599
600 let complete = exec.run_until_stalled(&mut fut);
601
602 let list_response = match complete {
603 Poll::Ready(result) => result,
604 _ => panic!("Expected an iface list response"),
605 };
606
607 let response = match list_response {
608 Ok(response) => response,
609 Err(_) => panic!("Expected a valid list response"),
610 };
611
612 assert_eq!(response, ifaces)
614 }
615
616 #[test]
617 fn list_ifaces_properly_handles_zero_ifaces() {
618 let mut exec = TestExecutor::new();
619 let (wlan_monitor, server) = create_wlan_monitor_util();
620 let mut next_device_monitor_req = server.into_future();
621
622 let iface_id_list: Vec<u16> = vec![];
624 let iface_list_vec = vec![];
625
626 let fut = get_iface_list(&wlan_monitor);
627 let mut fut = pin!(fut);
628 assert!(exec.run_until_stalled(&mut fut).is_pending());
629
630 send_iface_list_response(&mut exec, &mut next_device_monitor_req, iface_list_vec);
631
632 let complete = exec.run_until_stalled(&mut fut);
633
634 let list_response = match complete {
635 Poll::Ready(result) => result,
636 _ => panic!("Expected an iface list response"),
637 };
638
639 let response = match list_response {
640 Ok(response) => response,
641 Err(_) => panic!("Expected a valid list response"),
642 };
643
644 assert_eq!(response, iface_id_list)
646 }
647
648 #[test]
649 fn list_phys_returns_iface_id_vector() {
650 let mut exec = TestExecutor::new();
651 let (monitor_service, server) = create_wlan_monitor_util();
652 let mut next_device_service_req = server.into_future();
653
654 let phy_id_list: Vec<u16> = vec![0, 1, 35, 36];
656 let mut phy_list_vec = vec![];
657 for id in &phy_id_list {
658 phy_list_vec.push(*id);
659 }
660
661 let fut = get_phy_list(&monitor_service);
662 let mut fut = pin!(fut);
663 assert!(exec.run_until_stalled(&mut fut).is_pending());
664
665 send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
666
667 let complete = exec.run_until_stalled(&mut fut);
668
669 let list_response = match complete {
670 Poll::Ready(result) => result,
671 _ => panic!("Expected an phy list response"),
672 };
673
674 let response = match list_response {
675 Ok(response) => response,
676 Err(_) => panic!("Expected a valid list response"),
677 };
678
679 assert_eq!(response, phy_id_list)
681 }
682
683 #[test]
684 fn list_phys_properly_handles_zero_phys() {
685 let mut exec = TestExecutor::new();
686 let (monitor_service, server) = create_wlan_monitor_util();
687 let mut next_device_service_req = server.into_future();
688
689 let phy_id_list: Vec<u16> = vec![];
691 let phy_list_vec = vec![];
692
693 let fut = get_phy_list(&monitor_service);
694 let mut fut = pin!(fut);
695 assert!(exec.run_until_stalled(&mut fut).is_pending());
696
697 send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
698
699 let complete = exec.run_until_stalled(&mut fut);
700
701 let list_response = match complete {
702 Poll::Ready(result) => result,
703 _ => panic!("Expected an phy list response"),
704 };
705
706 let response = match list_response {
707 Ok(response) => response,
708 Err(_) => panic!("Expected a valid list response"),
709 };
710
711 assert_eq!(response, phy_id_list)
713 }
714
715 fn poll_device_monitor_req(
716 exec: &mut TestExecutor,
717 next_device_monitor_req: &mut StreamFuture<DeviceMonitorRequestStream>,
718 ) -> Poll<DeviceMonitorRequest> {
719 exec.run_until_stalled(next_device_monitor_req).map(|(req, stream)| {
720 *next_device_monitor_req = stream.into_future();
721 req.expect("did not expect the DeviceMonitorRequestStream to end")
722 .expect("error polling device service request stream")
723 })
724 }
725
726 fn send_iface_list_response(
727 exec: &mut TestExecutor,
728 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
729 ifaces: Vec<u16>,
730 ) {
731 let responder = match poll_device_monitor_req(exec, server) {
732 Poll::Ready(DeviceMonitorRequest::ListIfaces { responder }) => responder,
733 Poll::Pending => panic!("expected a request to be available"),
734 _ => panic!("expected a ListIfaces request"),
735 };
736
737 let _result = responder.send(&ifaces[..]);
739 }
740
741 fn send_phy_list_response(
742 exec: &mut TestExecutor,
743 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
744 phy_list_vec: Vec<u16>,
745 ) {
746 let responder = match poll_device_monitor_req(exec, server) {
747 Poll::Ready(DeviceMonitorRequest::ListPhys { responder }) => responder,
748 Poll::Pending => panic!("expected a request to be available"),
749 _ => panic!("expected a ListPhys request"),
750 };
751
752 let _result = responder.send(&phy_list_vec);
754 }
755
756 #[test]
757 fn get_client_sme_valid_iface() {
758 let mut exec = TestExecutor::new();
759 let (wlan_monitor, server) = create_wlan_monitor_util();
760 let mut next_device_monitor_req = server.into_future();
761
762 let fut = get_sme_proxy(&wlan_monitor, 1);
763 let mut fut = pin!(fut);
764 assert!(exec.run_until_stalled(&mut fut).is_pending());
765
766 send_sme_proxy_response(&mut exec, &mut next_device_monitor_req, Ok(()));
768
769 let () = match exec.run_until_stalled(&mut fut) {
770 Poll::Ready(Ok(_)) => (),
771 _ => panic!("Expected a status response"),
772 };
773 }
774
775 fn send_sme_proxy_response(
776 exec: &mut TestExecutor,
777 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
778 result: Result<(), zx::Status>,
779 ) {
780 let responder = match poll_device_monitor_req(exec, server) {
781 Poll::Ready(DeviceMonitorRequest::GetClientSme { responder, .. }) => responder,
782 Poll::Pending => panic!("expected a request to be available"),
783 _ => panic!("expected a GetClientSme request"),
784 };
785
786 let _result = responder.send(result.map_err(|e| e.into_raw()));
788 }
789
790 #[test]
791 fn get_client_sme_invalid_iface() {
792 let mut exec = TestExecutor::new();
793 let (wlan_monitor, server) = create_wlan_monitor_util();
794 let mut next_device_monitor_req = server.into_future();
795
796 let fut = get_sme_proxy(&wlan_monitor, 1);
797 let mut fut = pin!(fut);
798 assert!(exec.run_until_stalled(&mut fut).is_pending());
799
800 send_sme_proxy_response(
802 &mut exec,
803 &mut next_device_monitor_req,
804 Err(zx::Status::NOT_FOUND),
805 );
806
807 let complete = exec.run_until_stalled(&mut fut);
808
809 match complete {
810 Poll::Ready(Err(_)) => (),
811 _ => panic!("Expected a status response"),
812 };
813 }
814
815 #[test]
816 fn connect_success_returns_true() {
817 let connect_result =
818 test_wpa2_connect("TestAp", "password", "TestAp", fidl_ieee80211::StatusCode::Success);
819 assert!(connect_result);
820 }
821
822 #[test]
823 fn connect_failed_returns_false() {
824 let connect_result = test_wpa2_connect(
825 "TestAp",
826 "password",
827 "",
828 fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
829 );
830 assert!(!connect_result);
831 }
832
833 #[test]
834 fn connect_different_ssid_returns_false() {
835 let connect_result = test_wpa2_connect(
836 "TestAp1",
837 "password",
838 "TestAp2",
839 fidl_ieee80211::StatusCode::Success,
840 );
841 assert!(!connect_result);
842 }
843
844 fn test_wpa2_connect(
845 target_ssid: &str,
846 password: &str,
847 connected_to_ssid: &str,
848 result_code: fidl_ieee80211::StatusCode,
849 ) -> bool {
850 let target_ssid = Ssid::try_from(target_ssid).unwrap();
851 let connected_to_ssid = Ssid::try_from(connected_to_ssid).unwrap();
852
853 let mut exec = TestExecutor::new();
854 let (client_sme, server) = create_client_sme_proxy();
855 let mut next_client_sme_req = server.into_future();
856
857 let target_password = password.as_bytes();
858 let target_bss_desc = generate_random_wpa2_bss_description();
859
860 let fut = connect(
861 &client_sme,
862 target_ssid.clone(),
863 target_password.to_vec(),
864 target_bss_desc.clone(),
865 );
866 let mut fut = pin!(fut);
867 assert!(exec.run_until_stalled(&mut fut).is_pending());
868
869 send_connect_request_response(
871 &mut exec,
872 &mut next_client_sme_req,
873 &target_ssid,
874 fidl_internal::Authentication::try_from(SecurityContext {
875 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
876 unparsed_credential_bytes: target_password.to_vec(),
877 })
878 .unwrap(),
879 result_code,
880 );
881
882 if result_code == fidl_ieee80211::StatusCode::Success {
884 assert!(exec.run_until_stalled(&mut fut).is_pending());
885 send_status_response(
886 &mut exec,
887 &mut next_client_sme_req,
888 Some(connected_to_ssid),
889 None,
890 );
891 }
892
893 let complete = exec.run_until_stalled(&mut fut);
894
895 let connection_result = match complete {
896 Poll::Ready(result) => result,
897 _ => panic!("Expected a connect response"),
898 };
899
900 let returned_bool = match connection_result {
901 Ok(response) => response,
902 _ => panic!("Expected a valid connection result"),
903 };
904
905 returned_bool
906 }
907
908 #[test]
909 fn connect_properly_passes_network_info_with_password() {
910 let mut exec = TestExecutor::new();
911 let (client_sme, server) = create_client_sme_proxy();
912 let mut next_client_sme_req = server.into_future();
913
914 let target_ssid = Ssid::try_from("TestAp").unwrap();
915 let target_password = "password".as_bytes();
916 let target_bss_desc = generate_random_wpa2_bss_description();
917
918 let fut = connect(
919 &client_sme,
920 target_ssid.clone(),
921 target_password.to_vec(),
922 target_bss_desc.clone(),
923 );
924 let mut fut = pin!(fut);
925 assert!(exec.run_until_stalled(&mut fut).is_pending());
926
927 verify_connect_request_info(
929 &mut exec,
930 &mut next_client_sme_req,
931 &target_ssid,
932 fidl_internal::Authentication::try_from(SecurityContext {
933 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
934 unparsed_credential_bytes: target_password.to_vec(),
935 })
936 .unwrap(),
937 target_bss_desc,
938 );
939 }
940
941 #[test]
942 fn connect_properly_passes_network_info_open() {
943 let mut exec = TestExecutor::new();
944 let (client_sme, server) = create_client_sme_proxy();
945 let mut next_client_sme_req = server.into_future();
946
947 let target_ssid = Ssid::try_from("TestAp").unwrap();
948 let target_password = "".as_bytes();
949 let target_bss_desc = fake_fidl_bss_description!(Open);
950
951 let fut = connect(
952 &client_sme,
953 target_ssid.clone(),
954 target_password.to_vec(),
955 target_bss_desc.clone(),
956 );
957 let mut fut = pin!(fut);
958 assert!(exec.run_until_stalled(&mut fut).is_pending());
959
960 verify_connect_request_info(
962 &mut exec,
963 &mut next_client_sme_req,
964 &target_ssid,
965 fidl_internal::Authentication::try_from(SecurityContext {
966 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
967 unparsed_credential_bytes: vec![],
968 })
969 .unwrap(),
970 target_bss_desc,
971 );
972 }
973
974 fn verify_connect_request_info(
975 exec: &mut TestExecutor,
976 server: &mut StreamFuture<ClientSmeRequestStream>,
977 expected_ssid: &Ssid,
978 expected_authentication: fidl_internal::Authentication,
979 expected_bss_desc: fidl_ieee80211::BssDescription,
980 ) {
981 match poll_client_sme_request(exec, server) {
982 Poll::Ready(ClientSmeRequest::Connect { req, .. }) => {
983 assert_eq!(expected_ssid, &req.ssid);
984 assert_eq!(req.authentication, expected_authentication);
985 assert_eq!(req.bss_description, expected_bss_desc);
986 }
987 _ => panic!("expected a Connect request"),
988 }
989 }
990
991 fn send_connect_request_response(
992 exec: &mut TestExecutor,
993 server: &mut StreamFuture<ClientSmeRequestStream>,
994 expected_ssid: &Ssid,
995 expected_authentication: fidl_internal::Authentication,
996 connect_result: fidl_ieee80211::StatusCode,
997 ) {
998 let responder = match poll_client_sme_request(exec, server) {
999 Poll::Ready(ClientSmeRequest::Connect { req, txn, .. }) => {
1000 assert_eq!(expected_ssid, &req.ssid[..]);
1001 assert_eq!(req.authentication, expected_authentication);
1002 txn.expect("expected a Connect transaction channel")
1003 }
1004 Poll::Pending => panic!("expected a request to be available"),
1005 _ => panic!("expected a Connect request"),
1006 };
1007 let connect_transaction = responder.into_stream().control_handle();
1008 connect_transaction
1009 .send_on_connect_result(&fidl_sme::ConnectResult {
1010 code: connect_result,
1011 is_credential_rejected: false,
1012 is_reconnect: false,
1013 })
1014 .expect("failed to send OnConnectResult to ConnectTransaction");
1015 }
1016
1017 fn poll_client_sme_request(
1018 exec: &mut TestExecutor,
1019 next_client_sme_req: &mut StreamFuture<ClientSmeRequestStream>,
1020 ) -> Poll<ClientSmeRequest> {
1021 exec.run_until_stalled(next_client_sme_req).map(|(req, stream)| {
1022 *next_client_sme_req = stream.into_future();
1023 req.expect("did not expect the ClientSmeRequestStream to end")
1024 .expect("error polling client sme request stream")
1025 })
1026 }
1027
1028 fn create_client_sme_proxy() -> (fidl_sme::ClientSmeProxy, ClientSmeRequestStream) {
1029 let (proxy, server) = endpoints::create_proxy::<ClientSmeMarker>();
1030 let server = server.into_stream();
1031 (proxy, server)
1032 }
1033
1034 fn create_wlan_monitor_util() -> (DeviceMonitorProxy, DeviceMonitorRequestStream) {
1035 let (proxy, server) = endpoints::create_proxy::<DeviceMonitorMarker>();
1036 let server = server.into_stream();
1037 (proxy, server)
1038 }
1039
1040 enum StatusResponse {
1041 Idle,
1042 Connected,
1043 Connecting,
1044 }
1045
1046 #[test]
1047 fn disconnect_with_empty_status_response() {
1048 if let Poll::Ready(result) = test_disconnect(StatusResponse::Idle) {
1049 return assert!(result.is_ok());
1050 }
1051 panic!("disconnect did not return a Poll::Ready")
1052 }
1053
1054 #[test]
1055 fn disconnect_fail_because_connected() {
1056 if let Poll::Ready(result) = test_disconnect(StatusResponse::Connected) {
1057 return assert!(result.is_err());
1058 }
1059 panic!("disconnect did not return a Poll::Ready")
1060 }
1061
1062 #[test]
1063 fn disconnect_fail_because_connecting() {
1064 if let Poll::Ready(result) = test_disconnect(StatusResponse::Connecting) {
1065 return assert!(result.is_err());
1066 }
1067 panic!("disconnect did not return a Poll::Ready")
1068 }
1069
1070 fn test_disconnect(status: StatusResponse) -> Poll<Result<(), Error>> {
1071 let mut exec = TestExecutor::new();
1072 let (client_sme, server) = create_client_sme_proxy();
1073 let mut client_sme_req = server.into_future();
1074
1075 let fut = disconnect(&client_sme);
1076 let mut fut = pin!(fut);
1077 assert!(exec.run_until_stalled(&mut fut).is_pending());
1078
1079 send_disconnect_request_response(&mut exec, &mut client_sme_req);
1080
1081 assert!(exec.run_until_stalled(&mut fut).is_pending());
1082
1083 match status {
1084 StatusResponse::Idle => {
1085 send_status_response(&mut exec, &mut client_sme_req, None, None)
1086 }
1087 StatusResponse::Connected => send_status_response(
1088 &mut exec,
1089 &mut client_sme_req,
1090 Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1091 None,
1092 ),
1093 StatusResponse::Connecting => send_status_response(
1094 &mut exec,
1095 &mut client_sme_req,
1096 None,
1097 Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1098 ),
1099 }
1100
1101 exec.run_until_stalled(&mut fut)
1102 }
1103
1104 fn send_disconnect_request_response(
1105 exec: &mut TestExecutor,
1106 server: &mut StreamFuture<ClientSmeRequestStream>,
1107 ) {
1108 let rsp = match poll_client_sme_request(exec, server) {
1109 Poll::Ready(ClientSmeRequest::Disconnect { responder, .. }) => responder,
1110 Poll::Pending => panic!("Expected a DisconnectRequest"),
1111 _ => panic!("Expected a DisconnectRequest"),
1112 };
1113 rsp.send().expect("Failed to send DisconnectResponse.");
1114 }
1115
1116 fn create_serving_ap_info_using_ssid(ssid: Ssid) -> fidl_sme::ServingApInfo {
1117 fidl_sme::ServingApInfo {
1118 bssid: [0, 1, 2, 3, 4, 5],
1119 ssid: ssid.into(),
1120 rssi_dbm: -30,
1121 snr_db: 10,
1122 primary: fidl_ieee80211::ChannelNumber {
1123 number: 1,
1124 band: fidl_ieee80211::WlanBand::TwoGhz,
1125 },
1126 protection: Protection::Wpa2Personal,
1127 bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
1128 vht_secondary_80_channel: fidl_ieee80211::ChannelNumber {
1129 number: 0,
1130 band: fidl_ieee80211::WlanBand::TwoGhz,
1131 },
1132 }
1133 }
1134
1135 fn send_status_response(
1136 exec: &mut TestExecutor,
1137 server: &mut StreamFuture<ClientSmeRequestStream>,
1138 connected_to_ssid: Option<Ssid>,
1139 connecting_to_ssid: Option<Ssid>,
1140 ) {
1141 let rsp = match poll_client_sme_request(exec, server) {
1142 Poll::Ready(ClientSmeRequest::Status { responder }) => responder,
1143 Poll::Pending => panic!("Expected a StatusRequest"),
1144 _ => panic!("Expected a StatusRequest"),
1145 };
1146
1147 let response = match (connected_to_ssid, connecting_to_ssid) {
1148 (Some(_), Some(_)) => panic!("SME cannot simultaneously be Connected and Connecting."),
1149 (Some(ssid), None) => {
1150 let serving_ap_info = create_serving_ap_info_using_ssid(ssid);
1151 fidl_sme::ClientStatusResponse::Connected(serving_ap_info)
1152 }
1153 (None, Some(ssid)) => fidl_sme::ClientStatusResponse::Connecting(ssid.to_vec()),
1154 (None, None) => fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {}),
1155 };
1156
1157 rsp.send(&response).expect("Failed to send StatusResponse.");
1158 }
1159
1160 #[test]
1161 fn scan_success_returns_empty_results() {
1162 assert_eq!(test_scan(&[]), &[]);
1163 }
1164
1165 #[test]
1166 fn scan_success_returns_results() {
1167 let scan_results = &[
1168 create_scan_result(
1169 [0, 1, 2, 3, 4, 5],
1170 Ssid::try_from("foo").unwrap(),
1171 -30,
1172 20,
1173 Channel::new(1, Cbw::Cbw20, fidl_ieee80211::WlanBand::TwoGhz),
1174 Protection::Wpa2Personal,
1175 Some(fidl_sme::Compatibility::Compatible(fidl_sme::Compatible {
1176 mutual_security_protocols: vec![fidl_internal::Protocol::Wpa2Personal],
1177 })),
1178 ),
1179 create_scan_result(
1180 [1, 2, 3, 4, 5, 6],
1181 Ssid::try_from("hello").unwrap(),
1182 -60,
1183 10,
1184 Channel::new(2, Cbw::Cbw20, fidl_ieee80211::WlanBand::TwoGhz),
1185 Protection::Wpa2Personal,
1186 None,
1187 ),
1188 ];
1189
1190 assert_eq!(test_scan(scan_results), scan_results);
1191 }
1192
1193 #[test]
1194 fn scan_error_correctly_handled() {
1195 assert!(test_scan_error().is_err())
1197 }
1198
1199 fn test_scan(scan_results: &[fidl_sme::ScanResult]) -> Vec<fidl_sme::ScanResult> {
1200 let mut exec = TestExecutor::new();
1201 let (client_sme, server) = create_client_sme_proxy();
1202 let mut client_sme_req = server.into_future();
1203
1204 let fut = passive_scan(&client_sme);
1205 let mut fut = pin!(fut);
1206 assert!(exec.run_until_stalled(&mut fut).is_pending());
1207
1208 send_scan_result_response(&mut exec, &mut client_sme_req, scan_results);
1209
1210 let complete = exec.run_until_stalled(&mut fut);
1211 let request_result = match complete {
1212 Poll::Ready(result) => result,
1213 _ => panic!("Expected a scan request result"),
1214 };
1215 let returned_scan_results = request_result.expect("failed to get scan results");
1216
1217 returned_scan_results
1218 }
1219
1220 fn send_scan_result_response(
1221 exec: &mut TestExecutor,
1222 server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1223 scan_results: &[fidl_sme::ScanResult],
1224 ) {
1225 match poll_client_sme_request(exec, server) {
1226 Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => {
1227 let vmo = write_vmo(scan_results.to_vec()).expect("failed to write VMO");
1228 responder.send(Ok(vmo)).expect("failed to send scan results")
1229 }
1230 Poll::Pending => panic!("expected a request to be available"),
1231 _ => panic!("expected a scan request"),
1232 }
1233 }
1234
1235 fn test_scan_error() -> Result<(), Error> {
1236 let mut exec = TestExecutor::new();
1237 let (client_sme, server) = create_client_sme_proxy();
1238 let mut client_sme_req = server.into_future();
1239
1240 let fut = passive_scan(&client_sme);
1241 let mut fut = pin!(fut);
1242 assert!(exec.run_until_stalled(&mut fut).is_pending());
1243
1244 send_scan_error_response(&mut exec, &mut client_sme_req);
1245 let _ = exec.run_until_stalled(&mut fut)?;
1246 Ok(())
1247 }
1248
1249 fn send_scan_error_response(
1250 exec: &mut TestExecutor,
1251 server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1252 ) {
1253 match poll_client_sme_request(exec, server) {
1254 Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => responder
1255 .send(Err(fidl_sme::ScanErrorCode::InternalError))
1256 .expect("failed to send ScanError"),
1257 Poll::Pending => panic!("expected a request to be available"),
1258 _ => panic!("expected a scan request"),
1259 };
1260 }
1261
1262 fn create_scan_result(
1263 bssid: [u8; 6],
1264 ssid: Ssid,
1265 rssi_dbm: i8,
1266 snr_db: i8,
1267 channel: Channel,
1268 protection: Protection,
1269 compatibility: Option<fidl_sme::Compatibility>,
1270 ) -> fidl_sme::ScanResult {
1271 fidl_sme::ScanResult {
1272 compatibility: compatibility.unwrap_or(fidl_sme::Compatibility::Compatible(
1273 fidl_sme::Compatible { mutual_security_protocols: vec![] },
1274 )),
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}