1use crate::Bssid;
8use anyhow::{Error, format_err};
9use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
10use std::fmt;
11use std::str::FromStr;
12use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
13
14pub(crate) type MacAddrByteArray = [u8; fidl_ieee80211::MAC_ADDR_LEN as usize];
17
18pub const BROADCAST_ADDR: MacAddr = MacAddr([0xFF; 6]);
19pub const NULL_ADDR: MacAddr = MacAddr([0x00; fidl_ieee80211::MAC_ADDR_LEN as usize]);
20
21#[repr(transparent)]
22#[derive(
23 KnownLayout,
24 FromBytes,
25 IntoBytes,
26 Immutable,
27 Unaligned,
28 Clone,
29 Copy,
30 PartialEq,
31 Eq,
32 PartialOrd,
33 Ord,
34 Hash,
35)]
36pub struct MacAddr(pub(crate) MacAddrByteArray);
37
38impl MacAddr {
39 pub const fn len(&self) -> usize {
40 self.0.len()
41 }
42
43 pub fn is_unicast(&self) -> bool {
47 self.0[0] & 1 == 0
48 }
49
50 pub fn is_multicast(&self) -> bool {
53 self.0[0] & 0x01 != 0
54 }
55
56 pub fn is_locally_administered(&self) -> bool {
59 self.0[0] & 0x02 != 0
60 }
61
62 pub fn as_slice(&self) -> &[u8] {
63 &self.0
64 }
65}
66
67pub trait MacAddrBytes {
71 fn to_array(&self) -> MacAddrByteArray;
72 fn as_array(&self) -> &MacAddrByteArray;
73}
74
75impl MacAddrBytes for MacAddr {
76 fn to_array(&self) -> MacAddrByteArray {
77 self.0
78 }
79
80 fn as_array(&self) -> &MacAddrByteArray {
81 &self.0
82 }
83}
84
85pub(crate) trait MacFmt {
86 fn to_mac_string(&self) -> String
87 where
88 Self: MacAddrBytes,
89 {
90 let mac = self.to_array();
91 format!(
92 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
93 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
94 )
95 }
96}
97
98pub trait OuiFmt {
99 fn to_oui_uppercase(&self, sep: &str) -> String
100 where
101 Self: MacAddrBytes,
102 {
103 let mac = self.to_array();
104 format!("{:02X}{}{:02X}{}{:02X}", mac[0], sep, mac[1], sep, mac[2])
105 }
106}
107
108impl MacFmt for MacAddr {}
109impl OuiFmt for MacAddr {}
110
111impl From<Bssid> for MacAddr {
112 fn from(bssid: Bssid) -> MacAddr {
113 MacAddr(bssid.0)
114 }
115}
116
117impl From<MacAddrByteArray> for MacAddr {
118 fn from(bytes: MacAddrByteArray) -> MacAddr {
119 MacAddr(bytes)
120 }
121}
122
123impl fmt::Display for MacAddr {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 write!(f, "{}", self.to_mac_string())
126 }
127}
128
129fn detect_delimiter(s: &str) -> Result<char, Error> {
130 let contains_semicolon = s.contains(':');
131 let contains_hyphen = s.contains('-');
132 match (contains_semicolon, contains_hyphen) {
133 (true, true) => return Err(format_err!("Either exclusively ':' or '-' must be used.")),
134 (false, false) => {
135 return Err(format_err!("No valid delimiter found. Only ':' and '-' are supported."));
136 }
137 (true, false) => Ok(':'),
138 (false, true) => Ok('-'),
139 }
140}
141
142impl FromStr for MacAddr {
143 type Err = Error;
144
145 fn from_str(s: &str) -> Result<Self, Self::Err> {
146 let mut bytes: MacAddrByteArray = [0; 6];
147 let mut index = 0;
148
149 let delimiter = detect_delimiter(s)?;
150 for octet in s.split(delimiter) {
151 if index == 6 {
152 return Err(format_err!("Too many octets"));
153 }
154 bytes[index] = u8::from_str_radix(octet, 16)?;
155 index += 1;
156 }
157
158 if index != 6 {
159 return Err(format_err!("Too few octets. Mixed delimiters are not supported."));
160 }
161 Ok(MacAddr(bytes))
162 }
163}
164
165impl fmt::Debug for MacAddr {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 write!(f, "MacAddr({})", self)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn format_mac_addr_as_mac_string() {
177 let mac_addr: MacAddr = MacAddr::from([0x00, 0x12, 0x48, 0x9a, 0xbc, 0xdf]);
178 assert_eq!("00:12:48:9a:bc:df", &format!("{}", mac_addr));
179 }
180
181 #[test]
182 fn format_mac_addr_as_mac_debug_string() {
183 let mac_addr: MacAddr = MacAddr::from([0x00, 0x12, 0x48, 0x9a, 0xbc, 0xdf]);
184 assert_eq!("MacAddr(00:12:48:9a:bc:df)", &format!("{:?}", mac_addr));
185 }
186
187 #[test]
188 fn format_oui_uppercase() {
189 let mac: MacAddr = MacAddr::from([0x0a, 0xb1, 0xcd, 0x9a, 0xbc, 0xdf]);
190 assert_eq!(mac.to_oui_uppercase(""), "0AB1CD");
191 assert_eq!(mac.to_oui_uppercase(":"), "0A:B1:CD");
192 assert_eq!(mac.to_oui_uppercase("-"), "0A-B1-CD");
193 }
194
195 #[test]
196 fn unicast_addresses() {
197 assert!(MacAddr::from([0; 6]).is_unicast());
198 assert!(MacAddr::from([0xfe; 6]).is_unicast());
199 }
200
201 #[test]
202 fn non_unicast_addresses() {
203 assert!(!MacAddr::from([0xff; 6]).is_unicast()); assert!(!MacAddr::from([0x33, 0x33, 0, 0, 0, 0]).is_unicast()); assert!(!MacAddr::from([0x01, 0x00, 0x53, 0, 0, 0]).is_unicast()); }
207
208 #[test]
209 fn is_multicast_valid_addr() {
210 assert!(MacAddr::from([33, 33, 33, 33, 33, 33]).is_multicast());
211 }
212
213 #[test]
214 fn is_multicast_not_valid_addr() {
215 assert!(!MacAddr::from([34, 33, 33, 33, 33, 33]).is_multicast());
216 }
217
218 #[test]
219 fn locally_administered_addresses() {
220 assert!(MacAddr::from([0x02, 0, 0, 0, 0, 0]).is_locally_administered());
221 assert!(MacAddr::from([0xfe; 6]).is_locally_administered());
222 assert!(MacAddr::from([0xff; 6]).is_locally_administered());
223 }
224
225 #[test]
226 fn universally_administered_addresses() {
227 assert!(!MacAddr::from([0; 6]).is_locally_administered());
228 assert!(!MacAddr::from([0x01, 0, 0, 0, 0, 0]).is_locally_administered());
229 assert!(!MacAddr::from([0xfd; 6]).is_locally_administered());
230 }
231
232 #[test]
233 fn successfully_parse_mac_str() {
234 assert_eq!(
235 "01:23:cd:11:11:11".parse::<MacAddr>().unwrap(),
236 MacAddr::from([0x01, 0x23, 0xcd, 0x11, 0x11, 0x11])
237 );
238 assert_eq!(
239 "01-23-cd-11-11-11".parse::<MacAddr>().unwrap(),
240 MacAddr::from([0x01, 0x23, 0xcd, 0x11, 0x11, 0x11])
241 );
242 assert_eq!(
243 "1-23-cd-11-11-11".parse::<MacAddr>().unwrap(),
244 MacAddr::from([0x01, 0x23, 0xcd, 0x11, 0x11, 0x11])
245 );
246 }
247
248 #[test]
249 fn mac_addr_from_str() {
250 assert_eq!(
251 MacAddr::from_str("01:02:03:ab:cd:ef").unwrap(),
252 MacAddr([0x01, 0x02, 0x03, 0xab, 0xcd, 0xef])
253 );
254 assert_eq!(
255 MacAddr::from_str("01-02-03-ab-cd-ef").unwrap(),
256 MacAddr([0x01, 0x02, 0x03, 0xab, 0xcd, 0xef])
257 );
258 }
259
260 #[test]
261 fn fail_to_parse_mac_str() {
262 assert!("11:11:23::11:11:11".parse::<MacAddr>().is_err());
263 assert!("11:11:23:11:11:11:11".parse::<MacAddr>().is_err());
264 assert!(":11:23:11:11:11:11".parse::<MacAddr>().is_err());
265 assert!("11:23:11:11:11:11:11".parse::<MacAddr>().is_err());
266 assert!("11:23:11:11:11:11:".parse::<MacAddr>().is_err());
267 assert!("111:23:11:11:11:11".parse::<MacAddr>().is_err());
268 assert!("11:23-11:11-11:11".parse::<MacAddr>().is_err());
269 assert!("11-23:11-11:11-11".parse::<MacAddr>().is_err());
270 assert!("-11-23-11-11-11-11".parse::<MacAddr>().is_err());
271 assert!("11-23-11-11-11-11-".parse::<MacAddr>().is_err());
272 }
273}
274
275