1use crate::event::action::{self, AuthenticationControl, AuthenticationTap};
6use crate::event::{Handler, branch};
7use fidl::endpoints::{create_endpoints, create_proxy};
8use fidl_fuchsia_wlan_common::WlanMacRole;
9use fidl_fuchsia_wlan_tap::{WlanRxInfo, WlantapPhyConfig, WlantapPhyProxy};
10use fuchsia_component::client::connect_to_protocol_at;
11use ieee80211::{Bssid, MacAddr, Ssid};
12use std::future::Future;
13use std::pin::pin;
14use std::sync::LazyLock;
15use wlan_common::bss::Protection;
16use wlan_common::channel::{Cbw, Channel};
17use wlan_common::ie::rsn::cipher::{CIPHER_CCMP_128, CIPHER_TKIP, Cipher};
18use wlan_common::ie::rsn::rsne;
19use wlan_common::ie::rsn::suite_filter::DEFAULT_GROUP_MGMT_CIPHER;
20use wlan_common::ie::wpa;
21use wlan_common::{TimeUnit, data_writer, mac, mgmt_writer};
22use wlan_frame_writer::write_frame_to_vec;
23use wlan_rsn::rsna::UpdateSink;
24use {
25 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
26 fidl_fuchsia_wlan_mlme as fidl_mlme, fidl_fuchsia_wlan_policy as fidl_policy,
27 fidl_fuchsia_wlan_softmac as fidl_wlan_softmac,
28};
29
30pub mod event;
31pub mod netdevice_helper;
32pub mod test_utils;
33
34pub use wlancfg_helper::*;
35
36mod config;
37mod wlancfg_helper;
38
39pub const PSK_STR_LEN: usize = 64;
40
41pub static CLIENT_MAC_ADDR: LazyLock<MacAddr> =
42 LazyLock::new(|| [0x67, 0x62, 0x6f, 0x6e, 0x69, 0x6b].into());
43pub static AP_MAC_ADDR: LazyLock<Bssid> =
44 LazyLock::new(|| [0x70, 0xf1, 0x1c, 0x05, 0x2d, 0x7f].into());
45pub static AP_SSID: LazyLock<Ssid> = LazyLock::new(|| Ssid::try_from("ap_ssid").unwrap());
46pub static ETH_DST_MAC: LazyLock<MacAddr> =
47 LazyLock::new(|| [0x65, 0x74, 0x68, 0x64, 0x73, 0x74].into());
48
49pub const WLANCFG_DEFAULT_AP_CHANNEL: Channel = Channel { primary: 11, cbw: Cbw::Cbw20 };
50
51pub static ARTIFICIAL_SCAN_SLEEP: LazyLock<zx::MonotonicDuration> =
55 LazyLock::new(|| zx::MonotonicDuration::from_seconds(2));
56
57pub static SCAN_RESPONSE_TEST_TIMEOUT: LazyLock<zx::MonotonicDuration> =
61 LazyLock::new(|| zx::MonotonicDuration::from_seconds(70));
62
63pub struct Supplicant<'a> {
67 pub controller: &'a fidl_policy::ClientControllerProxy,
68 pub state_update_stream: &'a mut fidl_policy::ClientStateUpdatesRequestStream,
69 pub security_type: fidl_policy::SecurityType,
70 pub password: Option<&'a str>,
71}
72
73impl<'a> Supplicant<'a> {
74 pub fn reborrow(&mut self) -> Supplicant<'_> {
87 Supplicant {
88 controller: self.controller,
89 state_update_stream: &mut *self.state_update_stream,
90 security_type: self.security_type,
91 password: self.password,
92 }
93 }
94}
95
96pub fn default_wlantap_config_client() -> WlantapPhyConfig {
97 wlantap_config_client(format!("wlantap-client"), *CLIENT_MAC_ADDR)
98}
99
100pub fn wlantap_config_client(name: String, mac_addr: MacAddr) -> WlantapPhyConfig {
101 config::create_wlantap_config(name, mac_addr, WlanMacRole::Client)
102}
103
104pub fn default_wlantap_config_ap() -> WlantapPhyConfig {
105 wlantap_config_ap(format!("wlantap-ap"), (*AP_MAC_ADDR).into())
106}
107
108pub fn wlantap_config_ap(name: String, mac_addr: MacAddr) -> WlantapPhyConfig {
109 config::create_wlantap_config(name, mac_addr, WlanMacRole::Ap)
110}
111
112pub fn rx_info_with_default_ap() -> WlanRxInfo {
113 rx_info_with_valid_rssi(&WLANCFG_DEFAULT_AP_CHANNEL, 0)
114}
115
116fn rx_info_with_valid_rssi(channel: &Channel, rssi_dbm: i8) -> WlanRxInfo {
117 WlanRxInfo {
118 rx_flags: 0,
119 valid_fields: if rssi_dbm == 0 {
120 0
121 } else {
122 fidl_wlan_softmac::WlanRxInfoValid::RSSI.bits()
123 },
124 phy: fidl_common::WlanPhyType::Dsss,
125 data_rate: 0,
126 channel: fidl_ieee80211::WlanChannel::from(channel),
127 mcs: 0,
128 rssi_dbm,
129 snr_dbh: 0,
130 }
131}
132
133pub fn send_sae_authentication_frame(
134 sae_frame: &fidl_mlme::SaeFrame,
135 channel: &Channel,
136 bssid: &Bssid,
137 proxy: &WlantapPhyProxy,
138) -> Result<(), anyhow::Error> {
139 let buffer = write_frame_to_vec!({
140 headers: {
141 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
142 mac::FrameControl(0)
143 .with_frame_type(mac::FrameType::MGMT)
144 .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
145 *CLIENT_MAC_ADDR,
146 bssid.clone().into(),
147 mac::SequenceControl(0).with_seq_num(123),
148 ),
149 mac::AuthHdr: &mac::AuthHdr {
150 auth_alg_num: mac::AuthAlgorithmNumber::SAE,
151 auth_txn_seq_num: sae_frame.seq_num,
152 status_code: sae_frame.status_code.into(),
153 },
154 },
155 body: &sae_frame.sae_fields[..],
156 })?;
157 proxy.rx(&buffer, &rx_info_with_valid_rssi(channel, 0))?;
158 Ok(())
159}
160
161pub fn send_open_authentication(
162 channel: &Channel,
163 bssid: &Bssid,
164 status_code: impl Into<mac::StatusCode>,
165 proxy: &WlantapPhyProxy,
166) -> Result<(), anyhow::Error> {
167 let buffer = write_frame_to_vec!({
168 headers: {
169 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
170 mac::FrameControl(0)
171 .with_frame_type(mac::FrameType::MGMT)
172 .with_mgmt_subtype(mac::MgmtSubtype::AUTH),
173 *CLIENT_MAC_ADDR,
174 bssid.clone().into(),
175 mac::SequenceControl(0).with_seq_num(123),
176 ),
177 mac::AuthHdr: &mac::AuthHdr {
178 auth_alg_num: mac::AuthAlgorithmNumber::OPEN,
179 auth_txn_seq_num: 2,
180 status_code: status_code.into(),
181 },
182 },
183 })?;
184 proxy.rx(&buffer, &rx_info_with_valid_rssi(channel, 0))?;
185 Ok(())
186}
187
188pub fn send_association_response(
189 channel: &Channel,
190 bssid: &Bssid,
191 status_code: impl Into<mac::StatusCode>,
192 proxy: &WlantapPhyProxy,
193) -> Result<(), anyhow::Error> {
194 let buffer = write_frame_to_vec!({
195 headers: {
196 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
197 mac::FrameControl(0)
198 .with_frame_type(mac::FrameType::MGMT)
199 .with_mgmt_subtype(mac::MgmtSubtype::ASSOC_RESP),
200 *CLIENT_MAC_ADDR,
201 bssid.clone().into(),
202 mac::SequenceControl(0).with_seq_num(123),
203 ),
204 mac::AssocRespHdr: &mac::AssocRespHdr {
205 capabilities: mac::CapabilityInfo(0).with_ess(true).with_short_preamble(true),
206 status_code: status_code.into(),
207 aid: 2, },
209 },
210 ies: {
211 supported_rates: &[0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24],
215 extended_supported_rates: &[48, 72, 128 + 96, 108],
217 },
218 })?;
219 proxy.rx(&buffer, &rx_info_with_valid_rssi(channel, 0))?;
220 Ok(())
221}
222
223pub fn send_disassociate(
224 channel: &Channel,
225 bssid: &Bssid,
226 reason_code: impl Into<mac::ReasonCode>,
227 proxy: &WlantapPhyProxy,
228) -> Result<(), anyhow::Error> {
229 let buffer = write_frame_to_vec!({
230 headers: {
231 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
232 mac::FrameControl(0)
233 .with_frame_type(mac::FrameType::MGMT)
234 .with_mgmt_subtype(mac::MgmtSubtype::DISASSOC),
235 *CLIENT_MAC_ADDR,
236 bssid.clone().into(),
237 mac::SequenceControl(0).with_seq_num(123),
238 ),
239 mac::DisassocHdr: &mac::DisassocHdr {
240 reason_code: reason_code.into(),
241 },
242 },
243 })?;
244 proxy.rx(&buffer, &rx_info_with_valid_rssi(channel, 0))?;
245 Ok(())
246}
247
248pub fn password_or_psk_to_policy_credential<S: ToString>(
249 password_or_psk: Option<S>,
250) -> fidl_policy::Credential {
251 return match password_or_psk {
252 None => fidl_policy::Credential::None(fidl_policy::Empty),
253 Some(p) => {
254 let p = p.to_string().as_bytes().to_vec();
255 if p.len() == PSK_STR_LEN {
256 let psk = hex::decode(p).expect("Failed to decode psk");
258 fidl_policy::Credential::Psk(psk)
259 } else {
260 fidl_policy::Credential::Password(p)
261 }
262 }
263 };
264}
265
266pub fn create_authenticator(
267 bssid: &Bssid,
268 ssid: &Ssid,
269 password_or_psk: &str,
270 gtk_cipher: Cipher,
272 advertised_protection: Protection,
274 supplicant_protection: Protection,
276) -> wlan_rsn::Authenticator {
277 let nonce_rdr =
278 wlan_rsn::nonce::NonceReader::new(&bssid.clone().into()).expect("creating nonce reader");
279 let gtk_provider = wlan_rsn::GtkProvider::new(gtk_cipher, 1, 0).expect("creating gtk provider");
280
281 let advertised_protection_info = match advertised_protection {
282 Protection::Wpa3Personal => wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa3_rsne()),
283 Protection::Wpa2Wpa3Personal => {
284 wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa2_wpa3_rsne())
285 }
286 Protection::Wpa2Personal | Protection::Wpa1Wpa2Personal => wlan_rsn::ProtectionInfo::Rsne(
287 rsne::Rsne::wpa2_rsne_with_caps(rsne::RsnCapabilities(0)),
288 ),
289 Protection::Wpa2PersonalTkipOnly | Protection::Wpa1Wpa2PersonalTkipOnly => {
290 panic!("need tkip support")
291 }
292 Protection::Wpa1 => {
293 wlan_rsn::ProtectionInfo::LegacyWpa(wpa::fake_wpa_ies::fake_deprecated_wpa1_vendor_ie())
294 }
295 _ => {
296 panic!("{} not implemented", advertised_protection)
297 }
298 };
299
300 match supplicant_protection {
301 Protection::Wpa1 | Protection::Wpa2Personal => {
302 let psk = match password_or_psk.len() {
303 PSK_STR_LEN => {
304 hex::decode(password_or_psk).expect("Failed to decode psk").into_boxed_slice()
306 }
307 _ => {
308 wlan_rsn::psk::compute(password_or_psk.as_bytes(), ssid).expect("computing PSK")
309 }
310 };
311 let supplicant_protection_info = match supplicant_protection {
312 Protection::Wpa1 => wlan_rsn::ProtectionInfo::LegacyWpa(
313 wpa::fake_wpa_ies::fake_deprecated_wpa1_vendor_ie(),
314 ),
315 Protection::Wpa2Personal => wlan_rsn::ProtectionInfo::Rsne(
316 rsne::Rsne::wpa2_rsne_with_caps(rsne::RsnCapabilities(0)),
317 ),
318 _ => unreachable!("impossible combination in this nested match"),
319 };
320 wlan_rsn::Authenticator::new_wpa2psk_ccmp128(
321 nonce_rdr,
322 std::sync::Arc::new(fuchsia_sync::Mutex::new(gtk_provider)),
323 psk,
324 *CLIENT_MAC_ADDR,
325 supplicant_protection_info,
326 bssid.clone().into(),
327 advertised_protection_info,
328 )
329 .expect("creating authenticator")
330 }
331 Protection::Wpa3Personal => {
332 let igtk_provider = wlan_rsn::IgtkProvider::new(DEFAULT_GROUP_MGMT_CIPHER)
333 .expect("creating igtk provider");
334 let supplicant_protection_info =
335 wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa3_rsne());
336 wlan_rsn::Authenticator::new_wpa3(
337 nonce_rdr,
338 std::sync::Arc::new(fuchsia_sync::Mutex::new(gtk_provider)),
339 std::sync::Arc::new(fuchsia_sync::Mutex::new(igtk_provider)),
340 ssid.clone(),
341 password_or_psk.as_bytes().to_vec(),
342 *CLIENT_MAC_ADDR,
343 supplicant_protection_info,
344 bssid.clone().into(),
345 advertised_protection_info,
346 )
347 .expect("creating authenticator")
348 }
349 _ => {
350 panic!("Cannot create an authenticator for {}", supplicant_protection)
351 }
352 }
353}
354
355pub enum ApAdvertisementMode {
356 Beacon,
357 ProbeResponse,
358}
359
360pub trait ApAdvertisement {
361 fn mode(&self) -> ApAdvertisementMode;
362 fn channel(&self) -> &Channel;
363 fn bssid(&self) -> &Bssid;
364 fn ssid(&self) -> &Ssid;
365 fn protection(&self) -> &Protection;
366 fn rssi_dbm(&self) -> i8;
367 fn wsc_ie(&self) -> Option<&Vec<u8>>;
368
369 fn beacon_interval(&self) -> TimeUnit {
370 TimeUnit::DEFAULT_BEACON_INTERVAL * 20u16
371 }
372
373 fn capabilities(&self) -> mac::CapabilityInfo {
374 mac::CapabilityInfo(0)
375 .with_ess(true)
378 .with_ibss(false)
379 .with_privacy(*self.protection() != Protection::Open)
383 }
384
385 fn send(&self, phy: &WlantapPhyProxy) -> Result<(), anyhow::Error> {
386 let buffer = self.generate_frame()?;
387 phy.rx(&buffer, &rx_info_with_valid_rssi(&self.channel(), self.rssi_dbm()))?;
388 Ok(())
389 }
390
391 fn generate_frame(&self) -> Result<Vec<u8>, anyhow::Error> {
392 let mode = self.mode();
393 let protection = self.protection();
394 let beacon_header = match mode {
395 ApAdvertisementMode::Beacon => {
396 Some(mac::BeaconHdr::new(self.beacon_interval(), self.capabilities()))
397 }
398 _ => None,
399 };
400 let probe_response_header = match mode {
401 ApAdvertisementMode::ProbeResponse => {
402 Some(mac::ProbeRespHdr::new(self.beacon_interval(), self.capabilities()))
403 }
404 _ => None,
405 };
406
407 let buffer = write_frame_to_vec!({
408 headers: {
409 mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
410 mac::FrameControl(0)
411 .with_frame_type(mac::FrameType::MGMT)
412 .with_mgmt_subtype(match mode {
413 ApAdvertisementMode::Beacon => mac::MgmtSubtype::BEACON,
414 ApAdvertisementMode::ProbeResponse{..} => mac::MgmtSubtype::PROBE_RESP
415 }),
416 match mode {
417 ApAdvertisementMode::Beacon => ieee80211::BROADCAST_ADDR,
418 ApAdvertisementMode::ProbeResponse{..} => *CLIENT_MAC_ADDR
419 },
420 *self.bssid(),
421 mac::SequenceControl(0).with_seq_num(123),
422 ),
423 mac::BeaconHdr?: beacon_header,
424 mac::ProbeRespHdr?: probe_response_header,
425 },
426 ies: {
427 ssid: &self.ssid(),
428 supported_rates: &[0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0xe0, 0x6c],
429 extended_supported_rates: { },
430 dsss_param_set: &ie::DsssParamSet { current_channel: self.channel().primary },
431 rsne?: match protection {
432 Protection::Unknown => panic!("Cannot send beacon with unknown protection"),
433 Protection::Open | Protection::Wep | Protection::Wpa1 => None,
434 Protection::Wpa1Wpa2Personal | Protection::Wpa2Personal =>
435 Some(rsne::Rsne::wpa2_rsne_with_caps(rsne::RsnCapabilities(0))),
436 Protection::Wpa2Wpa3Personal => Some(rsne::Rsne::wpa2_wpa3_rsne()),
437 Protection::Wpa3Personal => Some(rsne::Rsne::wpa3_rsne()),
438 _ => panic!("unsupported fake beacon: {:?}", protection),
439 },
440 wpa1?: match protection {
441 Protection::Unknown => panic!("Cannot send beacon with unknown protection"),
442 Protection::Open | Protection::Wep => None,
443 Protection::Wpa1 | Protection::Wpa1Wpa2Personal => Some(wpa::fake_wpa_ies::fake_deprecated_wpa1_vendor_ie()),
444 Protection::Wpa2Personal | Protection::Wpa2Wpa3Personal | Protection::Wpa3Personal => None,
445 _ => panic!("unsupported fake beacon: {:?}", protection),
446 },
447 wsc?: self.wsc_ie()
448 },
449 })?;
450 Ok(buffer.into())
451 }
452}
453
454pub struct Beacon {
455 pub channel: Channel,
456 pub bssid: Bssid,
457 pub ssid: Ssid,
458 pub protection: Protection,
459 pub rssi_dbm: i8,
460}
461
462impl ApAdvertisement for Beacon {
463 fn mode(&self) -> ApAdvertisementMode {
464 ApAdvertisementMode::Beacon
465 }
466 fn channel(&self) -> &Channel {
467 &self.channel
468 }
469 fn bssid(&self) -> &Bssid {
470 &self.bssid
471 }
472 fn ssid(&self) -> &Ssid {
473 &self.ssid
474 }
475 fn protection(&self) -> &Protection {
476 &self.protection
477 }
478 fn rssi_dbm(&self) -> i8 {
479 self.rssi_dbm
480 }
481 fn wsc_ie(&self) -> Option<&Vec<u8>> {
482 None
483 }
484}
485
486pub struct ProbeResponse {
487 pub channel: Channel,
488 pub bssid: Bssid,
489 pub ssid: Ssid,
490 pub protection: Protection,
491 pub rssi_dbm: i8,
492 pub wsc_ie: Option<Vec<u8>>,
493}
494
495impl ApAdvertisement for ProbeResponse {
496 fn mode(&self) -> ApAdvertisementMode {
497 ApAdvertisementMode::ProbeResponse
498 }
499 fn channel(&self) -> &Channel {
500 &self.channel
501 }
502 fn bssid(&self) -> &Bssid {
503 &self.bssid
504 }
505 fn ssid(&self) -> &Ssid {
506 &self.ssid
507 }
508 fn protection(&self) -> &Protection {
509 &self.protection
510 }
511 fn rssi_dbm(&self) -> i8 {
512 self.rssi_dbm
513 }
514 fn wsc_ie(&self) -> Option<&Vec<u8>> {
515 self.wsc_ie.as_ref()
516 }
517}
518
519pub async fn save_network_and_wait_until_connected(
520 test_ns_prefix: &str,
521 ssid: &Ssid,
522 security_type: fidl_policy::SecurityType,
523 credential: fidl_policy::Credential,
524) -> (fidl_policy::ClientControllerProxy, fidl_policy::ClientStateUpdatesRequestStream) {
525 let (client_controller, mut client_state_update_stream) =
527 wlancfg_helper::init_client_controller(test_ns_prefix).await;
528
529 save_network(&client_controller, ssid, security_type, credential).await;
530
531 let id = fidl_policy::NetworkIdentifier { ssid: ssid.to_vec(), type_: security_type.clone() };
533 wait_until_client_state(&mut client_state_update_stream, |update| {
534 has_id_and_state(update, &id, fidl_policy::ConnectionState::Connected)
535 })
536 .await;
537
538 (client_controller, client_state_update_stream)
539}
540
541pub async fn connect_or_timeout_with<F>(
544 helper: &mut test_utils::TestHelper,
545 timeout: zx::MonotonicDuration,
546 ssid: &Ssid,
547 bssid: &Bssid,
548 protection: &Protection,
549 authenticator: Option<wlan_rsn::Authenticator>,
550 future: F,
551) -> F::Output
552where
553 F: Future + Unpin,
554{
555 let phy = helper.proxy();
556 let channel = Channel::new(1, Cbw::Cbw20);
557 let beacons = [Beacon {
558 channel,
559 bssid: bssid.clone(),
560 ssid: ssid.clone(),
561 protection: protection.clone(),
562 rssi_dbm: -30,
563 }];
564 let mut control = authenticator
565 .map(|authenticator| AuthenticationControl { updates: UpdateSink::new(), authenticator });
566 let connect = if let Some(ref mut control) = control {
567 let tap = AuthenticationTap { control, handler: action::authenticate_with_control_state() };
568 event::boxed(action::connect_with_authentication_tap(
569 &phy, ssid, bssid, &channel, protection, tap,
570 ))
571 } else {
572 event::boxed(action::connect_with_open_authentication(
573 &phy, ssid, bssid, &channel, protection,
574 ))
575 };
576 helper
577 .run_until_complete_or_timeout(
578 timeout,
579 format!(
580 "connecting to {} ({:02X?}) with {:?} protection",
581 ssid.to_string_not_redactable(),
582 bssid,
583 protection,
584 ),
585 branch::or((
586 event::on_scan(action::send_advertisements_and_scan_completion(&phy, beacons)),
587 event::on_transmit(connect),
588 ))
589 .expect("failed to connect client"),
590 future,
591 )
592 .await
593}
594
595pub async fn connect_or_timeout(
598 helper: &mut test_utils::TestHelper,
599 timeout: zx::MonotonicDuration,
600 ssid: &Ssid,
601 bssid: &Bssid,
602 bss_protection: &Protection,
603 password_or_psk: Option<&str>,
604 security_type: fidl_policy::SecurityType,
605) {
606 let authenticator = match bss_protection {
607 Protection::Wpa3Personal | Protection::Wpa2Wpa3Personal => {
608 password_or_psk.map(|password_or_psk| {
609 create_authenticator(
610 bssid,
611 ssid,
612 password_or_psk,
613 CIPHER_CCMP_128,
614 *bss_protection,
615 Protection::Wpa3Personal,
616 )
617 })
618 }
619 Protection::Wpa2Personal | Protection::Wpa1Wpa2Personal => {
620 password_or_psk.map(|password_or_psk| {
621 create_authenticator(
622 bssid,
623 ssid,
624 password_or_psk,
625 CIPHER_CCMP_128,
626 *bss_protection,
627 Protection::Wpa2Personal,
628 )
629 })
630 }
631 Protection::Wpa2PersonalTkipOnly | Protection::Wpa1Wpa2PersonalTkipOnly => {
632 panic!("Hardware simulator does not support WPA2-TKIP.")
633 }
634 Protection::Wpa1 => password_or_psk.map(|password_or_psk| {
635 create_authenticator(
636 bssid,
637 ssid,
638 password_or_psk,
639 CIPHER_TKIP,
640 *bss_protection,
641 Protection::Wpa1,
642 )
643 }),
644 Protection::Open => None,
645 _ => {
646 panic!("Unsupported WLAN protection: {}", bss_protection)
647 }
648 };
649
650 let credential = password_or_psk_to_policy_credential(password_or_psk);
651 let test_ns_prefix = helper.test_ns_prefix().to_string();
652 let connect = pin!(save_network_and_wait_until_connected(
653 &test_ns_prefix,
654 ssid,
655 security_type,
656 credential
657 ));
658 connect_or_timeout_with(helper, timeout, ssid, bssid, bss_protection, authenticator, connect)
659 .await;
660}
661
662pub fn rx_wlan_data_frame(
663 channel: &Channel,
664 addr1: &MacAddr,
665 addr2: &MacAddr,
666 addr3: &MacAddr,
667 payload: &[u8],
668 ether_type: u16,
669 phy: &WlantapPhyProxy,
670) -> Result<(), anyhow::Error> {
671 let buffer = write_frame_to_vec!({
672 headers: {
673 mac::FixedDataHdrFields: &mac::FixedDataHdrFields {
674 frame_ctrl: mac::FrameControl(0)
675 .with_frame_type(mac::FrameType::DATA)
676 .with_data_subtype(mac::DataSubtype(0))
677 .with_from_ds(true),
678 duration: 0,
679 addr1: *addr1,
680 addr2: *addr2,
681 addr3: *addr3,
682 seq_ctrl: mac::SequenceControl(0).with_seq_num(3),
683 },
684 mac::LlcHdr: &data_writer::make_snap_llc_hdr(ether_type),
685 },
686 payload: payload,
687 })?;
688
689 phy.rx(&buffer, &rx_info_with_valid_rssi(channel, 0))?;
690 Ok(())
691}
692
693pub async fn loop_until_iface_is_found(helper: &mut test_utils::TestHelper) {
694 let policy_provider =
696 connect_to_protocol_at::<fidl_policy::ClientProviderMarker>(helper.test_ns_prefix())
697 .expect("connecting to wlan policy");
698 let (client_controller, server_end) = create_proxy();
699 let (update_client_end, _update_server_end) = create_endpoints();
700 let () =
701 policy_provider.get_controller(server_end, update_client_end).expect("getting controller");
702
703 let mut retry = test_utils::RetryWithBackoff::infinite_with_max_interval(
708 zx::MonotonicDuration::from_seconds(10),
709 );
710 loop {
711 let (scan_proxy, server_end) = create_proxy();
712 client_controller.scan_for_networks(server_end).expect("requesting scan");
713
714 let fut = pin!(async move { scan_proxy.get_next().await.expect("getting scan results") });
715
716 let phy = helper.proxy();
717 match helper
718 .run_until_complete_or_timeout(
719 *SCAN_RESPONSE_TEST_TIMEOUT,
720 "receive a scan response",
721 event::on_scan(action::send_advertisements_and_scan_completion(
722 &phy,
723 [] as [Beacon; 0],
724 )),
725 fut,
726 )
727 .await
728 {
729 Err(_) => {
730 retry.sleep_unless_after_deadline().await.unwrap_or_else(|_| {
731 panic!("Wlanstack did not recognize the interface in time")
732 });
733 }
734 Ok(_) => return,
735 }
736 }
737}