Skip to main content

ieee80211/
mac_addr.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// LINT.IfChange
6
7use 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
14// Strictly speaking, the MAC address is not defined in 802.11, but it's defined
15// here for convenience.
16pub(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    /// A MAC address is a unicast address if the least significant bit of the first octet is 0.
44    /// See "individual/group bit" in
45    /// https://standards.ieee.org/content/dam/ieee-standards/standards/web/documents/tutorials/macgrp.pdf
46    pub fn is_unicast(&self) -> bool {
47        self.0[0] & 1 == 0
48    }
49
50    /// IEEE Std 802.3-2015, 3.2.3: The least significant bit of the first octet of a MAC address
51    /// denotes multicast.
52    pub fn is_multicast(&self) -> bool {
53        self.0[0] & 0x01 != 0
54    }
55
56    /// IEEE Std 802.3-2015, 3.2.3: The second least significant bit of the first octet of a MAC
57    /// address denotes a locally administered address.
58    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
67/// This trait aims to add some friction to convert a type into MacAddrBytes. The purpose being that
68/// function using the types implementing this trait, e.g. MacAddr, should prefer not accessing
69/// the MacAddrBytes directly when possible.
70pub 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()); // broadcast
204        assert!(!MacAddr::from([0x33, 0x33, 0, 0, 0, 0]).is_unicast()); // IPv6 multicast
205        assert!(!MacAddr::from([0x01, 0x00, 0x53, 0, 0, 0]).is_unicast()); // IPv4 multicast
206    }
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// LINT.ThenChange(//src/connectivity/wlan/drivers/lib/macaddr/include/wlan/drivers/macaddr.h)