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