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