xref: /cloud-hypervisor/net_util/src/mac.rs (revision ea4693a09123234951ae1516f112c5cfce5032ca)
153f52954SSebastien Boeuf // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
253f52954SSebastien Boeuf // SPDX-License-Identifier: Apache-2.0
353f52954SSebastien Boeuf //
453f52954SSebastien Boeuf // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
553f52954SSebastien Boeuf // Use of this source code is governed by a BSD-style license that can be
653f52954SSebastien Boeuf // found in the THIRD-PARTY file.
753f52954SSebastien Boeuf 
8a5747a84SRob Bradford use std::str::FromStr;
961e57e1cSRuoqing He use std::{fmt, io};
1053f52954SSebastien Boeuf 
1153f52954SSebastien Boeuf use serde::de::{Deserialize, Deserializer, Error};
1253f52954SSebastien Boeuf use serde::ser::{Serialize, Serializer};
1353f52954SSebastien Boeuf 
1453f52954SSebastien Boeuf pub const MAC_ADDR_LEN: usize = 6;
1553f52954SSebastien Boeuf 
162716bc33SRob Bradford #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1753f52954SSebastien Boeuf pub struct MacAddr {
1853f52954SSebastien Boeuf     bytes: [u8; MAC_ADDR_LEN],
1953f52954SSebastien Boeuf }
2053f52954SSebastien Boeuf 
2153f52954SSebastien Boeuf impl MacAddr {
parse_str<S>(s: &S) -> Result<MacAddr, io::Error> where S: AsRef<str> + ?Sized,2206396593SSebastien Boeuf     pub fn parse_str<S>(s: &S) -> Result<MacAddr, io::Error>
2353f52954SSebastien Boeuf     where
2453f52954SSebastien Boeuf         S: AsRef<str> + ?Sized,
2553f52954SSebastien Boeuf     {
2653f52954SSebastien Boeuf         let v: Vec<&str> = s.as_ref().split(':').collect();
2753f52954SSebastien Boeuf         let mut bytes = [0u8; MAC_ADDR_LEN];
28*ea4693a0SJinank Jain         let common_err = Err(io::Error::other(format!(
29*ea4693a0SJinank Jain             "parsing of {} into a MAC address failed",
30*ea4693a0SJinank Jain             s.as_ref()
31*ea4693a0SJinank Jain         )));
3253f52954SSebastien Boeuf 
3353f52954SSebastien Boeuf         if v.len() != MAC_ADDR_LEN {
3406396593SSebastien Boeuf             return common_err;
3553f52954SSebastien Boeuf         }
3653f52954SSebastien Boeuf 
3753f52954SSebastien Boeuf         for i in 0..MAC_ADDR_LEN {
3853f52954SSebastien Boeuf             if v[i].len() != 2 {
3906396593SSebastien Boeuf                 return common_err;
4053f52954SSebastien Boeuf             }
4106396593SSebastien Boeuf             bytes[i] = u8::from_str_radix(v[i], 16).map_err(|e| {
42*ea4693a0SJinank Jain                 io::Error::other(format!(
43*ea4693a0SJinank Jain                     "parsing of {} into a MAC address failed: {}",
44*ea4693a0SJinank Jain                     s.as_ref(),
45*ea4693a0SJinank Jain                     e
46*ea4693a0SJinank Jain                 ))
4706396593SSebastien Boeuf             })?;
4853f52954SSebastien Boeuf         }
4953f52954SSebastien Boeuf 
5053f52954SSebastien Boeuf         Ok(MacAddr { bytes })
5153f52954SSebastien Boeuf     }
5253f52954SSebastien Boeuf 
5353f52954SSebastien Boeuf     // Does not check whether src.len() == MAC_ADDR_LEN.
5453f52954SSebastien Boeuf     #[inline]
from_bytes_unchecked(src: &[u8]) -> MacAddr5553f52954SSebastien Boeuf     pub fn from_bytes_unchecked(src: &[u8]) -> MacAddr {
5653f52954SSebastien Boeuf         // TODO: using something like std::mem::uninitialized could avoid the extra initialization,
5753f52954SSebastien Boeuf         // if this ever becomes a performance bottleneck.
5853f52954SSebastien Boeuf         let mut bytes = [0u8; MAC_ADDR_LEN];
595825ab2dSBo Chen         bytes[..].copy_from_slice(src);
6053f52954SSebastien Boeuf 
6153f52954SSebastien Boeuf         MacAddr { bytes }
6253f52954SSebastien Boeuf     }
6353f52954SSebastien Boeuf 
6453f52954SSebastien Boeuf     // An error can only occur if the slice length is different from MAC_ADDR_LEN.
6553f52954SSebastien Boeuf     #[inline]
from_bytes(src: &[u8]) -> Result<MacAddr, io::Error>66a4134f6bSRob Bradford     pub fn from_bytes(src: &[u8]) -> Result<MacAddr, io::Error> {
6753f52954SSebastien Boeuf         if src.len() != MAC_ADDR_LEN {
68*ea4693a0SJinank Jain             return Err(io::Error::other(format!(
69*ea4693a0SJinank Jain                 "invalid length of slice: {} vs {}",
70*ea4693a0SJinank Jain                 src.len(),
71*ea4693a0SJinank Jain                 MAC_ADDR_LEN
72*ea4693a0SJinank Jain             )));
7353f52954SSebastien Boeuf         }
7453f52954SSebastien Boeuf         Ok(MacAddr::from_bytes_unchecked(src))
7553f52954SSebastien Boeuf     }
7653f52954SSebastien Boeuf 
7753f52954SSebastien Boeuf     #[inline]
get_bytes(&self) -> &[u8]7853f52954SSebastien Boeuf     pub fn get_bytes(&self) -> &[u8] {
7953f52954SSebastien Boeuf         &self.bytes
8053f52954SSebastien Boeuf     }
8153f52954SSebastien Boeuf 
local_random() -> MacAddr82576a28aeSSamuel Ortiz     pub fn local_random() -> MacAddr {
83576a28aeSSamuel Ortiz         // Generate a fully random MAC
84c1be41bfSRob Bradford         let mut random_bytes = [0u8; MAC_ADDR_LEN];
8507b7457cSRuoqing He         if let Err(e) = getrandom::fill(&mut random_bytes) {
86c1be41bfSRob Bradford             error!(
87c1be41bfSRob Bradford                 "Error populating MAC address with random data: {}",
88c313bcbfSHui Zhu                 e.to_string()
89c313bcbfSHui Zhu             );
90c1be41bfSRob Bradford         }
91576a28aeSSamuel Ortiz 
92576a28aeSSamuel Ortiz         // Set the first byte to make the OUI a locally administered OUI
93576a28aeSSamuel Ortiz         random_bytes[0] = 0x2e;
94576a28aeSSamuel Ortiz 
95576a28aeSSamuel Ortiz         MacAddr {
96576a28aeSSamuel Ortiz             bytes: random_bytes,
97576a28aeSSamuel Ortiz         }
98576a28aeSSamuel Ortiz     }
9953f52954SSebastien Boeuf }
10053f52954SSebastien Boeuf 
101f63cb85fSRob Bradford impl fmt::Display for MacAddr {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result102f63cb85fSRob Bradford     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103f63cb85fSRob Bradford         let b = &self.bytes;
104f63cb85fSRob Bradford         write!(
105f63cb85fSRob Bradford             f,
106f63cb85fSRob Bradford             "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
107f63cb85fSRob Bradford             b[0], b[1], b[2], b[3], b[4], b[5]
108f63cb85fSRob Bradford         )
109f63cb85fSRob Bradford     }
110f63cb85fSRob Bradford }
111f63cb85fSRob Bradford 
11253f52954SSebastien Boeuf impl Serialize for MacAddr {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,11353f52954SSebastien Boeuf     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
11453f52954SSebastien Boeuf     where
11553f52954SSebastien Boeuf         S: Serializer,
11653f52954SSebastien Boeuf     {
11753f52954SSebastien Boeuf         self.to_string().serialize(serializer)
11853f52954SSebastien Boeuf     }
11953f52954SSebastien Boeuf }
12053f52954SSebastien Boeuf 
12153f52954SSebastien Boeuf impl<'de> Deserialize<'de> for MacAddr {
deserialize<D>(deserializer: D) -> Result<MacAddr, D::Error> where D: Deserializer<'de>,12253f52954SSebastien Boeuf     fn deserialize<D>(deserializer: D) -> Result<MacAddr, D::Error>
12353f52954SSebastien Boeuf     where
12453f52954SSebastien Boeuf         D: Deserializer<'de>,
12553f52954SSebastien Boeuf     {
12653f52954SSebastien Boeuf         let s = String::deserialize(deserializer)?;
12706396593SSebastien Boeuf         MacAddr::parse_str(&s)
1285e527294SRob Bradford             .map_err(|e| D::Error::custom(format!("The provided MAC address is invalid: {e}")))
12953f52954SSebastien Boeuf     }
13053f52954SSebastien Boeuf }
13153f52954SSebastien Boeuf 
132a5747a84SRob Bradford pub enum MacAddrParseError {
133a5747a84SRob Bradford     InvalidValue(String),
134a5747a84SRob Bradford }
135a5747a84SRob Bradford 
136a5747a84SRob Bradford impl FromStr for MacAddr {
137a5747a84SRob Bradford     type Err = MacAddrParseError;
138a5747a84SRob Bradford 
from_str(s: &str) -> std::result::Result<Self, Self::Err>139a5747a84SRob Bradford     fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
140a5747a84SRob Bradford         MacAddr::parse_str(s).map_err(|_| MacAddrParseError::InvalidValue(s.to_owned()))
141a5747a84SRob Bradford     }
142a5747a84SRob Bradford }
143a5747a84SRob Bradford 
14453f52954SSebastien Boeuf #[cfg(test)]
14553f52954SSebastien Boeuf mod tests {
14653f52954SSebastien Boeuf     use super::*;
14753f52954SSebastien Boeuf 
14853f52954SSebastien Boeuf     #[test]
test_mac_addr()14953f52954SSebastien Boeuf     fn test_mac_addr() {
15053f52954SSebastien Boeuf         // too long
151297236a7SRuoqing He         MacAddr::parse_str("aa:aa:aa:aa:aa:aa:aa").unwrap_err();
15253f52954SSebastien Boeuf 
15353f52954SSebastien Boeuf         // invalid hex
154297236a7SRuoqing He         MacAddr::parse_str("aa:aa:aa:aa:aa:ax").unwrap_err();
15553f52954SSebastien Boeuf 
15653f52954SSebastien Boeuf         // single digit mac address component should be invalid
157297236a7SRuoqing He         MacAddr::parse_str("aa:aa:aa:aa:aa:b").unwrap_err();
15853f52954SSebastien Boeuf 
15953f52954SSebastien Boeuf         // components with more than two digits should also be invalid
160297236a7SRuoqing He         MacAddr::parse_str("aa:aa:aa:aa:aa:bbb").unwrap_err();
16153f52954SSebastien Boeuf 
16253f52954SSebastien Boeuf         let mac = MacAddr::parse_str("12:34:56:78:9a:BC").unwrap();
16353f52954SSebastien Boeuf 
1645e527294SRob Bradford         println!("parsed MAC address: {mac}");
16553f52954SSebastien Boeuf 
16653f52954SSebastien Boeuf         let bytes = mac.get_bytes();
16753f52954SSebastien Boeuf         assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
16853f52954SSebastien Boeuf     }
16953f52954SSebastien Boeuf 
17053f52954SSebastien Boeuf     #[test]
test_from_bytes()17153f52954SSebastien Boeuf     fn test_from_bytes() {
17253f52954SSebastien Boeuf         let src1 = [0x01, 0x02, 0x03, 0x04, 0x05];
17353f52954SSebastien Boeuf         let src2 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
17453f52954SSebastien Boeuf         let src3 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
17553f52954SSebastien Boeuf 
176297236a7SRuoqing He         MacAddr::from_bytes(&src1[..]).unwrap_err();
17753f52954SSebastien Boeuf 
17853f52954SSebastien Boeuf         let x = MacAddr::from_bytes(&src2[..]).unwrap();
17953f52954SSebastien Boeuf         assert_eq!(x.to_string(), String::from("01:02:03:04:05:06"));
18053f52954SSebastien Boeuf 
181297236a7SRuoqing He         MacAddr::from_bytes(&src3[..]).unwrap_err();
18253f52954SSebastien Boeuf     }
18353f52954SSebastien Boeuf 
18453f52954SSebastien Boeuf     #[test]
test_mac_addr_serialization_and_deserialization()18553f52954SSebastien Boeuf     fn test_mac_addr_serialization_and_deserialization() {
18653f52954SSebastien Boeuf         let mac: MacAddr =
18753f52954SSebastien Boeuf             serde_json::from_str("\"12:34:56:78:9a:bc\"").expect("MacAddr deserialization failed.");
18853f52954SSebastien Boeuf 
18953f52954SSebastien Boeuf         let bytes = mac.get_bytes();
19053f52954SSebastien Boeuf         assert_eq!(bytes, [0x12u8, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
19153f52954SSebastien Boeuf 
19253f52954SSebastien Boeuf         let s = serde_json::to_string(&mac).expect("MacAddr serialization failed.");
19353f52954SSebastien Boeuf         assert_eq!(s, "\"12:34:56:78:9a:bc\"");
19453f52954SSebastien Boeuf     }
19553f52954SSebastien Boeuf }
196