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