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