Skip to main content

macaddr_ouidb/
macaddr.rs

1use core::fmt;
2use core::ops::Sub;
3use core::str::FromStr;
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
7
8use crate::OuiDb;
9
10/// The number of bytes in an ethernet (MAC) address.
11pub const ETHER_ADDR_LEN: usize = 6;
12
13const LOCAL_ADDR_BIT: u8 = 0x02;
14const MULTICAST_ADDR_BIT: u8 = 0x01;
15
16#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)]
17pub enum ParseMacError {
18    #[error("Invalid length")]
19    InvalidLength,
20    #[error("Invalid digit")]
21    InvalidDigit,
22}
23
24/// Mac Address
25#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
26#[repr(transparent)]
27pub struct MacAddress(pub(crate) [u8; 6]);
28
29impl MacAddress {
30    /// Construct a new `MacAddress` instance
31    pub const fn new(mac: [u8; 6]) -> Self {
32        MacAddress(mac)
33    }
34
35    /// Construct a new `MacAddress` instance from 6 components
36    pub const fn new6(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> Self {
37        MacAddress([a, b, c, d, e, f])
38    }
39
40    /// Construct a new `MacAddress` instance from slice
41    pub const fn from_slice(mac: &[u8]) -> Result<Self, ParseMacError> {
42        if mac.len() != 6 {
43            return Err(ParseMacError::InvalidLength);
44        }
45        let bytes = [mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]];
46        Ok(MacAddress(bytes))
47    }
48
49    /// Construct an all-zero `MacAddr` instance.
50    pub const fn zero() -> Self {
51        Self([0; ETHER_ADDR_LEN])
52    }
53
54    /// Construct a broadcast `MacAddr` instance.
55    pub const fn broadcast() -> Self {
56        Self([0xff; ETHER_ADDR_LEN])
57    }
58
59    /// Returns true if a `MacAddr` is an all-zero address.
60    pub fn is_zero(&self) -> bool {
61        *self == Self::zero()
62    }
63
64    /// Returns true if the MacAddr is a universally administered addresses (UAA).
65    pub fn is_universal(&self) -> bool {
66        !self.is_local()
67    }
68
69    /// Returns true if the MacAddr is a locally administered addresses (LAA).
70    pub fn is_local(&self) -> bool {
71        (self.0[0] & LOCAL_ADDR_BIT) == LOCAL_ADDR_BIT
72    }
73
74    /// Returns true if the MacAddr is a unicast address.
75    pub fn is_unicast(&self) -> bool {
76        !self.is_multicast()
77    }
78
79    /// Returns true if the MacAddr is a multicast address.
80    pub fn is_multicast(&self) -> bool {
81        (self.0[0] & MULTICAST_ADDR_BIT) == MULTICAST_ADDR_BIT
82    }
83
84    /// Returns true if the MacAddr is a broadcast address.
85    pub fn is_broadcast(&self) -> bool {
86        *self == Self::broadcast()
87    }
88
89    /// Return true if the MacAddr is a virtual NIC
90    pub fn is_virtual_nic(&self) -> bool {
91        crate::OUI_DB
92            .lookup_mac(*self)
93            .map_or(false, |name| OuiDb::is_virtual_nic(name))
94    }
95
96    /// Return OUI (Organizationally Unique Identifier)
97    pub fn oui(&self) -> Option<&'static str> {
98        crate::OUI_DB.lookup_mac(*self)
99    }
100
101    pub fn octets(&self) -> [u8; 6] {
102        self.0
103    }
104
105    pub fn to_u64(&self) -> u64 {
106        ((self.0[0] as u64) << 40)
107            | ((self.0[1] as u64) << 32)
108            | ((self.0[2] as u64) << 24)
109            | ((self.0[3] as u64) << 16)
110            | ((self.0[4] as u64) << 8)
111            | (self.0[5] as u64)
112    }
113}
114
115impl From<[u8; 6]> for MacAddress {
116    fn from(value: [u8; 6]) -> Self {
117        Self(value)
118    }
119}
120
121impl fmt::Debug for MacAddress {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        write!(
124            f,
125            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
126            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
127        )
128    }
129}
130impl fmt::Display for MacAddress {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        write!(
133            f,
134            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
135            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
136        )
137    }
138}
139
140impl Sub for &MacAddress {
141    type Output = i64;
142
143    fn sub(self, rhs: Self) -> Self::Output {
144        let self_val = self.to_u64();
145        let rhs_val = rhs.to_u64();
146        self_val as i64 - rhs_val as i64
147    }
148}
149
150impl Sub for MacAddress {
151    type Output = i64;
152
153    fn sub(self, rhs: Self) -> Self::Output {
154        let self_val = self.to_u64();
155        let rhs_val = rhs.to_u64();
156        self_val as i64 - rhs_val as i64
157    }
158}
159
160impl FromStr for MacAddress {
161    type Err = ParseMacError;
162
163    /// parse mac address, e.g. `52:54:00:12:34:56` or `52-54-00-12-34-56`
164    fn from_str(s: &str) -> Result<Self, Self::Err> {
165        let value = Self::from_str_sep(s, ':');
166        if value.is_ok() {
167            value
168        } else {
169            Self::from_str_sep(s, '-')
170        }
171    }
172}
173
174impl core::convert::TryFrom<&'_ str> for MacAddress {
175    type Error = ParseMacError;
176
177    fn try_from(value: &str) -> Result<Self, Self::Error> {
178        value.parse()
179    }
180}
181
182impl core::convert::TryFrom<&'_ [u8]> for MacAddress {
183    type Error = ParseMacError;
184
185    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
186        Self::from_slice(value)
187    }
188}
189
190#[cfg(feature = "std")]
191impl core::convert::TryFrom<std::borrow::Cow<'_, str>> for MacAddress {
192    type Error = ParseMacError;
193
194    fn try_from(value: std::borrow::Cow<'_, str>) -> Result<Self, Self::Error> {
195        value.parse()
196    }
197}
198
199impl MacAddress {
200    /// parse mac address, e.g. `52:54:00:12:34:56` or `52-54-00-12-34-56`
201    fn from_str_sep(s: &str, separator: char) -> Result<Self, ParseMacError> {
202        let mut parts = [0u8; 6];
203        let splits = s.split(separator);
204        let mut i = 0;
205        for split in splits {
206            if i == 6 {
207                return Err(ParseMacError::InvalidLength);
208            }
209            match u8::from_str_radix(split, 16) {
210                Ok(b) if split.len() != 0 => parts[i] = b,
211                _ => return Err(ParseMacError::InvalidDigit),
212            }
213            i += 1;
214        }
215
216        if i == 6 {
217            Ok(Self(parts))
218        } else {
219            Err(ParseMacError::InvalidLength)
220        }
221    }
222}
223
224#[cfg(feature = "serde")]
225impl Serialize for MacAddress {
226    /// Serializes the MAC address.
227    ///
228    /// It serializes either to a string or its binary representation, depending on what the format
229    /// prefers.
230    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
231        if serializer.is_human_readable() {
232            serializer.collect_str(self)
233        } else {
234            serializer.serialize_bytes(&self.0)
235        }
236    }
237}
238
239#[cfg(feature = "serde")]
240impl<'de> Deserialize<'de> for MacAddress {
241    /// Deserializes the MAC address.
242    ///
243    /// It deserializes it from either a byte array (of size 6) or a string. If the format is
244    /// self-descriptive (like JSON or MessagePack), it auto-detects it. If not, it obeys the
245    /// human-readable property of the deserializer.
246    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
247        struct MacAddrVisitor;
248        impl<'de> de::Visitor<'de> for MacAddrVisitor {
249            type Value = MacAddress;
250
251            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddress, E> {
252                value.parse().map_err(|err| E::custom(err))
253            }
254
255            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddress, E> {
256                if v.len() == 6 {
257                    Ok(MacAddress::new([v[0], v[1], v[2], v[3], v[4], v[5]]))
258                } else {
259                    Err(E::invalid_length(v.len(), &self))
260                }
261            }
262
263            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
264                write!(
265                    formatter,
266                    "either a string representation of a MAC address or 6-element byte array"
267                )
268            }
269        }
270
271        // Decide what hint to provide to the deserializer based on if it is human readable or not
272        if deserializer.is_human_readable() {
273            deserializer.deserialize_str(MacAddrVisitor)
274        } else {
275            deserializer.deserialize_bytes(MacAddrVisitor)
276        }
277    }
278}
279
280#[cfg(feature = "pnet")]
281impl From<pnet_base::MacAddr> for MacAddress {
282    fn from(value: pnet_base::MacAddr) -> Self {
283        Self(value.octets())
284    }
285}