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