oui_data/
lib.rs

1//! OUI database, generated from IEEE CSV files.
2//!
3//! Use the `lookup` function to lookup a MAC address in the MA-L, MA-M, MA-S, CID,
4//! and IAB registries.
5//!
6//! ```rs
7//! let record = oui::lookup("00:00:00:00:00:00").unwrap();
8//! assert_eq!(record.organization(), "XEROX CORPORATION");
9//! ```
10//!
11
12/// The registry for an OUI record.
13#[derive(Debug, Copy, Clone)]
14pub enum Registry {
15    /// MA-L 24-bit OUI (Organizationally Unique Identifier) registry.
16    Mal,
17    /// MA-M 28-bit registry.
18    Mam,
19    /// MA-S 36-bit registry.
20    Mas,
21    /// CID 24-bit registry. Entries in the CID registry are used for cases where
22    /// unique MAC addresses are not required.
23    Cid,
24    /// IAB (Individual Address Blocks) registry.
25    Iab,
26}
27
28/// A record from the OUI database.
29#[derive(Debug, Clone)]
30pub struct OuiData {
31    registry: Registry,
32    oui: &'static str,
33    organization: &'static str,
34}
35
36impl OuiData {
37    /// The registry for this record.
38    pub fn registry(&self) -> Registry {
39        self.registry
40    }
41
42    /// The MAC address prefix for this record. This is an upper-case string
43    /// like "000000", representing MAC addresses that are prefixed with "00:00:00".
44    pub fn oui(&self) -> &'static str {
45        self.oui
46    }
47
48    /// The name of the vendor associated with this record.
49    pub fn organization(&self) -> &'static str {
50        self.organization
51    }
52}
53
54/// Retrieve the OUI record for a given MAC address.
55pub fn lookup(mac: &str) -> Option<&'static OuiData> {
56    if mac.chars().all(|c| matches!(c, '0'..='9' | 'A'..='F')) {
57        return lookup_prefix(mac);
58    }
59
60    let mac = mac.to_uppercase().replace([':', '-'], "");
61    lookup_prefix(&mac)
62}
63
64fn lookup_prefix(mac: &str) -> Option<&'static OuiData> {
65    let mut result: Option<&'static OuiData> = None;
66    if mac.len() >= 9 {
67        result = OUI_ENTRIES.get(&mac[..9]);
68    }
69    if mac.len() >= 7 {
70        result = result.or_else(|| OUI_ENTRIES.get(&mac[..7]));
71    }
72    if mac.len() >= 6 {
73        result = result.or_else(|| OUI_ENTRIES.get(&mac[..6]));
74    }
75
76    result
77}
78
79include!(concat!(env!("OUT_DIR"), "/oui.rs"));
80
81#[cfg(test)]
82mod test {
83    use super::*;
84
85    #[test]
86    fn should_lookup_entry_from_oui() {
87        let record = lookup("00:00:00:00:00:00").unwrap();
88        assert_eq!(record.organization(), "XEROX CORPORATION");
89    }
90
91    #[test]
92    fn should_accept_mac_with_dashes() {
93        let record = lookup("00-00-00-00-00-00").unwrap();
94        assert_eq!(record.organization(), "XEROX CORPORATION");
95    }
96
97    #[test]
98    fn should_ignore_case() {
99        let record = lookup("50:a6:d8:00:00:00").unwrap();
100        assert_eq!(record.organization(), "Apple, Inc.");
101    }
102
103    #[test]
104    fn should_ignore_colons() {
105        let record = lookup("50A6D8000000").unwrap();
106        assert_eq!(record.organization(), "Apple, Inc.");
107    }
108
109    #[test]
110    fn should_accept_prefix() {
111        let record = lookup("50A6D8").unwrap();
112        assert_eq!(record.organization(), "Apple, Inc.");
113    }
114
115    #[test]
116    fn should_lookup_entry_from_oui28() {
117        let record = lookup("B8:4C:87:40:00:00").unwrap();
118        assert_eq!(record.organization(), "Blum Novotest GmbH");
119    }
120
121    #[test]
122    fn should_lookup_entry_from_oui36() {
123        let record = lookup("8C:1F:64:AF:A0:00").unwrap();
124        assert_eq!(record.organization(), "DATA ELECTRONIC DEVICES, INC");
125    }
126
127    #[test]
128    fn should_lookup_entry_from_cid() {
129        let record = lookup("EA:27:01:00:00:00").unwrap();
130        assert_eq!(record.organization(), "ACCE Technology Corp.");
131    }
132
133    #[test]
134    fn should_lookup_entry_from_iab() {
135        let record = lookup("40:D8:55:0D:70:00").unwrap();
136        assert_eq!(record.organization(), "Avant Technologies");
137    }
138}