wlan_common/ie/
id.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
13// IEEE Std 802.11-2024, 9.4.2.1, Table 9-130
14impl 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
51// IEEE Std 802.11-2024, 9.4.2.1, Table 9-130
52impl 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    // Known specific vendor IE with 4 bytes header (OUI + OUI type), such as Owe Transition IE.
63    Vendor4 {
64        vendor_ie_hdr: [u8; 4], // OUI (3B), OUI type (1B)
65    },
66    // Known specific vendor IE with 6 bytes header (OUI + OUI type + type + subtype), such as
67    // WMM Param and WMM Info IEs.
68    //
69    // Unknown vendor IEs are also placed here.
70    // TODO(https://fxbug.dev/42150940): Consider adding a separate variant for unknown vendor IEs
71    Vendor6 {
72        // TODO(https://fxbug.dev/42150940): The Vendor Specific element defined by IEEE 802.11-2016 9.4.2.26
73        // does not have a header length of 6. Instead, the OUI is noted to be either 3 or 5
74        // bytes and the vendor determines the remainder of the contents.
75        vendor_ie_hdr: [u8; 6], // OUI (3B), OUI type (1B), type (1B), subtype (1B)
76    },
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    /// Number of bytes consumed from the IE body to construct IeType
165    pub fn extra_len(&self) -> usize {
166        self.extra_bytes().len()
167    }
168
169    /// Return the bytes consumed from the IE body (not IE header) to construct IeType
170    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}