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