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