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