macaddr_ouidb/oui.rs
1//! # Organizationally Unique Identifiers (OUI) database
2//!
3//! ## 查询
4//! 1. 取 mac 前 3 个字节,二分查找 `OuiDb::oui_24` `prefix`, 找不到返回 None,获取 `loc` 对应值
5//! 2. 如 loc bit-31-30 = 0,则计算 offset/length,返回 `OuiDb::names[offset..offset+length]`
6//! 2. 如 loc bit-31-30 = 1,取 mac [第 4 个字节 & 0xF0], 查找 `OuiDb::oui_28`
7//! 2. 如 loc bit-31-30 = 2,取 mac [第 4 个字节,第 5 个字节 & 0xF0], 查找 `OuiDb::oui_36`
8//!
9use crate::MacAddress;
10
11const OUI_SUBTABLE: &str = "Ieee Registration Authority";
12
13const OUI_VIRTUAL: [&'static str; 7] = [
14 "VMware",
15 "QEMU virtual NIC",
16 "Bochs virtual NIC",
17 "PearPC virtual NIC",
18 "Cooperative Linux virtual NIC",
19 "Oracle VirtualBox virtual NIC",
20 "OpenStack",
21];
22
23/// # Organizationally Unique Identifiers (OUI) database
24///
25
26pub struct OuiDb {
27 pub(crate) names: &'static str,
28
29 /// MA-L(24 bit prefix) 24 bit ≈1677 万 传统大厂/运营商
30 pub(crate) oui_24: &'static OuiTable<3>,
31
32 /// MA-M(28 bit prefix) 20 bit ≈104 万 中型设备商/模组厂
33 pub(crate) oui_28: &'static [OuiSubtable<u8>],
34
35 /// MA-S(36 bit prefix) 12 bit =4096 IoT/小微/专用硬件
36 pub(crate) oui_36: &'static [OuiSubtable<u16>],
37}
38
39/// # Locate org name or subtable offset
40///
41/// ## Type 00 - 指向 names 字符串
42/// ```text
43/// bit: 31 30 | 29 28 ... 11 10 09 08 | 07 06 ... 01 00
44/// ───── ───────────────────── ───────────────
45/// type offset (22 bits) length
46/// ```
47///
48/// ## Type 01/02/03 - 指向子表偏移量
49/// ```text
50/// bit: 31 30 | 29 28 ... 17 16 | 15 14 ... 01 00
51/// ───── ─────────────── ───────────────
52/// type reserved subtable index
53/// ```
54///
55/// * bit 31-30: type
56/// - 00 指向 OuiDb::names 中的偏移量和长度
57/// - bit-29-08: offset (22 bits)
58/// - bit-07-00: length (8 bits)
59/// - 01 指向 OuiDb::oui_28 偏移量
60/// - bit-15-00: subtable index (16 bits)
61/// - 02 指向 OuiDb::oui_36 偏移量
62/// - bit-15-00: subtable index (16 bits)
63///
64type OuiLoc = u32;
65
66/// 列存储(节约内存,提升缓存利用率)
67pub(crate) struct OuiTable<const N: usize> {
68 pub(crate) prefix: &'static [[u8; N]],
69 pub(crate) loc: &'static [OuiLoc],
70}
71
72/// 列存储(节约内存,提升缓存利用率)
73pub(crate) struct OuiSubtable<T: 'static> {
74 pub(crate) prefix: &'static [T],
75 pub(crate) loc: &'static [OuiLoc],
76}
77
78impl OuiDb {
79 /// Checks if the given OUI name corresponds to a virtual NIC.
80 ///
81 /// # Arguments
82 ///
83 /// * `name` - The OUI name to check.
84 ///
85 /// # Returns
86 ///
87 /// Returns `true` if the name is a virtual NIC (e.g., QEMU, VirtualBox), otherwise `false`.
88 pub fn is_virtual_nic(name: &str) -> bool {
89 OUI_VIRTUAL.contains(&name)
90 }
91
92 /// Returns the name identifier of the subtable (`oui_28`, `oui_32`, `oui_36`) in the database.
93 ///
94 /// # Returns
95 ///
96 /// Returns the subtable name string `"Ieee Registration Authority"`.
97 pub fn oui_subtable_name() -> &'static str {
98 OUI_SUBTABLE
99 }
100
101 /// Looks up the organization name (OUI vendor information) for a given MAC address.
102 ///
103 /// # Arguments
104 ///
105 /// * `mac` - The MAC address as a [`MacAddress`] type.
106 ///
107 /// # Returns
108 ///
109 /// Returns `Some(&'static str)` if the organization name is found, otherwise `None`.
110 ///
111 /// # Example
112 ///
113 /// ```
114 /// use macaddr_ouidb::*;
115 /// let mac = MacAddress::new([0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E]);
116 /// let org = OUI_DB.lookup_mac(mac);
117 /// ```
118 pub fn lookup_mac(&self, mac: MacAddress) -> Option<&'static str> {
119 self.lookup(mac.octets())
120 }
121
122 /// Looks up the organization name (OUI vendor information) for a given MAC address byte array.
123 ///
124 /// Supports 24-bit, 28-bit, and 36-bit OUI queries.
125 ///
126 /// # Arguments
127 ///
128 /// * `mac` - A 6-byte MAC address byte array.
129 ///
130 /// # Returns
131 ///
132 /// Returns `Some(&'static str)` if the organization name is found. Returns `None` if the MAC
133 /// address length is not 6 bytes or no match is found.
134 ///
135 // # Query Logic
136 //
137 // 1. Extract the first 3 bytes and perform a binary search in `oui_24.prefix`
138 // 2. Based on `loc` bits 31-30, determine the type:
139 // - `00`: Retrieve string from `names` using offset and length
140 // - `01`: Look up in `oui_28` subtable using `mac[3] & 0xF0`
141 // - `02`: Look up in `oui_36` subtable using `mac[3]` and `mac[4] & 0xF0` combined
142 pub fn lookup(&self, mac: [u8; 6]) -> Option<&'static str> {
143 // 1. 提取前 3 字节作为 oui_24 的查找键
144 let key3 = [mac[0], mac[1], mac[2]];
145
146 // 2. 二分查找 oui_24
147 let idx = match self.oui_24.prefix.binary_search(&key3) {
148 Ok(idx) => idx,
149 Err(_) => return None,
150 };
151
152 // 3. 获取 loc 值
153 let loc = self.oui_24.loc[idx];
154
155 // 4. 根据 bit-31-30 判断类型
156 match (loc >> 30) & 0x03 {
157 0 => {
158 // names 偏移量和长度
159 // bit-29-08: offset (22 bits), bit-07-00: length (8 bits)
160 let offset = ((loc >> 8) & 0x3FFFFF) as usize;
161 let length = (loc & 0xFF) as usize;
162 self.names.get(offset..offset + length)
163 }
164 1 => {
165 // oui_28: 取 mac[3] & 0xF0
166 let sub_idx = (loc & 0xFFFF) as usize;
167 let key4 = mac[3] & 0xF0;
168 self.lookup_subtable(&self.oui_28[sub_idx], key4)
169 }
170 2 => {
171 // oui_36: 取 mac[3] 和 mac[4] & 0xF0
172 // 组合为 16 位:高 8 位是 mac[3], 低 8 位是 mac[4] & 0xF0
173 let sub_idx = (loc & 0xFFFF) as usize;
174 let key5 = ((mac[3] as u16) << 8) | ((mac[4] & 0xF0) as u16);
175 self.lookup_subtable(&self.oui_36[sub_idx], key5)
176 }
177 _ => None,
178 }
179 }
180
181 fn lookup_subtable<T: Ord + Copy>(
182 &self,
183 subtable: &OuiSubtable<T>,
184 key: T,
185 ) -> Option<&'static str> {
186 let idx = match subtable.prefix.binary_search(&key) {
187 Ok(idx) => idx,
188 Err(_) => return None,
189 };
190 let loc = subtable.loc[idx];
191
192 // subtable 的 loc 只可能是 type 0 (指向 names)
193 // bit-29-08: offset (22 bits), bit-07-00: length (8 bits)
194 let offset = ((loc >> 8) & 0x3FFFFF) as usize;
195 let length = (loc & 0xFF) as usize;
196 self.names.get(offset..offset + length)
197 }
198}