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