libpacket_core/
macaddr.rs

1// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::error::Error;
10use std::fmt;
11use std::str::FromStr;
12
13#[cfg(feature = "serde")]
14use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
15
16/// The number of bytes in an ethernet (MAC) address.
17pub const ETHER_ADDR_LEN: usize = 6;
18
19/// Structure of a 48-bit Ethernet address.
20type EtherAddr = [u8; ETHER_ADDR_LEN];
21
22const LOCAL_ADDR_BIT: u8 = 0x02;
23const MULTICAST_ADDR_BIT: u8 = 0x01;
24
25/// A MAC address.
26#[derive(PartialEq, Eq, Clone, Copy, Default, Hash, Ord, PartialOrd)]
27pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);
28
29impl MacAddr {
30    /// Construct a new `MacAddr` instance.
31    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
32        MacAddr(a, b, c, d, e, f)
33    }
34
35    /// Construct an all-zero `MacAddr` instance.
36    pub fn zero() -> MacAddr {
37        Default::default()
38    }
39
40    /// Construct a broadcast `MacAddr` instance.
41    pub fn broadcast() -> MacAddr {
42        [0xff; ETHER_ADDR_LEN].into()
43    }
44
45    /// Returns true if a `MacAddr` is an all-zero address.
46    pub fn is_zero(&self) -> bool {
47        *self == Self::zero()
48    }
49
50    /// Returns true if the MacAddr is a universally administered addresses (UAA).
51    pub fn is_universal(&self) -> bool {
52        !self.is_local()
53    }
54
55    /// Returns true if the MacAddr is a locally administered addresses (LAA).
56    pub fn is_local(&self) -> bool {
57        (self.0 & LOCAL_ADDR_BIT) == LOCAL_ADDR_BIT
58    }
59
60    /// Returns true if the MacAddr is a unicast address.
61    pub fn is_unicast(&self) -> bool {
62        !self.is_multicast()
63    }
64
65    /// Returns true if the MacAddr is a multicast address.
66    pub fn is_multicast(&self) -> bool {
67        (self.0 & MULTICAST_ADDR_BIT) == MULTICAST_ADDR_BIT
68    }
69
70    /// Returns true if the MacAddr is a broadcast address.
71    pub fn is_broadcast(&self) -> bool {
72        *self == Self::broadcast()
73    }
74}
75
76impl From<EtherAddr> for MacAddr {
77    fn from(addr: EtherAddr) -> MacAddr {
78        MacAddr(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5])
79    }
80}
81
82impl From<MacAddr> for EtherAddr {
83    fn from(addr: MacAddr) -> Self {
84        [addr.0, addr.1, addr.2, addr.3, addr.4, addr.5]
85    }
86}
87
88impl PartialEq<EtherAddr> for MacAddr {
89    fn eq(&self, other: &EtherAddr) -> bool {
90        *self == MacAddr::from(*other)
91    }
92}
93
94impl fmt::Display for MacAddr {
95    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
96        write!(
97            fmt,
98            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
99            self.0, self.1, self.2, self.3, self.4, self.5
100        )
101    }
102}
103
104#[cfg(feature = "serde")]
105impl Serialize for MacAddr {
106    /// Serializes the MAC address.
107    ///
108    /// It serializes either to a string or its binary representation, depending on what the format
109    /// prefers.
110    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
111        if serializer.is_human_readable() {
112            serializer.serialize_str(&format!("{}", self))
113        } else {
114            serializer.serialize_bytes(&[self.0, self.1, self.2, self.3, self.4, self.5])
115        }
116    }
117}
118
119#[cfg(feature = "serde")]
120impl<'de> Deserialize<'de> for MacAddr {
121    /// Deserializes the MAC address.
122    ///
123    /// It deserializes it from either a byte array (of size 6) or a string. If the format is
124    /// self-descriptive (like JSON or MessagePack), it auto-detects it. If not, it obeys the
125    /// human-readable property of the deserializer.
126    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
127        struct MacAddrVisitor;
128        impl<'de> de::Visitor<'de> for MacAddrVisitor {
129            type Value = MacAddr;
130
131            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddr, E> {
132                value.parse().map_err(|err| E::custom(&format!("{}", err)))
133            }
134
135            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddr, E> {
136                if v.len() == 6 {
137                    Ok(MacAddr::new(v[0], v[1], v[2], v[3], v[4], v[5]))
138                } else {
139                    Err(E::invalid_length(v.len(), &self))
140                }
141            }
142
143            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
144                write!(
145                    formatter,
146                    "either a string representation of a MAC address or 6-element byte array"
147                )
148            }
149        }
150
151        // Decide what hint to provide to the deserializer based on if it is human readable or not
152        if deserializer.is_human_readable() {
153            deserializer.deserialize_str(MacAddrVisitor)
154        } else {
155            deserializer.deserialize_bytes(MacAddrVisitor)
156        }
157    }
158}
159
160impl fmt::Debug for MacAddr {
161    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
162        fmt::Display::fmt(self, fmt)
163    }
164}
165
166/// Represents an error which occurred whilst parsing a MAC address.
167#[derive(Copy, Debug, PartialEq, Eq, Clone)]
168pub enum ParseMacAddrErr {
169    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66.
170    TooManyComponents,
171    /// The MAC address has too few components, eg. 00:11.
172    TooFewComponents,
173    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55.
174    InvalidComponent,
175}
176
177impl Error for ParseMacAddrErr {}
178
179impl ParseMacAddrErr {
180    fn description(&self) -> &str {
181        match *self {
182            ParseMacAddrErr::TooManyComponents => "Too many components in a MAC address string",
183            ParseMacAddrErr::TooFewComponents => "Too few components in a MAC address string",
184            ParseMacAddrErr::InvalidComponent => "Invalid component in a MAC address string",
185        }
186    }
187}
188
189impl fmt::Display for ParseMacAddrErr {
190    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191        write!(f, "{}", self.description())
192    }
193}
194
195impl FromStr for MacAddr {
196    type Err = ParseMacAddrErr;
197    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
198        let mut parts = [0u8; 6];
199        let splits = s.split(':');
200        let mut i = 0;
201        for split in splits {
202            if i == 6 {
203                return Err(ParseMacAddrErr::TooManyComponents);
204            }
205            match u8::from_str_radix(split, 16) {
206                Ok(b) if split.len() != 0 => parts[i] = b,
207                _ => return Err(ParseMacAddrErr::InvalidComponent),
208            }
209            i += 1;
210        }
211
212        if i == 6 {
213            Ok(MacAddr(
214                parts[0], parts[1], parts[2], parts[3], parts[4], parts[5],
215            ))
216        } else {
217            Err(ParseMacAddrErr::TooFewComponents)
218        }
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225
226    #[test]
227    fn mac_addr_from_str() {
228        assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr::zero()));
229        assert_eq!("ff:ff:ff:ff:ff:ff".parse(), Ok(MacAddr::broadcast()));
230        assert_eq!(
231            "12:34:56:78:90:ab".parse(),
232            Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB))
233        );
234        assert_eq!(
235            "::::::".parse::<MacAddr>(),
236            Err(ParseMacAddrErr::InvalidComponent)
237        );
238        assert_eq!(
239            "0::::::".parse::<MacAddr>(),
240            Err(ParseMacAddrErr::InvalidComponent)
241        );
242        assert_eq!(
243            "::::0::".parse::<MacAddr>(),
244            Err(ParseMacAddrErr::InvalidComponent)
245        );
246        assert_eq!(
247            "12:34:56:78".parse::<MacAddr>(),
248            Err(ParseMacAddrErr::TooFewComponents)
249        );
250        assert_eq!(
251            "12:34:56:78:".parse::<MacAddr>(),
252            Err(ParseMacAddrErr::InvalidComponent)
253        );
254        assert_eq!(
255            "12:34:56:78:90".parse::<MacAddr>(),
256            Err(ParseMacAddrErr::TooFewComponents)
257        );
258        assert_eq!(
259            "12:34:56:78:90:".parse::<MacAddr>(),
260            Err(ParseMacAddrErr::InvalidComponent)
261        );
262        assert_eq!(
263            "12:34:56:78:90:00:00".parse::<MacAddr>(),
264            Err(ParseMacAddrErr::TooManyComponents)
265        );
266        assert_eq!(
267            "xx:xx:xx:xx:xx:xx".parse::<MacAddr>(),
268            Err(ParseMacAddrErr::InvalidComponent)
269        );
270    }
271
272    #[test]
273    fn str_from_mac_addr() {
274        assert_eq!(format!("{}", MacAddr::zero()), "00:00:00:00:00:00");
275        assert_eq!(format!("{}", MacAddr::broadcast()), "ff:ff:ff:ff:ff:ff");
276        assert_eq!(
277            format!("{}", MacAddr(0x12, 0x34, 0x56, 0x78, 0x09, 0xAB)),
278            "12:34:56:78:09:ab"
279        );
280    }
281
282    #[test]
283    fn type_of_addr() {
284        assert!(MacAddr::zero().is_zero());
285        assert!(MacAddr::broadcast().is_broadcast());
286
287        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
288        assert!(mac.is_local());
289        assert!(mac.is_unicast());
290
291        let mac = MacAddr(0xac, 0x87, 0xa3, 0x07, 0x32, 0xb8);
292        assert!(mac.is_universal());
293        assert!(mac.is_unicast());
294    }
295
296    #[test]
297    fn convertion() {
298        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
299        let addr = [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB];
300
301        assert_eq!(mac, MacAddr::from(addr));
302        assert_eq!(addr, EtherAddr::from(mac));
303        assert!(mac == addr);
304    }
305
306    #[cfg(feature = "serde")]
307    mod serde {
308        extern crate serde_test;
309        use self::serde_test::{
310            assert_de_tokens, assert_de_tokens_error, assert_tokens, Compact, Configure, Readable,
311            Token,
312        };
313        use super::*;
314
315        #[test]
316        fn string() {
317            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
318            assert_tokens(&mac.readable(), &[Token::Str("11:22:33:44:55:66")]);
319            assert_de_tokens(&mac.readable(), &[Token::String("11:22:33:44:55:66")]);
320            assert_de_tokens(&mac.readable(), &[Token::BorrowedStr("11:22:33:44:55:66")]);
321            assert_de_tokens_error::<Readable<MacAddr>>(
322                &[Token::Str("not an address")],
323                "Invalid component in a MAC address string",
324            );
325            // It still can detect bytes if provided
326            assert_de_tokens(
327                &mac.readable(),
328                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
329            );
330        }
331
332        #[test]
333        fn bytes() {
334            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
335            assert_tokens(
336                &mac.compact(),
337                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
338            );
339            assert_de_tokens(
340                &mac.compact(),
341                &[Token::BorrowedBytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
342            );
343            let err = "invalid length 2, expected either a string representation of a MAC address or 6-element byte array";
344            assert_de_tokens_error::<Compact<MacAddr>>(&[Token::Bytes(&[0x11, 0x33])], err);
345            let err = "invalid length 7, expected either a string representation of a MAC address or 6-element byte array";
346            assert_de_tokens_error::<Compact<MacAddr>>(
347                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77])],
348                err,
349            );
350            // Still can decode string in the compact mode
351            assert_de_tokens(&mac.compact(), &[Token::Str("11:22:33:44:55:66")]);
352        }
353    }
354}