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