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