1use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
6
7#[repr(C, packed)]
8#[derive(
9 Eq, PartialEq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug,
10)]
11pub struct Id(pub u8);
12
13impl Id {
15 pub const SSID: Self = Self(0);
16 pub const SUPPORTED_RATES: Self = Self(1);
17 pub const DSSS_PARAM_SET: Self = Self(3);
18 pub const TIM: Self = Self(5);
19 pub const COUNTRY: Self = Self(7);
20 pub const CHANNEL_SWITCH_ANNOUNCEMENT: Self = Self(37);
21 pub const HT_CAPABILITIES: Self = Self(45);
22 pub const RSNE: Self = Self(48);
23 pub const EXTENDED_SUPPORTED_RATES: Self = Self(50);
24 pub const MOBILITY_DOMAIN: Self = Self(54);
25 pub const EXTENDED_CHANNEL_SWITCH_ANNOUNCEMENT: Self = Self(60);
26 pub const HT_OPERATION: Self = Self(61);
27 pub const SECONDARY_CHANNEL_OFFSET: Self = Self(62);
28 pub const RM_ENABLED_CAPABILITIES: Self = Self(70);
29 pub const BSS_MAX_IDLE_PERIOD: Self = Self(90);
30 pub const MESH_PEERING_MGMT: Self = Self(117);
31 pub const EXT_CAPABILITIES: Self = Self(127);
32 pub const PREQ: Self = Self(130);
33 pub const PREP: Self = Self(131);
34 pub const PERR: Self = Self(132);
35 pub const VHT_CAPABILITIES: Self = Self(191);
36 pub const VHT_OPERATION: Self = Self(192);
37 pub const WIDE_BANDWIDTH_CHANNEL_SWITCH: Self = Self(194);
38 pub const TRANSMIT_POWER_ENVELOPE: Self = Self(195);
39 pub const CHANNEL_SWITCH_WRAPPER: Self = Self(196);
40 pub const VENDOR_SPECIFIC: Self = Self(221);
41 pub const RSNXE: Self = Self(244);
42 pub const EXTENSION: Self = Self(255);
43}
44
45#[repr(C, packed)]
46#[derive(
47 Eq, PartialEq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug,
48)]
49pub struct ExtendedId(pub u8);
50
51impl ExtendedId {
53 pub const DIFFIE_HELLMAN_PARAM: Self = Self(32);
54}
55
56#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
57pub enum IeType {
58 Ieee {
59 id: Id,
60 extension: Option<u8>,
61 },
62 Vendor4 {
64 vendor_ie_hdr: [u8; 4], },
66 Vendor6 {
72 vendor_ie_hdr: [u8; 6], },
77}
78
79const OWE_TRANSITION_HEADER: [u8; 4] = [0x50, 0x6f, 0x9a, 0x1c];
80const WMM_INFO_HEADER: [u8; 6] = [0x00, 0x50, 0xf2, 0x02, 0x00, 0x01];
81const WMM_PARAM_HEADER: [u8; 6] = [0x00, 0x50, 0xf2, 0x02, 0x01, 0x01];
82
83macro_rules! ie_type_basic_const {
84 ($id:ident) => {
85 pub const $id: Self = Self::new_basic(Id::$id);
86 };
87}
88
89macro_rules! ie_type_extended_const {
90 ($id:ident) => {
91 pub const $id: Self = Self::new_extended(ExtendedId::$id.0);
92 };
93}
94
95impl IeType {
96 ie_type_basic_const!(SSID);
97 ie_type_basic_const!(SUPPORTED_RATES);
98 ie_type_basic_const!(DSSS_PARAM_SET);
99 ie_type_basic_const!(TIM);
100 ie_type_basic_const!(COUNTRY);
101 ie_type_basic_const!(CHANNEL_SWITCH_ANNOUNCEMENT);
102 ie_type_basic_const!(HT_CAPABILITIES);
103 ie_type_basic_const!(RSNE);
104 ie_type_basic_const!(EXTENDED_SUPPORTED_RATES);
105 ie_type_basic_const!(MOBILITY_DOMAIN);
106 ie_type_basic_const!(EXTENDED_CHANNEL_SWITCH_ANNOUNCEMENT);
107 ie_type_basic_const!(HT_OPERATION);
108 ie_type_basic_const!(SECONDARY_CHANNEL_OFFSET);
109 ie_type_basic_const!(RM_ENABLED_CAPABILITIES);
110 ie_type_basic_const!(BSS_MAX_IDLE_PERIOD);
111 ie_type_basic_const!(MESH_PEERING_MGMT);
112 ie_type_basic_const!(EXT_CAPABILITIES);
113 ie_type_basic_const!(PREQ);
114 ie_type_basic_const!(PREP);
115 ie_type_basic_const!(PERR);
116 ie_type_basic_const!(VHT_CAPABILITIES);
117 ie_type_basic_const!(VHT_OPERATION);
118 ie_type_basic_const!(WIDE_BANDWIDTH_CHANNEL_SWITCH);
119 ie_type_basic_const!(TRANSMIT_POWER_ENVELOPE);
120 ie_type_basic_const!(CHANNEL_SWITCH_WRAPPER);
121 ie_type_basic_const!(RSNXE);
122
123 ie_type_extended_const!(DIFFIE_HELLMAN_PARAM);
124
125 pub const OWE_TRANSITION: Self = Self::new_vendor4(OWE_TRANSITION_HEADER);
126 pub const WMM_INFO: Self = Self::new_vendor6(WMM_INFO_HEADER);
127 pub const WMM_PARAM: Self = Self::new_vendor6(WMM_PARAM_HEADER);
128
129 pub const fn new_basic(id: Id) -> Self {
130 Self::Ieee { id, extension: None }
131 }
132
133 pub const fn new_extended(ext_id: u8) -> Self {
134 Self::Ieee { id: Id::EXTENSION, extension: Some(ext_id) }
135 }
136
137 pub fn new_vendor(vendor_ie_body: &[u8]) -> Option<Self> {
138 if vendor_ie_body.len() >= 4 && vendor_ie_body[0..4] == OWE_TRANSITION_HEADER {
139 Some(Self::OWE_TRANSITION)
140 } else if vendor_ie_body.len() >= 6 {
141 let mut header = [0u8; 6];
142 header.copy_from_slice(&vendor_ie_body[0..6]);
143 Some(Self::new_vendor6(header))
144 } else {
145 None
146 }
147 }
148
149 pub const fn new_vendor4(vendor_ie_hdr: [u8; 4]) -> Self {
150 Self::Vendor4 { vendor_ie_hdr }
151 }
152
153 pub const fn new_vendor6(vendor_ie_hdr: [u8; 6]) -> Self {
154 Self::Vendor6 { vendor_ie_hdr }
155 }
156
157 pub const fn basic_id(&self) -> Id {
158 match self {
159 Self::Ieee { id, .. } => *id,
160 Self::Vendor4 { .. } | Self::Vendor6 { .. } => Id::VENDOR_SPECIFIC,
161 }
162 }
163
164 pub fn extra_len(&self) -> usize {
166 self.extra_bytes().len()
167 }
168
169 pub fn extra_bytes(&self) -> &[u8] {
171 match self {
172 Self::Ieee { extension, .. } => {
173 extension.as_ref().map(|ext_id| std::slice::from_ref(ext_id)).unwrap_or(&[])
174 }
175 Self::Vendor4 { vendor_ie_hdr } => &vendor_ie_hdr[..],
176 Self::Vendor6 { vendor_ie_hdr } => &vendor_ie_hdr[..],
177 }
178 }
179}
180
181impl std::cmp::PartialOrd for IeType {
182 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
183 Some(self.cmp(other))
184 }
185}
186
187impl std::cmp::Ord for IeType {
188 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
189 match (self, other) {
190 (
191 Self::Ieee { extension: Some(ext_id), .. },
192 Self::Ieee { extension: Some(other_ext_id), .. },
193 ) => ext_id.cmp(other_ext_id),
194 (Self::Vendor4 { vendor_ie_hdr }, Self::Vendor4 { vendor_ie_hdr: other_hdr }) => {
195 vendor_ie_hdr.cmp(other_hdr)
196 }
197 (Self::Vendor6 { vendor_ie_hdr }, Self::Vendor6 { vendor_ie_hdr: other_hdr }) => {
198 vendor_ie_hdr.cmp(other_hdr)
199 }
200 (Self::Ieee { .. }, _) | (_, Self::Ieee { .. }) => {
201 self.basic_id().0.cmp(&other.basic_id().0)
202 }
203 (Self::Vendor4 { .. }, Self::Vendor6 { .. })
204 | (Self::Vendor6 { .. }, Self::Vendor4 { .. }) => {
205 self.extra_bytes().cmp(other.extra_bytes())
206 }
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_ie_type() {
217 let basic = IeType::new_basic(Id::SSID);
218 let extended = IeType::new_extended(2);
219 let vendor4 = IeType::new_vendor4([1, 2, 3, 4]);
220 let vendor6 = IeType::new_vendor6([1, 2, 3, 4, 5, 6]);
221
222 assert_eq!(basic, IeType::Ieee { id: Id::SSID, extension: None });
223 assert_eq!(extended, IeType::Ieee { id: Id::EXTENSION, extension: Some(2) });
224 assert_eq!(vendor4, IeType::Vendor4 { vendor_ie_hdr: [1, 2, 3, 4] });
225 assert_eq!(vendor6, IeType::Vendor6 { vendor_ie_hdr: [1, 2, 3, 4, 5, 6] });
226 assert_eq!(basic.basic_id(), Id::SSID);
227 assert_eq!(extended.basic_id(), Id::EXTENSION);
228 assert_eq!(vendor4.basic_id(), Id::VENDOR_SPECIFIC);
229 assert_eq!(vendor6.basic_id(), Id::VENDOR_SPECIFIC);
230 }
231}