1use crate::layer::dot11::types;
11use crate::layer::field::FieldError;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Dot11Elt {
25 pub id: u8,
27 pub len: u8,
29 pub info: Vec<u8>,
31}
32
33impl Dot11Elt {
34 #[must_use]
36 pub fn new(id: u8, info: Vec<u8>) -> Self {
37 Self {
38 id,
39 len: info.len() as u8,
40 info,
41 }
42 }
43
44 pub fn parse(buf: &[u8], offset: usize) -> Result<(Self, usize), FieldError> {
47 if buf.len() < offset + 2 {
48 return Err(FieldError::BufferTooShort {
49 offset,
50 need: 2,
51 have: buf.len().saturating_sub(offset),
52 });
53 }
54 let id = buf[offset];
55 let len = buf[offset + 1] as usize;
56 if buf.len() < offset + 2 + len {
57 return Err(FieldError::BufferTooShort {
58 offset: offset + 2,
59 need: len,
60 have: buf.len().saturating_sub(offset + 2),
61 });
62 }
63 let info = buf[offset + 2..offset + 2 + len].to_vec();
64 Ok((
65 Self {
66 id,
67 len: len as u8,
68 info,
69 },
70 2 + len,
71 ))
72 }
73
74 #[must_use]
77 pub fn parse_all(buf: &[u8], mut offset: usize) -> Vec<Self> {
78 let mut elements = Vec::new();
79 while offset < buf.len() {
80 match Self::parse(buf, offset) {
81 Ok((elt, consumed)) => {
82 offset += consumed;
83 elements.push(elt);
84 },
85 Err(_) => break,
86 }
87 }
88 elements
89 }
90
91 #[must_use]
93 pub fn build(&self) -> Vec<u8> {
94 let mut buf = Vec::with_capacity(2 + self.info.len());
95 buf.push(self.id);
96 buf.push(self.len);
97 buf.extend_from_slice(&self.info);
98 buf
99 }
100
101 #[must_use]
103 pub fn build_chain(elements: &[Dot11Elt]) -> Vec<u8> {
104 let total: usize = elements.iter().map(|e| 2 + e.info.len()).sum();
105 let mut buf = Vec::with_capacity(total);
106 for elt in elements {
107 buf.extend(elt.build());
108 }
109 buf
110 }
111
112 #[must_use]
114 pub fn ssid(name: &str) -> Self {
115 Self::new(types::ie_id::SSID, name.as_bytes().to_vec())
116 }
117
118 #[must_use]
120 pub fn ssid_hidden() -> Self {
121 Self::new(types::ie_id::SSID, Vec::new())
122 }
123
124 #[must_use]
126 pub fn is_ssid(&self) -> bool {
127 self.id == types::ie_id::SSID
128 }
129
130 #[must_use]
132 pub fn ssid_str(&self) -> Option<String> {
133 if self.id == types::ie_id::SSID {
134 Some(String::from_utf8_lossy(&self.info).into_owned())
135 } else {
136 None
137 }
138 }
139
140 #[must_use]
142 pub fn name(&self) -> &'static str {
143 types::ie_id::name(self.id)
144 }
145
146 #[must_use]
148 pub fn wire_len(&self) -> usize {
149 2 + self.info.len()
150 }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq)]
159pub struct Dot11EltSSID {
160 pub ssid: String,
162}
163
164impl Dot11EltSSID {
165 #[must_use]
167 pub fn new(ssid: &str) -> Self {
168 Self {
169 ssid: ssid.to_string(),
170 }
171 }
172
173 #[must_use]
175 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
176 if elt.id != types::ie_id::SSID {
177 return None;
178 }
179 Some(Self {
180 ssid: String::from_utf8_lossy(&elt.info).into_owned(),
181 })
182 }
183
184 #[must_use]
186 pub fn build(&self) -> Dot11Elt {
187 Dot11Elt::new(types::ie_id::SSID, self.ssid.as_bytes().to_vec())
188 }
189
190 #[must_use]
192 pub fn is_hidden(&self) -> bool {
193 self.ssid.is_empty()
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
205pub struct Dot11EltRates {
206 pub rates: Vec<u8>,
208}
209
210impl Dot11EltRates {
211 #[must_use]
213 pub fn new(rates: Vec<u8>) -> Self {
214 Self { rates }
215 }
216
217 #[must_use]
219 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
220 if elt.id != types::ie_id::RATES {
221 return None;
222 }
223 Some(Self {
224 rates: elt.info.clone(),
225 })
226 }
227
228 #[must_use]
230 pub fn build(&self) -> Dot11Elt {
231 Dot11Elt::new(types::ie_id::RATES, self.rates.clone())
232 }
233
234 #[must_use]
236 pub fn rate_mbps(rate_byte: u8) -> f32 {
237 f32::from(rate_byte & 0x7F) * 0.5
238 }
239
240 #[must_use]
242 pub fn is_basic(rate_byte: u8) -> bool {
243 rate_byte & 0x80 != 0
244 }
245
246 #[must_use]
248 pub fn rates_mbps(&self) -> Vec<f32> {
249 self.rates.iter().map(|&r| Self::rate_mbps(r)).collect()
250 }
251}
252
253#[derive(Debug, Clone, PartialEq, Eq)]
261pub struct Dot11EltDSSSet {
262 pub channel: u8,
264}
265
266impl Dot11EltDSSSet {
267 #[must_use]
269 pub fn new(channel: u8) -> Self {
270 Self { channel }
271 }
272
273 #[must_use]
275 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
276 if elt.id != types::ie_id::DS_PARAMETER_SET || elt.info.is_empty() {
277 return None;
278 }
279 Some(Self {
280 channel: elt.info[0],
281 })
282 }
283
284 #[must_use]
286 pub fn build(&self) -> Dot11Elt {
287 Dot11Elt::new(types::ie_id::DS_PARAMETER_SET, vec![self.channel])
288 }
289}
290
291#[derive(Debug, Clone, PartialEq, Eq)]
297pub struct Dot11EltTIM {
298 pub dtim_count: u8,
300 pub dtim_period: u8,
302 pub bitmap_control: u8,
304 pub bitmap: Vec<u8>,
306}
307
308impl Dot11EltTIM {
309 #[must_use]
311 pub fn new(dtim_count: u8, dtim_period: u8, bitmap_control: u8, bitmap: Vec<u8>) -> Self {
312 Self {
313 dtim_count,
314 dtim_period,
315 bitmap_control,
316 bitmap,
317 }
318 }
319
320 #[must_use]
322 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
323 if elt.id != types::ie_id::TIM || elt.info.len() < 3 {
324 return None;
325 }
326 Some(Self {
327 dtim_count: elt.info[0],
328 dtim_period: elt.info[1],
329 bitmap_control: elt.info[2],
330 bitmap: elt.info[3..].to_vec(),
331 })
332 }
333
334 #[must_use]
336 pub fn build(&self) -> Dot11Elt {
337 let mut info = Vec::with_capacity(3 + self.bitmap.len());
338 info.push(self.dtim_count);
339 info.push(self.dtim_period);
340 info.push(self.bitmap_control);
341 info.extend_from_slice(&self.bitmap);
342 Dot11Elt::new(types::ie_id::TIM, info)
343 }
344}
345
346#[derive(Debug, Clone, PartialEq, Eq)]
352pub struct CountryTriplet {
353 pub first_channel: u8,
355 pub num_channels: u8,
357 pub max_power: u8,
359}
360
361#[derive(Debug, Clone, PartialEq, Eq)]
363pub struct Dot11EltCountry {
364 pub country: [u8; 3],
366 pub triplets: Vec<CountryTriplet>,
368}
369
370impl Dot11EltCountry {
371 #[must_use]
373 pub fn new(country: [u8; 3], triplets: Vec<CountryTriplet>) -> Self {
374 Self { country, triplets }
375 }
376
377 #[must_use]
379 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
380 if elt.id != types::ie_id::COUNTRY || elt.info.len() < 3 {
381 return None;
382 }
383 let mut country = [0u8; 3];
384 country.copy_from_slice(&elt.info[0..3]);
385
386 let mut triplets = Vec::new();
387 let mut pos = 3;
388 while pos + 3 <= elt.info.len() {
389 triplets.push(CountryTriplet {
390 first_channel: elt.info[pos],
391 num_channels: elt.info[pos + 1],
392 max_power: elt.info[pos + 2],
393 });
394 pos += 3;
395 }
396
397 Some(Self { country, triplets })
398 }
399
400 #[must_use]
402 pub fn build(&self) -> Dot11Elt {
403 let mut info = Vec::with_capacity(3 + self.triplets.len() * 3);
404 info.extend_from_slice(&self.country);
405 for t in &self.triplets {
406 info.push(t.first_channel);
407 info.push(t.num_channels);
408 info.push(t.max_power);
409 }
410 Dot11Elt::new(types::ie_id::COUNTRY, info)
411 }
412
413 #[must_use]
415 pub fn country_str(&self) -> String {
416 String::from_utf8_lossy(&self.country).into_owned()
417 }
418}
419
420#[derive(Debug, Clone, PartialEq, Eq)]
426pub struct Dot11EltCSA {
427 pub mode: u8,
429 pub new_channel: u8,
431 pub count: u8,
433}
434
435impl Dot11EltCSA {
436 #[must_use]
438 pub fn new(mode: u8, new_channel: u8, count: u8) -> Self {
439 Self {
440 mode,
441 new_channel,
442 count,
443 }
444 }
445
446 #[must_use]
448 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
449 if elt.id != types::ie_id::CHANNEL_SWITCH_ANNOUNCEMENT || elt.info.len() < 3 {
450 return None;
451 }
452 Some(Self {
453 mode: elt.info[0],
454 new_channel: elt.info[1],
455 count: elt.info[2],
456 })
457 }
458
459 #[must_use]
461 pub fn build(&self) -> Dot11Elt {
462 Dot11Elt::new(
463 types::ie_id::CHANNEL_SWITCH_ANNOUNCEMENT,
464 vec![self.mode, self.new_channel, self.count],
465 )
466 }
467}
468
469#[derive(Debug, Clone, PartialEq, Eq)]
477pub struct Dot11EltHTCapabilities {
478 pub ht_cap_info: u16,
480 pub ampdu_params: u8,
482 pub mcs_set: [u8; 16],
484 pub ht_ext_cap: u16,
486 pub txbf_cap: u32,
488 pub asel_cap: u8,
490}
491
492pub const HT_CAP_LEN: usize = 26;
494
495impl Dot11EltHTCapabilities {
496 #[must_use]
498 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
499 if elt.id != types::ie_id::HT_CAPABILITIES || elt.info.len() < HT_CAP_LEN {
500 return None;
501 }
502 let d = &elt.info;
503 let ht_cap_info = u16::from_le_bytes([d[0], d[1]]);
504 let ampdu_params = d[2];
505 let mut mcs_set = [0u8; 16];
506 mcs_set.copy_from_slice(&d[3..19]);
507 let ht_ext_cap = u16::from_le_bytes([d[19], d[20]]);
508 let txbf_cap = u32::from_le_bytes([d[21], d[22], d[23], d[24]]);
509 let asel_cap = d[25];
510
511 Some(Self {
512 ht_cap_info,
513 ampdu_params,
514 mcs_set,
515 ht_ext_cap,
516 txbf_cap,
517 asel_cap,
518 })
519 }
520
521 #[must_use]
523 pub fn build(&self) -> Dot11Elt {
524 let mut info = Vec::with_capacity(HT_CAP_LEN);
525 info.extend_from_slice(&self.ht_cap_info.to_le_bytes());
526 info.push(self.ampdu_params);
527 info.extend_from_slice(&self.mcs_set);
528 info.extend_from_slice(&self.ht_ext_cap.to_le_bytes());
529 info.extend_from_slice(&self.txbf_cap.to_le_bytes());
530 info.push(self.asel_cap);
531 Dot11Elt::new(types::ie_id::HT_CAPABILITIES, info)
532 }
533
534 #[must_use]
536 pub fn ldpc(&self) -> bool {
537 self.ht_cap_info & 0x0001 != 0
538 }
539
540 #[must_use]
542 pub fn channel_width_set(&self) -> bool {
543 self.ht_cap_info & 0x0002 != 0
544 }
545
546 #[must_use]
548 pub fn short_gi_20(&self) -> bool {
549 self.ht_cap_info & 0x0020 != 0
550 }
551
552 #[must_use]
554 pub fn short_gi_40(&self) -> bool {
555 self.ht_cap_info & 0x0040 != 0
556 }
557}
558
559#[derive(Debug, Clone, PartialEq, Eq)]
565pub struct Dot11EltHTInfo {
566 pub primary_channel: u8,
568 pub ht_info: [u8; 5],
570 pub basic_mcs_set: [u8; 16],
572}
573
574pub const HT_INFO_LEN: usize = 22;
576
577impl Dot11EltHTInfo {
578 #[must_use]
580 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
581 if elt.id != types::ie_id::HT_INFORMATION || elt.info.len() < HT_INFO_LEN {
582 return None;
583 }
584 let d = &elt.info;
585 let primary_channel = d[0];
586 let mut ht_info = [0u8; 5];
587 ht_info.copy_from_slice(&d[1..6]);
588 let mut basic_mcs_set = [0u8; 16];
589 basic_mcs_set.copy_from_slice(&d[6..22]);
590
591 Some(Self {
592 primary_channel,
593 ht_info,
594 basic_mcs_set,
595 })
596 }
597
598 #[must_use]
600 pub fn build(&self) -> Dot11Elt {
601 let mut info = Vec::with_capacity(HT_INFO_LEN);
602 info.push(self.primary_channel);
603 info.extend_from_slice(&self.ht_info);
604 info.extend_from_slice(&self.basic_mcs_set);
605 Dot11Elt::new(types::ie_id::HT_INFORMATION, info)
606 }
607
608 #[must_use]
611 pub fn secondary_channel_offset(&self) -> u8 {
612 self.ht_info[0] & 0x03
613 }
614
615 #[must_use]
617 pub fn sta_channel_width(&self) -> bool {
618 self.ht_info[0] & 0x04 != 0
619 }
620}
621
622#[derive(Debug, Clone, PartialEq, Eq)]
630pub struct Dot11EltVHTCapabilities {
631 pub vht_cap_info: u32,
633 pub mcs_nss_set: [u8; 8],
635}
636
637pub const VHT_CAP_LEN: usize = 12;
639
640impl Dot11EltVHTCapabilities {
641 #[must_use]
643 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
644 if elt.id != types::ie_id::VHT_CAPABILITIES || elt.info.len() < VHT_CAP_LEN {
645 return None;
646 }
647 let d = &elt.info;
648 let vht_cap_info = u32::from_le_bytes([d[0], d[1], d[2], d[3]]);
649 let mut mcs_nss_set = [0u8; 8];
650 mcs_nss_set.copy_from_slice(&d[4..12]);
651
652 Some(Self {
653 vht_cap_info,
654 mcs_nss_set,
655 })
656 }
657
658 #[must_use]
660 pub fn build(&self) -> Dot11Elt {
661 let mut info = Vec::with_capacity(VHT_CAP_LEN);
662 info.extend_from_slice(&self.vht_cap_info.to_le_bytes());
663 info.extend_from_slice(&self.mcs_nss_set);
664 Dot11Elt::new(types::ie_id::VHT_CAPABILITIES, info)
665 }
666
667 #[must_use]
670 pub fn max_mpdu_length(&self) -> u8 {
671 (self.vht_cap_info & 0x03) as u8
672 }
673
674 #[must_use]
676 pub fn supported_channel_width(&self) -> u8 {
677 ((self.vht_cap_info >> 2) & 0x03) as u8
678 }
679
680 #[must_use]
682 pub fn short_gi_80(&self) -> bool {
683 self.vht_cap_info & 0x0020 != 0
684 }
685
686 #[must_use]
688 pub fn short_gi_160(&self) -> bool {
689 self.vht_cap_info & 0x0040 != 0
690 }
691}
692
693#[derive(Debug, Clone, PartialEq, Eq)]
699pub struct Dot11EltVHTOperation {
700 pub channel_width: u8,
702 pub center_freq_seg0: u8,
704 pub center_freq_seg1: u8,
706 pub basic_mcs_nss: u16,
708}
709
710pub const VHT_OP_LEN: usize = 5;
712
713impl Dot11EltVHTOperation {
714 #[must_use]
716 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
717 if elt.id != types::ie_id::VHT_OPERATION || elt.info.len() < VHT_OP_LEN {
718 return None;
719 }
720 let d = &elt.info;
721 Some(Self {
722 channel_width: d[0],
723 center_freq_seg0: d[1],
724 center_freq_seg1: d[2],
725 basic_mcs_nss: u16::from_le_bytes([d[3], d[4]]),
726 })
727 }
728
729 #[must_use]
731 pub fn build(&self) -> Dot11Elt {
732 let mut info = Vec::with_capacity(VHT_OP_LEN);
733 info.push(self.channel_width);
734 info.push(self.center_freq_seg0);
735 info.push(self.center_freq_seg1);
736 info.extend_from_slice(&self.basic_mcs_nss.to_le_bytes());
737 Dot11Elt::new(types::ie_id::VHT_OPERATION, info)
738 }
739}
740
741#[derive(Debug, Clone, PartialEq, Eq)]
747pub struct CipherSuite {
748 pub oui: [u8; 3],
750 pub suite_type: u8,
752}
753
754impl CipherSuite {
755 #[must_use]
757 pub fn from_bytes(data: &[u8; 4]) -> Self {
758 Self {
759 oui: [data[0], data[1], data[2]],
760 suite_type: data[3],
761 }
762 }
763
764 #[must_use]
766 pub fn to_bytes(&self) -> [u8; 4] {
767 [self.oui[0], self.oui[1], self.oui[2], self.suite_type]
768 }
769
770 #[must_use]
772 pub fn name(&self) -> &'static str {
773 if self.oui == [0x00, 0x0F, 0xAC] {
774 types::cipher_suite::name(self.suite_type)
775 } else {
776 "Vendor-Specific"
777 }
778 }
779}
780
781#[derive(Debug, Clone, PartialEq, Eq)]
783pub struct AkmSuite {
784 pub oui: [u8; 3],
786 pub suite_type: u8,
788}
789
790impl AkmSuite {
791 #[must_use]
793 pub fn from_bytes(data: &[u8; 4]) -> Self {
794 Self {
795 oui: [data[0], data[1], data[2]],
796 suite_type: data[3],
797 }
798 }
799
800 #[must_use]
802 pub fn to_bytes(&self) -> [u8; 4] {
803 [self.oui[0], self.oui[1], self.oui[2], self.suite_type]
804 }
805
806 #[must_use]
808 pub fn name(&self) -> &'static str {
809 if self.oui == [0x00, 0x0F, 0xAC] {
810 types::akm_suite::name(self.suite_type)
811 } else {
812 "Vendor-Specific"
813 }
814 }
815}
816
817#[derive(Debug, Clone, PartialEq, Eq)]
819pub struct RsnInfo {
820 pub version: u16,
822 pub group_cipher: CipherSuite,
824 pub pairwise_ciphers: Vec<CipherSuite>,
826 pub akm_suites: Vec<AkmSuite>,
828 pub rsn_capabilities: u16,
830 pub pmkid_count: u16,
832 pub pmkids: Vec<[u8; 16]>,
834 pub group_mgmt_cipher: Option<CipherSuite>,
836}
837
838#[derive(Debug, Clone, PartialEq, Eq)]
840pub struct Dot11EltRSN {
841 pub info: RsnInfo,
843}
844
845impl Dot11EltRSN {
846 #[must_use]
848 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
849 if elt.id != types::ie_id::RSN || elt.info.len() < 2 {
850 return None;
851 }
852 let data = &elt.info;
853 let mut offset = 0;
854
855 if offset + 2 > data.len() {
857 return None;
858 }
859 let version = u16::from_le_bytes([data[offset], data[offset + 1]]);
860 offset += 2;
861
862 if offset + 4 > data.len() {
864 return None;
865 }
866 let mut gc = [0u8; 4];
867 gc.copy_from_slice(&data[offset..offset + 4]);
868 let group_cipher = CipherSuite::from_bytes(&gc);
869 offset += 4;
870
871 let pairwise_count = if offset + 2 <= data.len() {
873 let c = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
874 offset += 2;
875 c
876 } else {
877 0
878 };
879
880 let mut pairwise_ciphers = Vec::with_capacity(pairwise_count);
882 for _ in 0..pairwise_count {
883 if offset + 4 > data.len() {
884 break;
885 }
886 let mut suite = [0u8; 4];
887 suite.copy_from_slice(&data[offset..offset + 4]);
888 pairwise_ciphers.push(CipherSuite::from_bytes(&suite));
889 offset += 4;
890 }
891
892 let akm_count = if offset + 2 <= data.len() {
894 let c = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
895 offset += 2;
896 c
897 } else {
898 0
899 };
900
901 let mut akm_suites = Vec::with_capacity(akm_count);
903 for _ in 0..akm_count {
904 if offset + 4 > data.len() {
905 break;
906 }
907 let mut suite = [0u8; 4];
908 suite.copy_from_slice(&data[offset..offset + 4]);
909 akm_suites.push(AkmSuite::from_bytes(&suite));
910 offset += 4;
911 }
912
913 let rsn_capabilities = if offset + 2 <= data.len() {
915 let c = u16::from_le_bytes([data[offset], data[offset + 1]]);
916 offset += 2;
917 c
918 } else {
919 0
920 };
921
922 let pmkid_count = if offset + 2 <= data.len() {
924 let c = u16::from_le_bytes([data[offset], data[offset + 1]]);
925 offset += 2;
926 c
927 } else {
928 0
929 };
930
931 let mut pmkids = Vec::new();
933 for _ in 0..pmkid_count {
934 if offset + 16 > data.len() {
935 break;
936 }
937 let mut pmkid = [0u8; 16];
938 pmkid.copy_from_slice(&data[offset..offset + 16]);
939 pmkids.push(pmkid);
940 offset += 16;
941 }
942
943 let group_mgmt_cipher = if offset + 4 <= data.len() {
945 let mut suite = [0u8; 4];
946 suite.copy_from_slice(&data[offset..offset + 4]);
947 Some(CipherSuite::from_bytes(&suite))
948 } else {
949 None
950 };
951
952 Some(Self {
953 info: RsnInfo {
954 version,
955 group_cipher,
956 pairwise_ciphers,
957 akm_suites,
958 rsn_capabilities,
959 pmkid_count,
960 pmkids,
961 group_mgmt_cipher,
962 },
963 })
964 }
965
966 #[must_use]
968 pub fn build(&self) -> Dot11Elt {
969 let rsn = &self.info;
970 let mut data = Vec::new();
971
972 data.extend_from_slice(&rsn.version.to_le_bytes());
974
975 data.extend_from_slice(&rsn.group_cipher.to_bytes());
977
978 data.extend_from_slice(&(rsn.pairwise_ciphers.len() as u16).to_le_bytes());
980 for cs in &rsn.pairwise_ciphers {
981 data.extend_from_slice(&cs.to_bytes());
982 }
983
984 data.extend_from_slice(&(rsn.akm_suites.len() as u16).to_le_bytes());
986 for akm in &rsn.akm_suites {
987 data.extend_from_slice(&akm.to_bytes());
988 }
989
990 data.extend_from_slice(&rsn.rsn_capabilities.to_le_bytes());
992
993 if !rsn.pmkids.is_empty() {
995 data.extend_from_slice(&(rsn.pmkids.len() as u16).to_le_bytes());
996 for pmkid in &rsn.pmkids {
997 data.extend_from_slice(pmkid);
998 }
999 }
1000
1001 if let Some(ref gmc) = rsn.group_mgmt_cipher {
1003 if rsn.pmkids.is_empty() {
1005 data.extend_from_slice(&0u16.to_le_bytes());
1006 }
1007 data.extend_from_slice(&gmc.to_bytes());
1008 }
1009
1010 Dot11Elt::new(types::ie_id::RSN, data)
1011 }
1012
1013 #[must_use]
1015 pub fn pre_auth(&self) -> bool {
1016 self.info.rsn_capabilities & 0x0001 != 0
1017 }
1018
1019 #[must_use]
1021 pub fn no_pairwise(&self) -> bool {
1022 self.info.rsn_capabilities & 0x0002 != 0
1023 }
1024
1025 #[must_use]
1027 pub fn ptksa_replay_counter(&self) -> u8 {
1028 ((self.info.rsn_capabilities >> 2) & 0x03) as u8
1029 }
1030
1031 #[must_use]
1033 pub fn gtksa_replay_counter(&self) -> u8 {
1034 ((self.info.rsn_capabilities >> 4) & 0x03) as u8
1035 }
1036
1037 #[must_use]
1039 pub fn mfp_required(&self) -> bool {
1040 self.info.rsn_capabilities & 0x0040 != 0
1041 }
1042
1043 #[must_use]
1045 pub fn mfp_capable(&self) -> bool {
1046 self.info.rsn_capabilities & 0x0080 != 0
1047 }
1048}
1049
1050#[derive(Debug, Clone, PartialEq, Eq)]
1056pub struct Dot11EltVendorSpecific {
1057 pub oui: [u8; 3],
1059 pub data: Vec<u8>,
1061}
1062
1063impl Dot11EltVendorSpecific {
1064 #[must_use]
1066 pub fn new(oui: [u8; 3], data: Vec<u8>) -> Self {
1067 Self { oui, data }
1068 }
1069
1070 #[must_use]
1072 pub fn parse(elt: &Dot11Elt) -> Option<Self> {
1073 if elt.id != types::ie_id::VENDOR_SPECIFIC || elt.info.len() < 3 {
1074 return None;
1075 }
1076 let mut oui = [0u8; 3];
1077 oui.copy_from_slice(&elt.info[0..3]);
1078 Some(Self {
1079 oui,
1080 data: elt.info[3..].to_vec(),
1081 })
1082 }
1083
1084 #[must_use]
1086 pub fn build(&self) -> Dot11Elt {
1087 let mut info = Vec::with_capacity(3 + self.data.len());
1088 info.extend_from_slice(&self.oui);
1089 info.extend_from_slice(&self.data);
1090 Dot11Elt::new(types::ie_id::VENDOR_SPECIFIC, info)
1091 }
1092
1093 #[must_use]
1095 pub fn is_wpa(&self) -> bool {
1096 self.oui == types::MICROSOFT_WPA_OUI
1097 && !self.data.is_empty()
1098 && self.data[0] == types::MICROSOFT_WPA_TYPE
1099 }
1100
1101 #[must_use]
1103 pub fn is_wmm(&self) -> bool {
1104 self.oui == types::MICROSOFT_WPA_OUI && !self.data.is_empty() && self.data[0] == 0x02
1105 }
1106}
1107
1108#[derive(Debug, Clone, PartialEq, Eq)]
1117pub struct Dot11EltMicrosoftWPA {
1118 pub version: u16,
1120 pub group_cipher: CipherSuite,
1122 pub pairwise_ciphers: Vec<CipherSuite>,
1124 pub akm_suites: Vec<AkmSuite>,
1126}
1127
1128impl Dot11EltMicrosoftWPA {
1129 #[must_use]
1131 pub fn parse(vs: &Dot11EltVendorSpecific) -> Option<Self> {
1132 if !vs.is_wpa() {
1133 return None;
1134 }
1135 let data = &vs.data;
1137 if data.len() < 7 {
1138 return None;
1140 }
1141 let mut offset = 1; let version = u16::from_le_bytes([data[offset], data[offset + 1]]);
1144 offset += 2;
1145
1146 if offset + 4 > data.len() {
1147 return None;
1148 }
1149 let mut gc = [0u8; 4];
1150 gc.copy_from_slice(&data[offset..offset + 4]);
1151 let group_cipher = CipherSuite::from_bytes(&gc);
1152 offset += 4;
1153
1154 let pw_count = if offset + 2 <= data.len() {
1156 let c = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
1157 offset += 2;
1158 c
1159 } else {
1160 0
1161 };
1162
1163 let mut pairwise_ciphers = Vec::with_capacity(pw_count);
1164 for _ in 0..pw_count {
1165 if offset + 4 > data.len() {
1166 break;
1167 }
1168 let mut suite = [0u8; 4];
1169 suite.copy_from_slice(&data[offset..offset + 4]);
1170 pairwise_ciphers.push(CipherSuite::from_bytes(&suite));
1171 offset += 4;
1172 }
1173
1174 let akm_count = if offset + 2 <= data.len() {
1176 let c = u16::from_le_bytes([data[offset], data[offset + 1]]) as usize;
1177 offset += 2;
1178 c
1179 } else {
1180 0
1181 };
1182
1183 let mut akm_suites = Vec::with_capacity(akm_count);
1184 for _ in 0..akm_count {
1185 if offset + 4 > data.len() {
1186 break;
1187 }
1188 let mut suite = [0u8; 4];
1189 suite.copy_from_slice(&data[offset..offset + 4]);
1190 akm_suites.push(AkmSuite::from_bytes(&suite));
1191 offset += 4;
1192 }
1193
1194 Some(Self {
1195 version,
1196 group_cipher,
1197 pairwise_ciphers,
1198 akm_suites,
1199 })
1200 }
1201
1202 #[must_use]
1204 pub fn build(&self) -> Dot11Elt {
1205 let mut data = Vec::new();
1206
1207 data.extend_from_slice(&types::MICROSOFT_WPA_OUI);
1209 data.push(types::MICROSOFT_WPA_TYPE);
1210
1211 data.extend_from_slice(&self.version.to_le_bytes());
1213
1214 data.extend_from_slice(&self.group_cipher.to_bytes());
1216
1217 data.extend_from_slice(&(self.pairwise_ciphers.len() as u16).to_le_bytes());
1219 for cs in &self.pairwise_ciphers {
1220 data.extend_from_slice(&cs.to_bytes());
1221 }
1222
1223 data.extend_from_slice(&(self.akm_suites.len() as u16).to_le_bytes());
1225 for akm in &self.akm_suites {
1226 data.extend_from_slice(&akm.to_bytes());
1227 }
1228
1229 Dot11Elt::new(types::ie_id::VENDOR_SPECIFIC, data)
1230 }
1231}
1232
1233#[must_use]
1239pub fn find_ie(elements: &[Dot11Elt], id: u8) -> Option<&Dot11Elt> {
1240 elements.iter().find(|e| e.id == id)
1241}
1242
1243#[must_use]
1245pub fn find_all_ies(elements: &[Dot11Elt], id: u8) -> Vec<&Dot11Elt> {
1246 elements.iter().filter(|e| e.id == id).collect()
1247}
1248
1249#[must_use]
1251pub fn extract_ssid(elements: &[Dot11Elt]) -> Option<String> {
1252 find_ie(elements, types::ie_id::SSID).and_then(Dot11Elt::ssid_str)
1253}
1254
1255#[must_use]
1257pub fn extract_channel(elements: &[Dot11Elt]) -> Option<u8> {
1258 find_ie(elements, types::ie_id::DS_PARAMETER_SET)
1259 .and_then(Dot11EltDSSSet::parse)
1260 .map(|ds| ds.channel)
1261}
1262
1263#[cfg(test)]
1268mod tests {
1269 use super::*;
1270
1271 #[test]
1272 fn test_dot11elt_parse_build_roundtrip() {
1273 let elt = Dot11Elt::new(0, b"TestSSID".to_vec());
1274 let wire = elt.build();
1275 assert_eq!(wire[0], 0); assert_eq!(wire[1], 8); assert_eq!(&wire[2..], b"TestSSID");
1278
1279 let (parsed, consumed) = Dot11Elt::parse(&wire, 0).unwrap();
1280 assert_eq!(consumed, 10);
1281 assert_eq!(parsed.id, 0);
1282 assert_eq!(parsed.len, 8);
1283 assert_eq!(&parsed.info, b"TestSSID");
1284 }
1285
1286 #[test]
1287 fn test_dot11elt_parse_all() {
1288 let ssid = Dot11Elt::ssid("MyNetwork");
1289 let rates = Dot11Elt::new(1, vec![0x82, 0x84, 0x8B, 0x96]);
1290 let chain = Dot11Elt::build_chain(&[ssid.clone(), rates.clone()]);
1291
1292 let parsed = Dot11Elt::parse_all(&chain, 0);
1293 assert_eq!(parsed.len(), 2);
1294 assert_eq!(parsed[0].id, 0);
1295 assert_eq!(parsed[0].ssid_str().unwrap(), "MyNetwork");
1296 assert_eq!(parsed[1].id, 1);
1297 assert_eq!(parsed[1].info, vec![0x82, 0x84, 0x8B, 0x96]);
1298 }
1299
1300 #[test]
1301 fn test_ssid_ie() {
1302 let ssid_ie = Dot11EltSSID::new("HelloWiFi");
1303 assert_eq!(ssid_ie.ssid, "HelloWiFi");
1304 assert!(!ssid_ie.is_hidden());
1305
1306 let elt = ssid_ie.build();
1307 assert_eq!(elt.id, 0);
1308 let parsed = Dot11EltSSID::parse(&elt).unwrap();
1309 assert_eq!(parsed.ssid, "HelloWiFi");
1310
1311 let hidden = Dot11EltSSID::new("");
1313 assert!(hidden.is_hidden());
1314 }
1315
1316 #[test]
1317 fn test_rates_ie() {
1318 let rates = Dot11EltRates::new(vec![0x82, 0x84, 0x8B, 0x96, 0x0C, 0x12, 0x18, 0x24]);
1319 let elt = rates.build();
1320 assert_eq!(elt.id, 1);
1321
1322 let parsed = Dot11EltRates::parse(&elt).unwrap();
1323 assert_eq!(parsed.rates.len(), 8);
1324 assert!(Dot11EltRates::is_basic(0x82)); assert!(!Dot11EltRates::is_basic(0x0C)); assert_eq!(Dot11EltRates::rate_mbps(0x82), 1.0);
1327 assert_eq!(Dot11EltRates::rate_mbps(0x0C), 6.0);
1328 }
1329
1330 #[test]
1331 fn test_dsset_ie() {
1332 let ds = Dot11EltDSSSet::new(6);
1333 let elt = ds.build();
1334 assert_eq!(elt.id, 3);
1335 assert_eq!(elt.info, vec![6]);
1336
1337 let parsed = Dot11EltDSSSet::parse(&elt).unwrap();
1338 assert_eq!(parsed.channel, 6);
1339 }
1340
1341 #[test]
1342 fn test_tim_ie() {
1343 let tim = Dot11EltTIM::new(0, 3, 0, vec![0x00]);
1344 let elt = tim.build();
1345 assert_eq!(elt.id, 5);
1346 assert_eq!(elt.info.len(), 4);
1347
1348 let parsed = Dot11EltTIM::parse(&elt).unwrap();
1349 assert_eq!(parsed.dtim_count, 0);
1350 assert_eq!(parsed.dtim_period, 3);
1351 assert_eq!(parsed.bitmap_control, 0);
1352 assert_eq!(parsed.bitmap, vec![0x00]);
1353 }
1354
1355 #[test]
1356 fn test_country_ie() {
1357 let country = Dot11EltCountry::new(
1358 *b"US ",
1359 vec![
1360 CountryTriplet {
1361 first_channel: 1,
1362 num_channels: 11,
1363 max_power: 30,
1364 },
1365 CountryTriplet {
1366 first_channel: 36,
1367 num_channels: 4,
1368 max_power: 17,
1369 },
1370 ],
1371 );
1372 let elt = country.build();
1373 assert_eq!(elt.id, 7);
1374
1375 let parsed = Dot11EltCountry::parse(&elt).unwrap();
1376 assert_eq!(parsed.country_str(), "US ");
1377 assert_eq!(parsed.triplets.len(), 2);
1378 assert_eq!(parsed.triplets[0].first_channel, 1);
1379 assert_eq!(parsed.triplets[0].num_channels, 11);
1380 assert_eq!(parsed.triplets[0].max_power, 30);
1381 assert_eq!(parsed.triplets[1].first_channel, 36);
1382 }
1383
1384 #[test]
1385 fn test_csa_ie() {
1386 let csa = Dot11EltCSA::new(1, 11, 5);
1387 let elt = csa.build();
1388 assert_eq!(elt.id, 37);
1389
1390 let parsed = Dot11EltCSA::parse(&elt).unwrap();
1391 assert_eq!(parsed.mode, 1);
1392 assert_eq!(parsed.new_channel, 11);
1393 assert_eq!(parsed.count, 5);
1394 }
1395
1396 #[test]
1397 fn test_ht_capabilities_ie() {
1398 let ht = Dot11EltHTCapabilities {
1399 ht_cap_info: 0x016F, ampdu_params: 0x17,
1401 mcs_set: [0xFF, 0xFF, 0xFF, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1402 ht_ext_cap: 0x0000,
1403 txbf_cap: 0x00000000,
1404 asel_cap: 0x00,
1405 };
1406 let elt = ht.build();
1407 assert_eq!(elt.id, 45);
1408 assert_eq!(elt.info.len(), HT_CAP_LEN);
1409
1410 let parsed = Dot11EltHTCapabilities::parse(&elt).unwrap();
1411 assert!(parsed.ldpc());
1412 assert!(parsed.channel_width_set());
1413 assert!(parsed.short_gi_20());
1414 assert!(parsed.short_gi_40());
1415 assert_eq!(parsed.ampdu_params, 0x17);
1416 }
1417
1418 #[test]
1419 fn test_ht_info_ie() {
1420 let ht_info = Dot11EltHTInfo {
1421 primary_channel: 6,
1422 ht_info: [0x05, 0x00, 0x00, 0x00, 0x00], basic_mcs_set: [0; 16],
1424 };
1425 let elt = ht_info.build();
1426 assert_eq!(elt.id, 61);
1427 assert_eq!(elt.info.len(), HT_INFO_LEN);
1428
1429 let parsed = Dot11EltHTInfo::parse(&elt).unwrap();
1430 assert_eq!(parsed.primary_channel, 6);
1431 assert_eq!(parsed.secondary_channel_offset(), 1);
1432 assert!(parsed.sta_channel_width());
1433 }
1434
1435 #[test]
1436 fn test_vht_capabilities_ie() {
1437 let vht = Dot11EltVHTCapabilities {
1438 vht_cap_info: 0x00000062, mcs_nss_set: [0xFF, 0xFE, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00],
1440 };
1441 let elt = vht.build();
1442 assert_eq!(elt.id, 191);
1443 assert_eq!(elt.info.len(), VHT_CAP_LEN);
1444
1445 let parsed = Dot11EltVHTCapabilities::parse(&elt).unwrap();
1446 assert_eq!(parsed.max_mpdu_length(), 2);
1447 assert!(parsed.short_gi_80());
1448 assert!(parsed.short_gi_160());
1449 }
1450
1451 #[test]
1452 fn test_vht_operation_ie() {
1453 let vht_op = Dot11EltVHTOperation {
1454 channel_width: 1,
1455 center_freq_seg0: 42,
1456 center_freq_seg1: 0,
1457 basic_mcs_nss: 0xFFFC,
1458 };
1459 let elt = vht_op.build();
1460 assert_eq!(elt.id, 192);
1461 assert_eq!(elt.info.len(), VHT_OP_LEN);
1462
1463 let parsed = Dot11EltVHTOperation::parse(&elt).unwrap();
1464 assert_eq!(parsed.channel_width, 1);
1465 assert_eq!(parsed.center_freq_seg0, 42);
1466 assert_eq!(parsed.center_freq_seg1, 0);
1467 assert_eq!(parsed.basic_mcs_nss, 0xFFFC);
1468 }
1469
1470 #[test]
1471 fn test_rsn_ie_wpa2_psk() {
1472 let rsn = Dot11EltRSN {
1473 info: RsnInfo {
1474 version: 1,
1475 group_cipher: CipherSuite::from_bytes(&[0x00, 0x0F, 0xAC, 0x04]), pairwise_ciphers: vec![CipherSuite::from_bytes(&[0x00, 0x0F, 0xAC, 0x04])],
1477 akm_suites: vec![AkmSuite::from_bytes(&[0x00, 0x0F, 0xAC, 0x02])], rsn_capabilities: 0x000C,
1479 pmkid_count: 0,
1480 pmkids: Vec::new(),
1481 group_mgmt_cipher: None,
1482 },
1483 };
1484
1485 let elt = rsn.build();
1486 assert_eq!(elt.id, 48);
1487
1488 let parsed = Dot11EltRSN::parse(&elt).unwrap();
1489 assert_eq!(parsed.info.version, 1);
1490 assert_eq!(parsed.info.group_cipher.name(), "CCMP-128");
1491 assert_eq!(parsed.info.pairwise_ciphers.len(), 1);
1492 assert_eq!(parsed.info.pairwise_ciphers[0].name(), "CCMP-128");
1493 assert_eq!(parsed.info.akm_suites.len(), 1);
1494 assert_eq!(parsed.info.akm_suites[0].name(), "PSK");
1495 assert_eq!(parsed.info.rsn_capabilities, 0x000C);
1496 }
1497
1498 #[test]
1499 fn test_rsn_ie_with_mfp() {
1500 let rsn = Dot11EltRSN {
1501 info: RsnInfo {
1502 version: 1,
1503 group_cipher: CipherSuite::from_bytes(&[0x00, 0x0F, 0xAC, 0x04]),
1504 pairwise_ciphers: vec![CipherSuite::from_bytes(&[0x00, 0x0F, 0xAC, 0x04])],
1505 akm_suites: vec![AkmSuite::from_bytes(&[0x00, 0x0F, 0xAC, 0x08])], rsn_capabilities: 0x00CC, pmkid_count: 0,
1508 pmkids: Vec::new(),
1509 group_mgmt_cipher: Some(CipherSuite::from_bytes(&[0x00, 0x0F, 0xAC, 0x06])), },
1511 };
1512
1513 let elt = rsn.build();
1514 let parsed = Dot11EltRSN::parse(&elt).unwrap();
1515 assert!(parsed.mfp_required());
1516 assert!(parsed.mfp_capable());
1517 assert!(parsed.info.group_mgmt_cipher.is_some());
1518 assert_eq!(
1519 parsed.info.group_mgmt_cipher.unwrap().name(),
1520 "BIP-CMAC-128"
1521 );
1522 }
1523
1524 #[test]
1525 fn test_vendor_specific_ie() {
1526 let vs = Dot11EltVendorSpecific::new([0x00, 0x50, 0xF2], vec![0x01, 0x01, 0x00]);
1527 assert!(vs.is_wpa());
1528 assert!(!vs.is_wmm());
1529
1530 let elt = vs.build();
1531 assert_eq!(elt.id, 221);
1532
1533 let parsed = Dot11EltVendorSpecific::parse(&elt).unwrap();
1534 assert_eq!(parsed.oui, [0x00, 0x50, 0xF2]);
1535 assert!(parsed.is_wpa());
1536 }
1537
1538 #[test]
1539 fn test_microsoft_wpa_ie() {
1540 let wpa = Dot11EltMicrosoftWPA {
1541 version: 1,
1542 group_cipher: CipherSuite::from_bytes(&[0x00, 0x50, 0xF2, 0x02]), pairwise_ciphers: vec![CipherSuite::from_bytes(&[0x00, 0x50, 0xF2, 0x02])], akm_suites: vec![AkmSuite::from_bytes(&[0x00, 0x50, 0xF2, 0x02])], };
1546
1547 let elt = wpa.build();
1548 assert_eq!(elt.id, 221);
1549
1550 let vs = Dot11EltVendorSpecific::parse(&elt).unwrap();
1552 assert!(vs.is_wpa());
1553
1554 let parsed = Dot11EltMicrosoftWPA::parse(&vs).unwrap();
1555 assert_eq!(parsed.version, 1);
1556 assert_eq!(parsed.group_cipher.to_bytes(), [0x00, 0x50, 0xF2, 0x02]);
1557 assert_eq!(parsed.pairwise_ciphers.len(), 1);
1558 assert_eq!(parsed.akm_suites.len(), 1);
1559 }
1560
1561 #[test]
1562 fn test_find_ie_helpers() {
1563 let elements = vec![
1564 Dot11Elt::ssid("TestNet"),
1565 Dot11Elt::new(1, vec![0x82, 0x84]),
1566 Dot11Elt::new(3, vec![6]),
1567 ];
1568
1569 assert_eq!(extract_ssid(&elements), Some("TestNet".to_string()));
1570 assert_eq!(extract_channel(&elements), Some(6));
1571 assert!(find_ie(&elements, 48).is_none()); }
1573
1574 #[test]
1575 fn test_dot11elt_name() {
1576 let ssid = Dot11Elt::ssid("Test");
1577 assert_eq!(ssid.name(), "SSID");
1578
1579 let rsn = Dot11Elt::new(48, vec![]);
1580 assert_eq!(rsn.name(), "RSN");
1581
1582 let ht = Dot11Elt::new(45, vec![]);
1583 assert_eq!(ht.name(), "HT Capabilities");
1584 }
1585
1586 #[test]
1587 fn test_dot11elt_wire_len() {
1588 let elt = Dot11Elt::new(0, b"Test".to_vec());
1589 assert_eq!(elt.wire_len(), 6); }
1591
1592 #[test]
1593 fn test_parse_truncated_ie() {
1594 let buf = vec![0x00];
1596 assert!(Dot11Elt::parse(&buf, 0).is_err());
1597
1598 let buf = vec![0x00, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05];
1600 assert!(Dot11Elt::parse(&buf, 0).is_err());
1601 }
1602
1603 #[test]
1604 fn test_parse_all_partial() {
1605 let mut buf = Dot11Elt::ssid("AB").build();
1607 buf.extend(Dot11Elt::new(1, vec![0x82]).build());
1608 buf.extend(&[0x03, 0x05]); let parsed = Dot11Elt::parse_all(&buf, 0);
1611 assert_eq!(parsed.len(), 2); }
1613
1614 #[test]
1615 fn test_rates_mbps() {
1616 let rates = Dot11EltRates::new(vec![0x82, 0x84, 0x8B, 0x96, 0x0C, 0x12, 0x18, 0x24]);
1617 let mbps = rates.rates_mbps();
1618 assert_eq!(mbps, vec![1.0, 2.0, 5.5, 11.0, 6.0, 9.0, 12.0, 18.0]);
1619 }
1620}