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