xref: /cloud-hypervisor/net_util/src/mac.rs (revision adb318f4cd0079246b3cb07e01c4e978330445d2)
1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5 // Use of this source code is governed by a BSD-style license that can be
6 // found in the THIRD-PARTY file.
7 
8 use std::fmt;
9 use std::io;
10 use std::str::FromStr;
11 
12 use serde::de::{Deserialize, Deserializer, Error};
13 use serde::ser::{Serialize, Serializer};
14 
15 pub const MAC_ADDR_LEN: usize = 6;
16 
17 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
18 pub struct MacAddr {
19     bytes: [u8; MAC_ADDR_LEN],
20 }
21 
22 impl MacAddr {
23     pub fn parse_str<S>(s: &S) -> Result<MacAddr, io::Error>
24     where
25         S: AsRef<str> + ?Sized,
26     {
27         let v: Vec<&str> = s.as_ref().split(':').collect();
28         let mut bytes = [0u8; MAC_ADDR_LEN];
29         let common_err = Err(io::Error::new(
30             io::ErrorKind::Other,
31             format!("parsing of {} into a MAC address failed", s.as_ref()),
32         ));
33 
34         if v.len() != MAC_ADDR_LEN {
35             return common_err;
36         }
37 
38         for i in 0..MAC_ADDR_LEN {
39             if v[i].len() != 2 {
40                 return common_err;
41             }
42             bytes[i] = u8::from_str_radix(v[i], 16).map_err(|e| {
43                 io::Error::new(
44                     io::ErrorKind::Other,
45                     format!("parsing of {} into a MAC address failed: {}", s.as_ref(), e),
46                 )
47             })?;
48         }
49 
50         Ok(MacAddr { bytes })
51     }
52 
53     // Does not check whether src.len() == MAC_ADDR_LEN.
54     #[inline]
55     pub fn from_bytes_unchecked(src: &[u8]) -> MacAddr {
56         // TODO: using something like std::mem::uninitialized could avoid the extra initialization,
57         // if this ever becomes a performance bottleneck.
58         let mut bytes = [0u8; MAC_ADDR_LEN];
59         bytes[..].copy_from_slice(src);
60 
61         MacAddr { bytes }
62     }
63 
64     // An error can only occur if the slice length is different from MAC_ADDR_LEN.
65     #[inline]
66     pub fn from_bytes(src: &[u8]) -> Result<MacAddr, io::Error> {
67         if src.len() != MAC_ADDR_LEN {
68             return Err(io::Error::new(
69                 io::ErrorKind::Other,
70                 format!("invalid length of slice: {} vs {}", src.len(), MAC_ADDR_LEN),
71             ));
72         }
73         Ok(MacAddr::from_bytes_unchecked(src))
74     }
75 
76     #[inline]
77     pub fn get_bytes(&self) -> &[u8] {
78         &self.bytes
79     }
80 
81     pub fn local_random() -> MacAddr {
82         // Generate a fully random MAC
83         let mut random_bytes = [0u8; MAC_ADDR_LEN];
84         if let Err(e) = getrandom::getrandom(&mut random_bytes) {
85             error!(
86                 "Error populating MAC address with random data: {}",
87                 e.to_string()
88             );
89         }
90 
91         // Set the first byte to make the OUI a locally administered OUI
92         random_bytes[0] = 0x2e;
93 
94         MacAddr {
95             bytes: random_bytes,
96         }
97     }
98 }
99 
100 impl fmt::Display for MacAddr {
101     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102         let b = &self.bytes;
103         write!(
104             f,
105             "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
106             b[0], b[1], b[2], b[3], b[4], b[5]
107         )
108     }
109 }
110 
111 impl Serialize for MacAddr {
112     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
113     where
114         S: Serializer,
115     {
116         self.to_string().serialize(serializer)
117     }
118 }
119 
120 impl<'de> Deserialize<'de> for MacAddr {
121     fn deserialize<D>(deserializer: D) -> Result<MacAddr, D::Error>
122     where
123         D: Deserializer<'de>,
124     {
125         let s = String::deserialize(deserializer)?;
126         MacAddr::parse_str(&s)
127             .map_err(|e| D::Error::custom(format!("The provided MAC address is invalid: {e}")))
128     }
129 }
130 
131 pub enum MacAddrParseError {
132     InvalidValue(String),
133 }
134 
135 impl FromStr for MacAddr {
136     type Err = MacAddrParseError;
137 
138     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
139         MacAddr::parse_str(s).map_err(|_| MacAddrParseError::InvalidValue(s.to_owned()))
140     }
141 }
142 
143 #[cfg(test)]
144 mod tests {
145     use super::*;
146 
147     #[test]
148     fn test_mac_addr() {
149         // too long
150         assert!(MacAddr::parse_str("aa:aa:aa:aa:aa:aa:aa").is_err());
151 
152         // invalid hex
153         assert!(MacAddr::parse_str("aa:aa:aa:aa:aa:ax").is_err());
154 
155         // single digit mac address component should be invalid
156         assert!(MacAddr::parse_str("aa:aa:aa:aa:aa:b").is_err());
157 
158         // components with more than two digits should also be invalid
159         assert!(MacAddr::parse_str("aa:aa:aa:aa:aa:bbb").is_err());
160 
161         let mac = MacAddr::parse_str("12:34:56:78:9a:BC").unwrap();
162 
163         println!("parsed MAC address: {mac}");
164 
165         let bytes = mac.get_bytes();
166         assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
167     }
168 
169     #[test]
170     fn test_from_bytes() {
171         let src1 = [0x01, 0x02, 0x03, 0x04, 0x05];
172         let src2 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
173         let src3 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
174 
175         assert!(MacAddr::from_bytes(&src1[..]).is_err());
176 
177         let x = MacAddr::from_bytes(&src2[..]).unwrap();
178         assert_eq!(x.to_string(), String::from("01:02:03:04:05:06"));
179 
180         assert!(MacAddr::from_bytes(&src3[..]).is_err());
181     }
182 
183     #[test]
184     fn test_mac_addr_serialization_and_deserialization() {
185         let mac: MacAddr =
186             serde_json::from_str("\"12:34:56:78:9a:bc\"").expect("MacAddr deserialization failed.");
187 
188         let bytes = mac.get_bytes();
189         assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
190 
191         let s = serde_json::to_string(&mac).expect("MacAddr serialization failed.");
192         assert_eq!(s, "\"12:34:56:78:9a:bc\"");
193     }
194 }
195