1use crate::channel::Channel;
6use crate::ie::owe_transition::{OweTransition, parse_owe_transition};
7use crate::ie::rsn::suite_filter;
8use crate::ie::wsc::{ProbeRespWsc, parse_probe_resp_wsc};
9use crate::ie::{self, IeType};
10use crate::mac::CapabilityInfo;
11use anyhow::format_err;
12use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
13use fidl_fuchsia_wlan_sme as fidl_sme;
14use ieee80211::{Bssid, MacAddrBytes, Ssid};
15use static_assertions::assert_eq_size;
16use std::cmp::Ordering;
17use std::collections::HashMap;
18use std::fmt;
19use std::hash::Hash;
20use std::ops::Range;
21use zerocopy::{IntoBytes, Ref};
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
24pub enum Protection {
25 Unknown,
26 Open,
27 OpenOweTransition,
28 Owe,
29 Wep,
30 Wpa1,
31 Wpa1Wpa2PersonalTkipOnly,
32 Wpa2PersonalTkipOnly,
33 Wpa1Wpa2Personal,
34 Wpa2Personal,
35 Wpa2Wpa3Personal,
36 Wpa3Personal,
37 Wpa2Enterprise,
38 Wpa3Enterprise,
42}
43
44impl From<Protection> for fidl_sme::Protection {
45 fn from(protection: Protection) -> fidl_sme::Protection {
46 match protection {
47 Protection::Unknown => fidl_sme::Protection::Unknown,
48 Protection::Open => fidl_sme::Protection::Open,
49 Protection::OpenOweTransition => fidl_sme::Protection::OpenOweTransition,
50 Protection::Owe => fidl_sme::Protection::Owe,
51 Protection::Wep => fidl_sme::Protection::Wep,
52 Protection::Wpa1 => fidl_sme::Protection::Wpa1,
53 Protection::Wpa1Wpa2PersonalTkipOnly => fidl_sme::Protection::Wpa1Wpa2PersonalTkipOnly,
54 Protection::Wpa2PersonalTkipOnly => fidl_sme::Protection::Wpa2PersonalTkipOnly,
55 Protection::Wpa1Wpa2Personal => fidl_sme::Protection::Wpa1Wpa2Personal,
56 Protection::Wpa2Personal => fidl_sme::Protection::Wpa2Personal,
57 Protection::Wpa2Wpa3Personal => fidl_sme::Protection::Wpa2Wpa3Personal,
58 Protection::Wpa3Personal => fidl_sme::Protection::Wpa3Personal,
59 Protection::Wpa2Enterprise => fidl_sme::Protection::Wpa2Enterprise,
60 Protection::Wpa3Enterprise => fidl_sme::Protection::Wpa3Enterprise,
61 }
62 }
63}
64
65impl From<fidl_sme::Protection> for Protection {
66 fn from(protection: fidl_sme::Protection) -> Self {
67 match protection {
68 fidl_sme::Protection::Unknown => Protection::Unknown,
69 fidl_sme::Protection::Open => Protection::Open,
70 fidl_sme::Protection::OpenOweTransition => Protection::OpenOweTransition,
71 fidl_sme::Protection::Owe => Protection::Owe,
72 fidl_sme::Protection::Wep => Protection::Wep,
73 fidl_sme::Protection::Wpa1 => Protection::Wpa1,
74 fidl_sme::Protection::Wpa1Wpa2PersonalTkipOnly => Protection::Wpa1Wpa2PersonalTkipOnly,
75 fidl_sme::Protection::Wpa2PersonalTkipOnly => Protection::Wpa2PersonalTkipOnly,
76 fidl_sme::Protection::Wpa1Wpa2Personal => Protection::Wpa1Wpa2Personal,
77 fidl_sme::Protection::Wpa2Personal => Protection::Wpa2Personal,
78 fidl_sme::Protection::Wpa2Wpa3Personal => Protection::Wpa2Wpa3Personal,
79 fidl_sme::Protection::Wpa3Personal => Protection::Wpa3Personal,
80 fidl_sme::Protection::Wpa2Enterprise => Protection::Wpa2Enterprise,
81 fidl_sme::Protection::Wpa3Enterprise => Protection::Wpa3Enterprise,
82 }
83 }
84}
85
86impl fmt::Display for Protection {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 match self {
89 Protection::Unknown => write!(f, "{}", "Unknown"),
90 Protection::Open => write!(f, "{}", "Open"),
91 Protection::OpenOweTransition => write!(f, "{}", "Open OWE Transition"),
92 Protection::Owe => write!(f, "{}", "OWE"),
93 Protection::Wep => write!(f, "{}", "WEP"),
94 Protection::Wpa1 => write!(f, "{}", "WPA1"),
95 Protection::Wpa1Wpa2PersonalTkipOnly => write!(f, "{}", "WPA1/2 PSK TKIP"),
96 Protection::Wpa2PersonalTkipOnly => write!(f, "{}", "WPA2 PSK TKIP"),
97 Protection::Wpa1Wpa2Personal => write!(f, "{}", "WPA1/2 PSK"),
98 Protection::Wpa2Personal => write!(f, "{}", "WPA2 PSK"),
99 Protection::Wpa2Wpa3Personal => write!(f, "{}", "WPA2/3 PSK"),
100 Protection::Wpa3Personal => write!(f, "{}", "WPA3 PSK"),
101 Protection::Wpa2Enterprise => write!(f, "{}", "WPA2 802.1X"),
102 Protection::Wpa3Enterprise => write!(f, "{}", "WPA3 802.1X"),
103 }
104 }
105}
106
107#[derive(Clone, Debug, Eq, Hash, PartialEq)]
108pub enum Standard {
109 Dot11A,
110 Dot11B,
111 Dot11G,
112 Dot11N,
113 Dot11Ac,
114}
115
116#[derive(Debug, Clone, PartialEq)]
117pub struct BssDescription {
118 pub ssid: Ssid,
120 pub bssid: Bssid,
121 pub bss_type: fidl_ieee80211::BssType,
122 pub beacon_period: u16,
123 pub capability_info: u16,
124 pub channel: Channel,
125 pub rssi_dbm: i8,
126 pub snr_db: i8,
127 ies: Vec<u8>,
129
130 rates: Vec<ie::SupportedRate>,
136 tim_range: Option<Range<usize>>,
137 country_range: Option<Range<usize>>,
138 rsne_range: Option<Range<usize>>,
139 ht_cap_range: Option<Range<usize>>,
140 ht_op_range: Option<Range<usize>>,
141 rm_enabled_cap_range: Option<Range<usize>>,
142 ext_cap_range: Option<Range<usize>>,
143 vht_cap_range: Option<Range<usize>>,
144 vht_op_range: Option<Range<usize>>,
145 rsnxe_range: Option<Range<usize>>,
146 owe_transition_range: Option<Range<usize>>,
147}
148
149impl BssDescription {
150 pub fn rates(&self) -> &[ie::SupportedRate] {
151 &self.rates[..]
152 }
153
154 pub fn dtim_period(&self) -> u8 {
155 self.tim_range
156 .as_ref()
157 .map(|range|
158 ie::parse_tim(&self.ies[range.clone()]).unwrap().header.dtim_period)
160 .unwrap_or(0)
161 }
162
163 pub fn country(&self) -> Option<&[u8]> {
164 self.country_range.as_ref().map(|range| &self.ies[range.clone()])
165 }
166
167 pub fn rsne(&self) -> Option<&[u8]> {
168 self.rsne_range.as_ref().map(|range| &self.ies[range.clone()])
169 }
170
171 pub fn ht_cap(&self) -> Option<Ref<&[u8], ie::HtCapabilities>> {
172 self.ht_cap_range.clone().map(|range| {
173 ie::parse_ht_capabilities(&self.ies[range]).unwrap()
175 })
176 }
177
178 pub fn raw_ht_cap(&self) -> Option<fidl_ieee80211::HtCapabilities> {
179 type HtCapArray = [u8; fidl_ieee80211::HT_CAP_LEN as usize];
180 self.ht_cap().map(|ht_cap| {
181 assert_eq_size!(ie::HtCapabilities, HtCapArray);
182 let bytes: HtCapArray = ht_cap.as_bytes().try_into().unwrap();
183 fidl_ieee80211::HtCapabilities { bytes }
184 })
185 }
186
187 pub fn ht_op(&self) -> Option<Ref<&[u8], ie::HtOperation>> {
188 self.ht_op_range.clone().map(|range| {
189 ie::parse_ht_operation(&self.ies[range]).unwrap()
191 })
192 }
193
194 pub fn rm_enabled_cap(&self) -> Option<Ref<&[u8], ie::RmEnabledCapabilities>> {
195 self.rm_enabled_cap_range.clone().map(|range| {
196 ie::parse_rm_enabled_capabilities(&self.ies[range]).unwrap()
198 })
199 }
200
201 pub fn ext_cap(&self) -> Option<ie::ExtCapabilitiesView<&[u8]>> {
202 self.ext_cap_range.clone().map(|range| ie::parse_ext_capabilities(&self.ies[range]))
203 }
204
205 pub fn raw_ht_op(&self) -> Option<fidl_ieee80211::HtOperation> {
206 type HtOpArray = [u8; fidl_ieee80211::HT_OP_LEN as usize];
207 self.ht_op().map(|ht_op| {
208 assert_eq_size!(ie::HtOperation, HtOpArray);
209 let bytes: HtOpArray = ht_op.as_bytes().try_into().unwrap();
210 fidl_ieee80211::HtOperation { bytes }
211 })
212 }
213
214 pub fn vht_cap(&self) -> Option<Ref<&[u8], ie::VhtCapabilities>> {
215 self.vht_cap_range.clone().map(|range| {
216 ie::parse_vht_capabilities(&self.ies[range]).unwrap()
218 })
219 }
220
221 pub fn raw_vht_cap(&self) -> Option<fidl_ieee80211::VhtCapabilities> {
222 type VhtCapArray = [u8; fidl_ieee80211::VHT_CAP_LEN as usize];
223 self.vht_cap().map(|vht_cap| {
224 assert_eq_size!(ie::VhtCapabilities, VhtCapArray);
225 let bytes: VhtCapArray = vht_cap.as_bytes().try_into().unwrap();
226 fidl_ieee80211::VhtCapabilities { bytes }
227 })
228 }
229
230 pub fn vht_op(&self) -> Option<Ref<&[u8], ie::VhtOperation>> {
231 self.vht_op_range.clone().map(|range| {
232 ie::parse_vht_operation(&self.ies[range]).unwrap()
234 })
235 }
236
237 pub fn raw_vht_op(&self) -> Option<fidl_ieee80211::VhtOperation> {
238 type VhtOpArray = [u8; fidl_ieee80211::VHT_OP_LEN as usize];
239 self.vht_op().map(|vht_op| {
240 assert_eq_size!(ie::VhtOperation, VhtOpArray);
241 let bytes: VhtOpArray = vht_op.as_bytes().try_into().unwrap();
242 fidl_ieee80211::VhtOperation { bytes }
243 })
244 }
245
246 pub fn rsnxe(&self) -> Option<ie::RsnxeView<&[u8]>> {
247 self.rsnxe_range.clone().map(|range| ie::parse_rsnxe(&self.ies[range]))
248 }
249
250 pub fn ies(&self) -> &[u8] {
251 &self.ies[..]
252 }
253
254 pub fn is_protected(&self) -> bool {
256 self.protection() != Protection::Open && self.protection() != Protection::OpenOweTransition
257 }
258
259 pub fn needs_eapol_exchange(&self) -> bool {
261 match self.protection() {
262 Protection::Unknown
263 | Protection::Open
264 | Protection::OpenOweTransition
265 | Protection::Wep => false,
266 _ => true,
267 }
268 }
269
270 pub fn protection(&self) -> Protection {
272 if !CapabilityInfo(self.capability_info).privacy() {
273 if self.owe_transition_range.is_some() {
274 return Protection::OpenOweTransition;
275 } else {
276 return Protection::Open;
277 }
278 }
279
280 let supports_wpa_1 = self
281 .wpa_ie()
282 .map(|wpa_ie| {
283 let rsne = ie::rsn::rsne::Rsne {
284 group_data_cipher_suite: Some(wpa_ie.multicast_cipher),
285 pairwise_cipher_suites: wpa_ie.unicast_cipher_list,
286 akm_suites: wpa_ie.akm_list,
287 ..Default::default()
288 };
289 suite_filter::WPA1_PERSONAL.is_satisfied(&rsne)
290 })
291 .unwrap_or(false);
292
293 let rsne = match self.rsne() {
294 Some(rsne) => match ie::rsn::rsne::from_bytes(rsne) {
295 Ok((_, rsne)) => rsne,
296 Err(_e) => {
297 return Protection::Unknown;
298 }
299 },
300 None if self.find_wpa_ie().is_some() => {
301 if supports_wpa_1 {
302 return Protection::Wpa1;
303 } else {
304 return Protection::Unknown;
305 }
306 }
307 None => return Protection::Wep,
308 };
309
310 let rsn_caps = rsne.rsn_capabilities.as_ref().unwrap_or(&ie::rsn::rsne::RsnCapabilities(0));
311 let mfp_req = rsn_caps.mgmt_frame_protection_req();
312 let mfp_cap = rsn_caps.mgmt_frame_protection_cap();
313
314 if suite_filter::WPA3_PERSONAL.is_satisfied(&rsne) {
315 if suite_filter::WPA2_PERSONAL.is_satisfied(&rsne) {
316 if mfp_cap {
317 return Protection::Wpa2Wpa3Personal;
318 }
319 } else if mfp_cap && mfp_req {
320 return Protection::Wpa3Personal;
321 }
322 }
323 if suite_filter::WPA2_PERSONAL.is_satisfied(&rsne) {
324 if supports_wpa_1 {
325 return Protection::Wpa1Wpa2Personal;
326 } else {
327 return Protection::Wpa2Personal;
328 }
329 }
330 if suite_filter::WPA2_PERSONAL_TKIP_ONLY.is_satisfied(&rsne) {
331 if supports_wpa_1 {
332 return Protection::Wpa1Wpa2PersonalTkipOnly;
333 } else {
334 return Protection::Wpa2PersonalTkipOnly;
335 }
336 }
337 if supports_wpa_1 {
338 return Protection::Wpa1;
339 }
340 if suite_filter::WPA3_ENTERPRISE_192_BIT.is_satisfied(&rsne) {
341 if mfp_cap && mfp_req {
342 return Protection::Wpa3Enterprise;
343 }
344 }
345 if suite_filter::WPA2_ENTERPRISE.is_satisfied(&rsne) {
346 return Protection::Wpa2Enterprise;
347 }
348 if suite_filter::OWE.is_satisfied(&rsne) {
349 return Protection::Owe;
350 }
351 Protection::Unknown
352 }
353
354 pub fn latest_standard(&self) -> Standard {
356 if self.vht_cap().is_some() && self.vht_op().is_some() {
357 Standard::Dot11Ac
358 } else if self.ht_cap().is_some() && self.ht_op().is_some() {
359 Standard::Dot11N
360 } else if self.channel.band == fidl_ieee80211::WlanBand::TwoGhz {
361 if self.rates.iter().any(|r| match r.rate() {
362 12 | 18 | 24 | 36 | 48 | 72 | 96 | 108 => true,
363 _ => false,
364 }) {
365 Standard::Dot11G
366 } else {
367 Standard::Dot11B
368 }
369 } else {
370 Standard::Dot11A
371 }
372 }
373
374 pub fn find_wpa_ie(&self) -> Option<&[u8]> {
376 ie::Reader::new(&self.ies[..])
377 .filter_map(|(id, ie)| match id {
378 ie::Id::VENDOR_SPECIFIC => match ie::parse_vendor_ie(ie) {
379 Ok(ie::VendorIe::MsftLegacyWpa(body)) => Some(&body[..]),
380 _ => None,
381 },
382 _ => None,
383 })
384 .next()
385 }
386
387 pub fn wpa_ie(&self) -> Result<ie::wpa::WpaIe, anyhow::Error> {
390 ie::parse_wpa_ie(self.find_wpa_ie().ok_or_else(|| format_err!("no wpa ie found"))?)
391 .map_err(|e| e.into())
392 }
393
394 pub fn find_wmm_param(&self) -> Option<&[u8]> {
396 ie::Reader::new(&self.ies[..])
397 .filter_map(|(id, ie)| match id {
398 ie::Id::VENDOR_SPECIFIC => match ie::parse_vendor_ie(ie) {
399 Ok(ie::VendorIe::WmmParam(body)) => Some(&body[..]),
400 _ => None,
401 },
402 _ => None,
403 })
404 .next()
405 }
406
407 pub fn wmm_param(&self) -> Result<Ref<&[u8], ie::WmmParam>, anyhow::Error> {
410 ie::parse_wmm_param(
411 self.find_wmm_param().ok_or_else(|| format_err!("no wmm parameter found"))?,
412 )
413 .map_err(|e| e.into())
414 }
415
416 pub fn find_wsc_ie(&self) -> Option<&[u8]> {
418 ie::Reader::new(&self.ies[..])
419 .filter_map(|(id, ie)| match id {
420 ie::Id::VENDOR_SPECIFIC => match ie::parse_vendor_ie(ie) {
421 Ok(ie::VendorIe::Wsc(body)) => Some(&body[..]),
422 _ => None,
423 },
424 _ => None,
425 })
426 .next()
427 }
428
429 pub fn probe_resp_wsc(&self) -> Option<ProbeRespWsc> {
430 match self.find_wsc_ie() {
431 Some(ie) => match parse_probe_resp_wsc(ie) {
432 Ok(wsc) => Some(wsc),
433 Err(_) => None,
438 },
439 None => None,
440 }
441 }
442
443 pub fn owe_transition(&self) -> Option<OweTransition> {
444 self.owe_transition_range.clone().map(|range| {
445 parse_owe_transition(&self.ies[range]).unwrap()
447 })
448 }
449
450 pub fn supports_uapsd(&self) -> bool {
451 let wmm_info = ie::Reader::new(&self.ies[..])
452 .filter_map(|(id, ie)| match id {
453 ie::Id::VENDOR_SPECIFIC => match ie::parse_vendor_ie(ie) {
454 Ok(ie::VendorIe::WmmInfo(body)) => {
455 ie::parse_wmm_info(body).map(|wmm_info| *wmm_info).ok()
456 }
457 Ok(ie::VendorIe::WmmParam(body)) => {
458 ie::parse_wmm_param(body).map(|wmm_param| wmm_param.wmm_info).ok()
459 }
460 _ => None,
461 },
462 _ => None,
463 })
464 .next();
465 wmm_info.map(|wmm_info| wmm_info.ap_wmm_info().uapsd()).unwrap_or(false)
466 }
467
468 pub fn supports_ft(&self) -> bool {
470 ie::Reader::new(&self.ies[..]).any(|(id, _ie)| id == ie::Id::MOBILITY_DOMAIN)
471 }
472
473 pub fn candidacy(&self) -> BssCandidacy {
475 let rssi_dbm = self.rssi_dbm;
476 match rssi_dbm {
477 0 => BssCandidacy { protection: self.protection(), rssi_dbm: i8::MIN },
480 _ => BssCandidacy { protection: self.protection(), rssi_dbm },
481 }
482 }
483
484 pub fn to_non_obfuscated_string(&self) -> String {
487 format!(
488 "SSID: {}, BSSID: {}, Protection: {}, Pri Chan: {}, Rx dBm: {}",
489 self.ssid.to_string_not_redactable(),
490 self.bssid,
491 self.protection(),
492 self.channel.primary,
493 self.rssi_dbm,
494 )
495 }
496
497 pub fn is_open(&self) -> bool {
498 matches!(self.protection(), Protection::Open | Protection::OpenOweTransition)
499 }
500
501 pub fn has_owe_configured(&self) -> bool {
502 matches!(self.protection(), Protection::Owe)
503 }
504
505 pub fn has_wep_configured(&self) -> bool {
506 matches!(self.protection(), Protection::Wep)
507 }
508
509 pub fn has_wpa1_configured(&self) -> bool {
510 matches!(
511 self.protection(),
512 Protection::Wpa1 | Protection::Wpa1Wpa2PersonalTkipOnly | Protection::Wpa1Wpa2Personal
513 )
514 }
515
516 pub fn has_wpa2_personal_configured(&self) -> bool {
517 matches!(
518 self.protection(),
519 Protection::Wpa1Wpa2PersonalTkipOnly
520 | Protection::Wpa1Wpa2Personal
521 | Protection::Wpa2PersonalTkipOnly
522 | Protection::Wpa2Personal
523 | Protection::Wpa2Wpa3Personal
524 )
525 }
526
527 pub fn has_wpa3_personal_configured(&self) -> bool {
528 matches!(self.protection(), Protection::Wpa2Wpa3Personal | Protection::Wpa3Personal)
529 }
530}
531
532impl From<BssDescription> for fidl_ieee80211::BssDescription {
533 fn from(bss: BssDescription) -> fidl_ieee80211::BssDescription {
534 let (bandwidth, vht_secondary_80_channel_num) = bss.channel.bandwidth.to_fidl();
535 let vht_secondary_80_channel = fidl_ieee80211::ChannelNumber {
536 band: bss.channel.band,
537 number: vht_secondary_80_channel_num,
538 };
539 fidl_ieee80211::BssDescription {
540 bssid: bss.bssid.to_array(),
541 bss_type: bss.bss_type,
542 beacon_period: bss.beacon_period,
543 capability_info: bss.capability_info,
544 primary: bss.channel.into(),
545 bandwidth,
546 vht_secondary_80_channel: vht_secondary_80_channel,
547 rssi_dbm: bss.rssi_dbm,
548 snr_db: bss.snr_db,
549 ies: bss.ies,
550 }
551 }
552}
553
554impl fmt::Display for BssDescription {
555 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
556 write!(
557 f,
558 "SSID: {}, BSSID: {}, Protection: {}, Pri Chan: {}, Rx dBm: {}",
559 self.ssid,
560 self.bssid,
561 self.protection(),
562 self.channel.primary,
563 self.rssi_dbm,
564 )
565 }
566}
567impl TryFrom<fidl_ieee80211::BssDescription> for BssDescription {
570 type Error = anyhow::Error;
571
572 fn try_from(bss: fidl_ieee80211::BssDescription) -> Result<BssDescription, Self::Error> {
573 let mut ssid_range = None;
574 let mut rates = None;
575 let mut tim_range = None;
576 let mut country_range = None;
577 let mut rsne_range = None;
578 let mut ht_cap_range = None;
579 let mut ht_op_range = None;
580 let mut rm_enabled_cap_range = None;
581 let mut ext_cap_range = None;
582 let mut vht_cap_range = None;
583 let mut vht_op_range = None;
584 let mut rsnxe_range = None;
585 let mut owe_transition_range = None;
586
587 for (ie_type, range) in ie::IeSummaryIter::new(&bss.ies[..]) {
588 let body = &bss.ies[range.clone()];
589 match ie_type {
590 IeType::SSID => {
591 ie::parse_ssid(body)?;
592 ssid_range = Some(range);
593 }
594 IeType::SUPPORTED_RATES => {
595 rates.get_or_insert(vec![]).extend(&*ie::parse_supported_rates(body)?);
596 }
597 IeType::EXTENDED_SUPPORTED_RATES => {
598 rates.get_or_insert(vec![]).extend(&*ie::parse_extended_supported_rates(body)?);
599 }
600 IeType::TIM => {
601 ie::parse_tim(body)?;
602 tim_range = Some(range);
603 }
604 IeType::COUNTRY => country_range = Some(range),
605 IeType::RSNE => rsne_range = Some(range.start - 2..range.end),
607 IeType::HT_CAPABILITIES => {
608 ie::parse_ht_capabilities(body)?;
609 ht_cap_range = Some(range);
610 }
611 IeType::HT_OPERATION => {
612 ie::parse_ht_operation(body)?;
613 ht_op_range = Some(range);
614 }
615 IeType::RM_ENABLED_CAPABILITIES => {
616 if let Ok(_) = ie::parse_rm_enabled_capabilities(body) {
617 rm_enabled_cap_range = Some(range);
618 }
619 }
620 IeType::EXT_CAPABILITIES => {
621 ext_cap_range = Some(range);
623 }
624 IeType::VHT_CAPABILITIES => {
625 ie::parse_vht_capabilities(body)?;
626 vht_cap_range = Some(range);
627 }
628 IeType::VHT_OPERATION => {
629 ie::parse_vht_operation(body)?;
630 vht_op_range = Some(range);
631 }
632 IeType::RSNXE => {
633 rsnxe_range = Some(range);
634 }
635 IeType::OWE_TRANSITION => {
636 if let Ok(_) = parse_owe_transition(body) {
637 owe_transition_range = Some(range);
638 }
639 }
640 _ => (),
641 }
642 }
643
644 let ssid_range = ssid_range.ok_or_else(|| format_err!("Missing SSID IE"))?;
645 let rates = rates.ok_or_else(|| format_err!("Missing rates IE"))?;
646
647 Ok(Self {
648 ssid: Ssid::from_bytes_unchecked(bss.ies[ssid_range].to_vec()),
649 bssid: Bssid::from(bss.bssid),
650 bss_type: bss.bss_type,
651 beacon_period: bss.beacon_period,
652 capability_info: bss.capability_info,
653 channel: crate::channel::Channel::from_fidl(
654 bss.primary,
655 bss.bandwidth,
656 bss.vht_secondary_80_channel,
657 )?,
658 rssi_dbm: bss.rssi_dbm,
659 snr_db: bss.snr_db,
660 ies: bss.ies,
661
662 rates,
663 tim_range,
664 country_range,
665 rsne_range,
666 ht_cap_range,
667 ht_op_range,
668 rm_enabled_cap_range,
669 ext_cap_range,
670 vht_cap_range,
671 vht_op_range,
672 rsnxe_range,
673 owe_transition_range,
674 })
675 }
676}
677
678#[derive(Debug, Eq, PartialEq)]
681pub struct BssCandidacy {
682 protection: Protection,
683 rssi_dbm: i8,
684}
685
686impl PartialOrd for BssCandidacy {
687 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
688 Some(self.cmp(other))
689 }
690}
691
692impl Ord for BssCandidacy {
693 fn cmp(&self, other: &Self) -> Ordering {
694 self.protection.cmp(&other.protection).then(self.rssi_dbm.cmp(&other.rssi_dbm))
695 }
696}
697
698pub fn phy_standard_map(bss_list: &Vec<BssDescription>) -> HashMap<Standard, usize> {
701 info_map(bss_list, |bss| bss.latest_standard())
702}
703
704pub fn channel_map(bss_list: &Vec<BssDescription>) -> HashMap<u8, usize> {
707 info_map(bss_list, |bss| bss.channel.primary)
708}
709
710fn info_map<F, T>(bss_list: &Vec<BssDescription>, f: F) -> HashMap<T, usize>
711where
712 T: Eq + Hash,
713 F: Fn(&BssDescription) -> T,
714{
715 let mut info_map: HashMap<T, usize> = HashMap::new();
716 for bss in bss_list {
717 *info_map.entry(f(&bss)).or_insert(0) += 1
718 }
719 info_map
720}
721
722#[cfg(test)]
723mod tests {
724 use super::*;
725 use crate::channel::Bandwidth;
726 use crate::fake_bss_description;
727 use crate::ie::IeType;
728 use crate::ie::fake_ies::{fake_owe_transition, fake_wmm_param};
729 use crate::test_utils::fake_frames::{
730 fake_unknown_rsne, fake_wmm_param_body, fake_wpa1_ie_body, fake_wpa2_mfpc_rsne,
731 fake_wpa2_mfpr_rsne, fake_wpa2_rsne, fake_wpa2_wpa3_mfpr_rsne, fake_wpa2_wpa3_no_mfp_rsne,
732 invalid_wpa3_enterprise_192_bit_rsne, invalid_wpa3_rsne,
733 };
734 use crate::test_utils::fake_stas::IesOverrides;
735 use assert_matches::assert_matches;
736 use test_case::test_case;
737
738 #[test_case(fake_bss_description!(
739 Wpa1Wpa2,
740 channel: Channel::new(36, Bandwidth::Cbw80P80{ vht_secondary_80_channel: 106 }, fidl_ieee80211::WlanBand::FiveGhz),
741 rssi_dbm: -20,
742 short_preamble: true,
743 ies_overrides: IesOverrides::new()
744 .set(IeType::DSSS_PARAM_SET, [136].to_vec())
745 ))]
746 #[test_case(fake_bss_description!(
747 Open,
748 channel: Channel::new(1, Bandwidth::Cbw20, fidl_ieee80211::WlanBand::TwoGhz),
749 beacon_period: 110,
750 short_preamble: true,
751 radio_measurement: true,
752 rates: vec![0x02, 0x04, 0x0c],
753 ))]
754 fn test_bss_lossless_conversion(bss: BssDescription) {
755 let fidl_bss = fidl_ieee80211::BssDescription::from(bss.clone());
756 assert_eq!(bss, BssDescription::try_from(fidl_bss.clone()).unwrap());
757 assert_eq!(
758 fidl_bss,
759 fidl_ieee80211::BssDescription::from(
760 BssDescription::try_from(fidl_bss.clone()).unwrap()
761 )
762 );
763 }
764
765 #[test]
766 fn test_known_protection() {
767 assert_eq!(Protection::Open, fake_bss_description!(Open).protection());
768 assert_eq!(
769 Protection::OpenOweTransition,
770 fake_bss_description!(OpenOweTransition).protection()
771 );
772 assert_eq!(Protection::Owe, fake_bss_description!(Owe).protection());
773 assert_eq!(Protection::Wep, fake_bss_description!(Wep).protection());
774 assert_eq!(Protection::Wpa1, fake_bss_description!(Wpa1).protection());
775 assert_eq!(Protection::Wpa1, fake_bss_description!(Wpa1Enhanced).protection());
776 assert_eq!(
777 Protection::Wpa1Wpa2PersonalTkipOnly,
778 fake_bss_description!(Wpa1Wpa2TkipOnly).protection()
779 );
780 assert_eq!(
781 Protection::Wpa2PersonalTkipOnly,
782 fake_bss_description!(Wpa2TkipOnly).protection()
783 );
784 assert_eq!(Protection::Wpa1Wpa2Personal, fake_bss_description!(Wpa1Wpa2).protection());
785 assert_eq!(Protection::Wpa2Personal, fake_bss_description!(Wpa2TkipCcmp).protection());
786 assert_eq!(Protection::Wpa2Personal, fake_bss_description!(Wpa2).protection());
787 assert_eq!(Protection::Wpa2Wpa3Personal, fake_bss_description!(Wpa2Wpa3).protection());
788 assert_eq!(Protection::Wpa3Personal, fake_bss_description!(Wpa3).protection());
789 assert_eq!(Protection::Wpa2Enterprise, fake_bss_description!(Wpa2Enterprise).protection());
790 assert_eq!(Protection::Wpa3Enterprise, fake_bss_description!(Wpa3Enterprise).protection());
791 }
792
793 #[test]
794 fn test_pmf_configs_supported() {
795 let bss = fake_bss_description!(Wpa2,
796 ies_overrides: IesOverrides::new()
797 .set(IeType::RSNE, fake_wpa2_mfpc_rsne()[2..].to_vec())
798 );
799 assert_eq!(Protection::Wpa2Personal, bss.protection());
800
801 let bss = fake_bss_description!(Wpa2,
802 ies_overrides: IesOverrides::new()
803 .set(IeType::RSNE, fake_wpa2_mfpr_rsne()[2..].to_vec())
804 );
805 assert_eq!(Protection::Wpa2Personal, bss.protection());
806
807 let bss = fake_bss_description!(Wpa2,
808 ies_overrides: IesOverrides::new()
809 .set(IeType::RSNE, fake_wpa2_wpa3_mfpr_rsne()[2..].to_vec())
810 );
811 assert_eq!(Protection::Wpa2Wpa3Personal, bss.protection());
812 }
813
814 #[test]
815 fn test_downgrade() {
816 let bss = fake_bss_description!(Wpa2,
818 ies_overrides: IesOverrides::new()
819 .set(IeType::RSNE, fake_wpa2_wpa3_no_mfp_rsne()[2..].to_vec())
820 );
821 assert_eq!(Protection::Wpa2Personal, bss.protection());
822
823 let bss = fake_bss_description!(Wpa1,
825 ies_overrides: IesOverrides::new()
826 .set(IeType::RSNE, invalid_wpa3_rsne()[2..].to_vec())
827 );
828 assert_eq!(Protection::Wpa1, bss.protection());
829 }
830
831 #[test]
832 fn test_unknown_protection() {
833 let bss = fake_bss_description!(Wpa2,
834 ies_overrides: IesOverrides::new()
835 .set(IeType::RSNE, fake_unknown_rsne()[2..].to_vec())
836 );
837 assert_eq!(Protection::Unknown, bss.protection());
838
839 let bss = fake_bss_description!(Wpa2,
840 ies_overrides: IesOverrides::new()
841 .set(IeType::RSNE, invalid_wpa3_rsne()[2..].to_vec())
842 );
843 assert_eq!(Protection::Unknown, bss.protection());
844
845 let bss = fake_bss_description!(Wpa2,
846 ies_overrides: IesOverrides::new()
847 .set(IeType::RSNE, invalid_wpa3_enterprise_192_bit_rsne()[2..].to_vec())
848 );
849 assert_eq!(Protection::Unknown, bss.protection());
850 }
851
852 #[test]
853 fn test_needs_eapol_exchange() {
854 assert!(fake_bss_description!(Owe).needs_eapol_exchange());
855 assert!(fake_bss_description!(Wpa1).needs_eapol_exchange());
856 assert!(fake_bss_description!(Wpa2).needs_eapol_exchange());
857
858 assert!(!fake_bss_description!(Open).needs_eapol_exchange());
859 assert!(!fake_bss_description!(OpenOweTransition).needs_eapol_exchange());
860 assert!(!fake_bss_description!(Wep).needs_eapol_exchange());
861 }
862
863 #[test]
864 fn test_rm_enabled_cap_ie() {
865 let bss = fake_bss_description!(Wpa2,
866 ies_overrides: IesOverrides::new()
867 .remove(IeType::RM_ENABLED_CAPABILITIES)
868 );
869 assert!(bss.rm_enabled_cap().is_none());
870
871 #[rustfmt::skip]
872 let rm_enabled_capabilities = vec![
873 0x03, 0x00, 0x00, 0x00, 0x00,
875 ];
876 let bss = fake_bss_description!(Wpa2,
877 ies_overrides: IesOverrides::new()
878 .remove(IeType::RM_ENABLED_CAPABILITIES)
879 .set(IeType::RM_ENABLED_CAPABILITIES, rm_enabled_capabilities.clone())
880 );
881 assert_matches!(bss.rm_enabled_cap(), Some(cap) => {
882 assert_eq!(cap.as_bytes(), &rm_enabled_capabilities[..]);
883 });
884 }
885
886 #[test]
887 fn test_ext_cap_ie() {
888 let bss = fake_bss_description!(Wpa2,
889 ies_overrides: IesOverrides::new()
890 .remove(IeType::EXT_CAPABILITIES)
891 );
892 assert!(bss.ext_cap().is_none());
893
894 #[rustfmt::skip]
895 let ext_capabilities = vec![
896 0x04, 0x00,
897 0x08, 0x00, 0x00, 0x00, 0x00, 0x40
899 ];
900 let bss = fake_bss_description!(Wpa2,
901 ies_overrides: IesOverrides::new()
902 .remove(IeType::EXT_CAPABILITIES)
903 .set(IeType::EXT_CAPABILITIES, ext_capabilities.clone())
904 );
905 let ext_cap = bss.ext_cap().expect("expect bss.ext_cap() to be Some");
906 assert_eq!(ext_cap.ext_caps_octet_1.map(|o| o.0), Some(0x04));
907 assert_eq!(ext_cap.ext_caps_octet_2.map(|o| o.0), Some(0x00));
908 assert_eq!(ext_cap.ext_caps_octet_3.map(|o| o.0), Some(0x08));
909 assert_eq!(ext_cap.remaining, &[0x00, 0x00, 0x00, 0x00, 0x40]);
910 }
911
912 #[test]
913 fn test_wpa_ie() {
914 let buf =
915 fake_bss_description!(Wpa1).wpa_ie().expect("failed to find WPA1 IE").into_bytes();
916 assert_eq!(&fake_wpa1_ie_body(false)[..], &buf[..]);
917 fake_bss_description!(Wpa2).wpa_ie().expect_err("found unexpected WPA1 IE");
918 }
919
920 #[test]
921 fn test_wmm_param() {
922 let bss = fake_bss_description!(Wpa2, qos: true, wmm_param: Some(fake_wmm_param()));
923 let wmm_param = bss.wmm_param().expect("failed to find wmm param");
924 assert_eq!(fake_wmm_param_body(), wmm_param.as_bytes());
925 }
926
927 #[test]
928 fn test_owe_transition() {
929 let bss = fake_bss_description!(OpenOweTransition);
930 let owe_transition = bss.owe_transition().expect("failed to find owe transition ie");
931 assert_eq!(owe_transition, fake_owe_transition());
932 }
933
934 #[test]
935 fn test_latest_standard_ac() {
936 let bss = fake_bss_description!(Open,
937 ies_overrides: IesOverrides::new()
938 .set(IeType::VHT_CAPABILITIES, vec![0; fidl_ieee80211::VHT_CAP_LEN as usize])
939 .set(IeType::VHT_OPERATION, vec![0; fidl_ieee80211::VHT_OP_LEN as usize]),
940 );
941 assert_eq!(Standard::Dot11Ac, bss.latest_standard());
942 }
943
944 #[test]
945 fn test_latest_standard_n() {
946 let bss = fake_bss_description!(Open,
947 ies_overrides: IesOverrides::new()
948 .set(IeType::HT_CAPABILITIES, vec![0; fidl_ieee80211::HT_CAP_LEN as usize])
949 .set(IeType::HT_OPERATION, vec![0; fidl_ieee80211::HT_OP_LEN as usize])
950 .remove(IeType::VHT_CAPABILITIES)
951 .remove(IeType::VHT_OPERATION),
952 );
953 assert_eq!(Standard::Dot11N, bss.latest_standard());
954 }
955
956 #[test]
957 fn test_latest_standard_g() {
958 let bss = fake_bss_description!(Open,
959 channel: Channel::new(1, Bandwidth::Cbw20, fidl_ieee80211::WlanBand::TwoGhz),
960 rates: vec![12],
961 ies_overrides: IesOverrides::new()
962 .remove(IeType::HT_CAPABILITIES)
963 .remove(IeType::HT_OPERATION)
964 .remove(IeType::VHT_CAPABILITIES)
965 .remove(IeType::VHT_OPERATION),
966 );
967 assert_eq!(Standard::Dot11G, bss.latest_standard());
968 }
969
970 #[test]
971 fn test_latest_standard_b() {
972 let bss = fake_bss_description!(Open,
973 channel: Channel::new(1, Bandwidth::Cbw20, fidl_ieee80211::WlanBand::TwoGhz),
974 rates: vec![2],
975 ies_overrides: IesOverrides::new()
976 .remove(IeType::HT_CAPABILITIES)
977 .remove(IeType::HT_OPERATION)
978 .remove(IeType::VHT_CAPABILITIES)
979 .remove(IeType::VHT_OPERATION),
980 );
981 assert_eq!(Standard::Dot11B, bss.latest_standard());
982 }
983
984 #[test]
985 fn test_latest_standard_b_with_basic() {
986 let bss = fake_bss_description!(Open,
987 channel: Channel::new(1, Bandwidth::Cbw20, fidl_ieee80211::WlanBand::TwoGhz),
988 rates: vec![ie::SupportedRate(2).with_basic(true).0],
989 ies_overrides: IesOverrides::new()
990 .remove(IeType::HT_CAPABILITIES)
991 .remove(IeType::HT_OPERATION)
992 .remove(IeType::VHT_CAPABILITIES)
993 .remove(IeType::VHT_OPERATION),
994 );
995 assert_eq!(Standard::Dot11B, bss.latest_standard());
996 }
997
998 #[test]
999 fn test_latest_standard_a() {
1000 let bss = fake_bss_description!(Open,
1001 channel: Channel::new(36, Bandwidth::Cbw20, fidl_ieee80211::WlanBand::FiveGhz),
1002 rates: vec![48],
1003 ies_overrides: IesOverrides::new()
1004 .remove(IeType::HT_CAPABILITIES)
1005 .remove(IeType::HT_OPERATION)
1006 .remove(IeType::VHT_CAPABILITIES)
1007 .remove(IeType::VHT_OPERATION),
1008 );
1009 assert_eq!(Standard::Dot11A, bss.latest_standard());
1010 }
1011
1012 #[test]
1013 fn test_supports_uapsd() {
1014 let bss = fake_bss_description!(Wpa2,
1015 ies_overrides: IesOverrides::new()
1016 .remove(IeType::WMM_INFO)
1017 .remove(IeType::WMM_PARAM)
1018 );
1019 assert!(!bss.supports_uapsd());
1020
1021 let mut wmm_info = vec![0x80]; let bss = fake_bss_description!(Wpa2,
1023 ies_overrides: IesOverrides::new()
1024 .remove(IeType::WMM_INFO)
1025 .remove(IeType::WMM_PARAM)
1026 .set(IeType::WMM_INFO, wmm_info.clone())
1027 );
1028 assert!(bss.supports_uapsd());
1029
1030 wmm_info = vec![0x00]; let bss = fake_bss_description!(Wpa2,
1032 ies_overrides: IesOverrides::new()
1033 .remove(IeType::WMM_INFO)
1034 .remove(IeType::WMM_PARAM)
1035 .set(IeType::WMM_INFO, wmm_info)
1036 );
1037 assert!(!bss.supports_uapsd());
1038
1039 #[rustfmt::skip]
1040 let mut wmm_param = vec![
1041 0x80, 0x00, 0x03, 0xa4, 0x00, 0x00, 0x27, 0xa4, 0x00, 0x00, 0x42, 0x43, 0x5e, 0x00, 0x62, 0x32, 0x2f, 0x00, ];
1048 let bss = fake_bss_description!(Wpa2,
1049 ies_overrides: IesOverrides::new()
1050 .remove(IeType::WMM_INFO)
1051 .remove(IeType::WMM_PARAM)
1052 .set(IeType::WMM_PARAM, wmm_param.clone())
1053 );
1054 assert!(bss.supports_uapsd());
1055
1056 wmm_param[0] = 0x00; let bss = fake_bss_description!(Wpa2,
1058 ies_overrides: IesOverrides::new()
1059 .remove(IeType::WMM_INFO)
1060 .remove(IeType::WMM_PARAM)
1061 .set(IeType::WMM_PARAM, wmm_param)
1062 );
1063 assert!(!bss.supports_uapsd());
1064 }
1065
1066 #[test]
1067 fn test_supports_ft() {
1068 let bss = fake_bss_description!(Wpa2,
1069 ies_overrides: IesOverrides::new()
1070 .remove(IeType::MOBILITY_DOMAIN)
1071 );
1072 assert!(!bss.supports_ft());
1073
1074 let bss = fake_bss_description!(Wpa2,
1075 ies_overrides: IesOverrides::new()
1076 .remove(IeType::MOBILITY_DOMAIN)
1077 .set(IeType::MOBILITY_DOMAIN, vec![0x00; 3])
1079 );
1080 assert!(bss.supports_ft());
1081 }
1082
1083 #[test]
1084 fn test_candidacy() {
1085 let bss_candidacy = fake_bss_description!(Wpa2, rssi_dbm: -10).candidacy();
1086 assert_eq!(
1087 bss_candidacy,
1088 BssCandidacy { protection: Protection::Wpa2Personal, rssi_dbm: -10 }
1089 );
1090
1091 let bss_candidacy = fake_bss_description!(Open, rssi_dbm: -10).candidacy();
1092 assert_eq!(bss_candidacy, BssCandidacy { protection: Protection::Open, rssi_dbm: -10 });
1093
1094 let bss_candidacy = fake_bss_description!(Wpa2, rssi_dbm: -20).candidacy();
1095 assert_eq!(
1096 bss_candidacy,
1097 BssCandidacy { protection: Protection::Wpa2Personal, rssi_dbm: -20 }
1098 );
1099
1100 let bss_candidacy = fake_bss_description!(Wpa2, rssi_dbm: 0).candidacy();
1101 assert_eq!(
1102 bss_candidacy,
1103 BssCandidacy { protection: Protection::Wpa2Personal, rssi_dbm: i8::MIN }
1104 );
1105 }
1106
1107 fn assert_bss_comparison(worse: &BssDescription, better: &BssDescription) {
1108 assert_eq!(Ordering::Less, worse.candidacy().cmp(&better.candidacy()));
1109 assert_eq!(Ordering::Greater, better.candidacy().cmp(&worse.candidacy()));
1110 }
1111
1112 #[test]
1113 fn test_bss_comparison() {
1114 assert_eq!(
1116 Ordering::Equal,
1117 fake_bss_description!(Wpa2, rssi_dbm: -10)
1118 .candidacy()
1119 .cmp(&fake_bss_description!(Wpa2, rssi_dbm: -10).candidacy())
1120 );
1121
1122 assert_bss_comparison(
1124 &fake_bss_description!(Wpa1, rssi_dbm: -10),
1125 &fake_bss_description!(Wpa2, rssi_dbm: -50),
1126 );
1127 assert_bss_comparison(
1128 &fake_bss_description!(Open, rssi_dbm: -10),
1129 &fake_bss_description!(Wpa2, rssi_dbm: -50),
1130 );
1131 assert_bss_comparison(
1133 &fake_bss_description!(Wpa2, rssi_dbm: -50),
1134 &fake_bss_description!(Wpa2, rssi_dbm: -10),
1135 );
1136 assert_bss_comparison(
1138 &fake_bss_description!(Wpa2, rssi_dbm: 0),
1139 &fake_bss_description!(Wpa2, rssi_dbm: -100),
1140 );
1141 }
1142
1143 #[test]
1144 fn test_bss_ie_fields() {
1145 #[rustfmt::skip]
1146 let ht_cap = vec![
1147 0xef, 0x09, 0x1b, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1153 ];
1154 #[rustfmt::skip]
1155 let ht_op = vec![
1156 0x9d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
1161 #[rustfmt::skip]
1162 let vht_cap = vec![
1163 0xb2, 0x01, 0x80, 0x33, 0xea, 0xff, 0x00, 0x00, 0xea, 0xff, 0x00, 0x00, ];
1166 let vht_op = vec![0x01, 0x9b, 0x00, 0xfc, 0xff];
1167 let rsnxe = vec![0b00100001];
1168
1169 let bss = fake_bss_description!(Wpa2,
1170 ies_overrides: IesOverrides::new()
1171 .set(IeType::SSID, b"ssidie".to_vec())
1172 .set(IeType::SUPPORTED_RATES, vec![0x81, 0x82, 0x83])
1173 .set(IeType::EXTENDED_SUPPORTED_RATES, vec![4, 5, 6])
1174 .set(IeType::COUNTRY, vec![1, 2, 3])
1175 .set(IeType::HT_CAPABILITIES, ht_cap.clone())
1176 .set(IeType::HT_OPERATION, ht_op.clone())
1177 .set(IeType::VHT_CAPABILITIES, vht_cap.clone())
1178 .set(IeType::VHT_OPERATION, vht_op.clone())
1179 .set(IeType::RSNXE, rsnxe.clone())
1180 );
1181 assert_eq!(bss.ssid, Ssid::try_from("ssidie").unwrap());
1182 assert_eq!(
1183 bss.rates(),
1184 &[
1185 ie::SupportedRate(0x81),
1186 ie::SupportedRate(0x82),
1187 ie::SupportedRate(0x83),
1188 ie::SupportedRate(4),
1189 ie::SupportedRate(5),
1190 ie::SupportedRate(6)
1191 ]
1192 );
1193 assert_eq!(bss.country(), Some(&[1, 2, 3][..]));
1194 assert_eq!(bss.rsne(), Some(&fake_wpa2_rsne()[..]));
1195 assert_matches!(bss.ht_cap(), Some(capability_info) => {
1196 assert_eq!(Ref::bytes(&capability_info), &ht_cap[..]);
1197 });
1198 assert_eq!(
1199 bss.raw_ht_cap().map(|capability_info| capability_info.bytes.to_vec()),
1200 Some(ht_cap)
1201 );
1202 assert_matches!(bss.ht_op(), Some(op) => {
1203 assert_eq!(Ref::bytes(&op), &ht_op[..]);
1204 });
1205 assert_eq!(bss.raw_ht_op().map(|op| op.bytes.to_vec()), Some(ht_op));
1206 assert_matches!(bss.vht_cap(), Some(capability_info) => {
1207 assert_eq!(Ref::bytes(&capability_info), &vht_cap[..]);
1208 });
1209 assert_eq!(
1210 bss.raw_vht_cap().map(|capability_info| capability_info.bytes.to_vec()),
1211 Some(vht_cap)
1212 );
1213 assert_matches!(bss.vht_op(), Some(op) => {
1214 assert_eq!(Ref::bytes(&op), &vht_op[..]);
1215 });
1216 assert_eq!(bss.raw_vht_op().map(|op| op.bytes.to_vec()), Some(vht_op));
1217 assert_matches!(bss.rsnxe(), Some(r) => {
1218 assert_eq!(Ref::bytes(&r.rsnxe_octet_1.expect("no octet 1")), &rsnxe[..]);
1219 });
1220 }
1221
1222 #[test]
1223 fn test_protection_conversions() {
1224 assert_eq!(
1225 Protection::Unknown,
1226 Protection::from(fidl_sme::Protection::from(Protection::Unknown))
1227 );
1228 assert_eq!(
1229 Protection::Open,
1230 Protection::from(fidl_sme::Protection::from(Protection::Open))
1231 );
1232 assert_eq!(
1233 Protection::OpenOweTransition,
1234 Protection::from(fidl_sme::Protection::from(Protection::OpenOweTransition))
1235 );
1236 assert_eq!(Protection::Owe, Protection::from(fidl_sme::Protection::from(Protection::Owe)));
1237 assert_eq!(Protection::Wep, Protection::from(fidl_sme::Protection::from(Protection::Wep)));
1238 assert_eq!(
1239 Protection::Wpa1,
1240 Protection::from(fidl_sme::Protection::from(Protection::Wpa1))
1241 );
1242 assert_eq!(
1243 Protection::Wpa1Wpa2PersonalTkipOnly,
1244 Protection::from(fidl_sme::Protection::from(Protection::Wpa1Wpa2PersonalTkipOnly))
1245 );
1246 assert_eq!(
1247 Protection::Wpa2PersonalTkipOnly,
1248 Protection::from(fidl_sme::Protection::from(Protection::Wpa2PersonalTkipOnly))
1249 );
1250 assert_eq!(
1251 Protection::Wpa1Wpa2Personal,
1252 Protection::from(fidl_sme::Protection::from(Protection::Wpa1Wpa2Personal))
1253 );
1254 assert_eq!(
1255 Protection::Wpa2Personal,
1256 Protection::from(fidl_sme::Protection::from(Protection::Wpa2Personal))
1257 );
1258 assert_eq!(
1259 Protection::Wpa2Wpa3Personal,
1260 Protection::from(fidl_sme::Protection::from(Protection::Wpa2Wpa3Personal))
1261 );
1262 assert_eq!(
1263 Protection::Wpa3Personal,
1264 Protection::from(fidl_sme::Protection::from(Protection::Wpa3Personal))
1265 );
1266 assert_eq!(
1267 Protection::Wpa2Enterprise,
1268 Protection::from(fidl_sme::Protection::from(Protection::Wpa2Enterprise))
1269 );
1270 assert_eq!(
1271 Protection::Wpa3Enterprise,
1272 Protection::from(fidl_sme::Protection::from(Protection::Wpa3Enterprise))
1273 );
1274 }
1275}