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