net_types/ethernet.rs
1// Copyright 2019 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//! Ethernet protocol types.
6
7use core::fmt::{self, Debug, Display, Formatter};
8
9use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
10
11use crate::ip::{AddrSubnet, IpAddr, IpAddress, Ipv6, Ipv6Addr};
12use crate::{
13 BroadcastAddr, BroadcastAddress, LinkLocalUnicastAddr, MulticastAddr, MulticastAddress,
14 UnicastAddr, UnicastAddress, Witness,
15};
16
17/// A media access control (MAC) address.
18///
19/// MAC addresses are used to identify devices in the Ethernet protocol.
20///
21/// MAC addresses can be derived from multicast IP addresses; see the `From`
22/// implementation for more details.
23///
24/// # Layout
25///
26/// `Mac` has the same layout as `[u8; 6]`, which is the layout that most
27/// protocols use to represent a MAC address in their packet formats. This can
28/// be useful when parsing a MAC address from a packet. For example:
29///
30/// ```rust
31/// # use net_types::ethernet::Mac;
32/// /// The header of an Ethernet frame.
33/// ///
34/// /// `EthernetHeader` has the same layout as the header of an Ethernet frame.
35/// #[repr(C)]
36/// struct EthernetHeader {
37/// dst: Mac,
38/// src: Mac,
39/// ethertype: [u8; 2],
40/// }
41/// ```
42#[derive(
43 Copy, Clone, Eq, PartialEq, Hash, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
44)]
45#[repr(transparent)]
46pub struct Mac([u8; Mac::BYTES]);
47
48impl Mac {
49 /// The number of bytes in a Mac address.
50 pub const BYTES: usize = 6;
51
52 /// The broadcast MAC address.
53 ///
54 /// The broadcast MAC address, FF:FF:FF:FF:FF:FF, indicates that a frame
55 /// should be received by all receivers regardless of their local MAC
56 /// address.
57 // TODO(https://github.com/rust-lang/rust/issues/73255): Make this
58 // `BroadcastAddr<Mac>` once the `const_precise_live_drops` feature has
59 // stabilized, and thus it's possible to write a `const fn` which converts
60 // from `BroadcastAddr<A>` to `A`.
61 pub const BROADCAST: Mac = Mac([0xFF; Self::BYTES]);
62
63 /// The default [RFC 4291] EUI-64 magic value used by the [`to_eui64`]
64 /// method.
65 ///
66 /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
67 /// [`to_eui64`]: crate::ethernet::Mac::to_eui64
68 pub const DEFAULT_EUI_MAGIC: [u8; 2] = [0xff, 0xfe];
69
70 /// The all-zeroes MAC address.
71 pub const UNSPECIFIED: Mac = Mac([0x00; Self::BYTES]);
72
73 /// Constructs a new MAC address.
74 #[inline]
75 pub const fn new(bytes: [u8; Self::BYTES]) -> Mac {
76 Mac(bytes)
77 }
78
79 /// Gets the bytes of the MAC address.
80 #[inline]
81 pub const fn bytes(self) -> [u8; Self::BYTES] {
82 self.0
83 }
84
85 /// Returns the [RFC 4291] EUI-64 interface identifier for this MAC address
86 /// with the default EUI magic value.
87 ///
88 /// `mac.to_eui64()` is equivalent to
89 /// `mac.to_eui64_with_magic(Mac::DEFAULT_EUI_MAGIC)`.
90 ///
91 /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
92 #[inline]
93 pub fn to_eui64(self) -> [u8; 8] {
94 self.to_eui64_with_magic(Mac::DEFAULT_EUI_MAGIC)
95 }
96
97 /// Returns the [RFC 4291] EUI-64 interface identifier for this MAC address
98 /// with a custom EUI magic value.
99 ///
100 /// `eui_magic` is the two bytes that are inserted between the bytes of the
101 /// MAC address to form the identifier. Also see the [`to_eui64`] method,
102 /// which uses the default magic value of 0xFFFE.
103 ///
104 /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
105 /// [`to_eui64`]: crate::ethernet::Mac::to_eui64
106 #[inline]
107 pub fn to_eui64_with_magic(self, eui_magic: [u8; 2]) -> [u8; 8] {
108 let mut eui = [0; 8];
109 eui[0..3].copy_from_slice(&self.0[0..3]);
110 eui[3..5].copy_from_slice(&eui_magic);
111 eui[5..8].copy_from_slice(&self.0[3..6]);
112 eui[0] ^= 0b0000_0010;
113 eui
114 }
115
116 /// Returns the link-local unicast IPv6 address and subnet for this MAC
117 /// address, as per [RFC 4862], with the default EUI magic value.
118 ///
119 /// `mac.to_ipv6_link_local()` is equivalent to
120 /// `mac.to_ipv6_link_local_with_magic(Mac::DEFAULT_EUI_MAGIC)`.
121 ///
122 /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
123 #[inline]
124 pub fn to_ipv6_link_local(self) -> AddrSubnet<Ipv6Addr, LinkLocalUnicastAddr<Ipv6Addr>> {
125 self.to_ipv6_link_local_with_magic(Mac::DEFAULT_EUI_MAGIC)
126 }
127
128 /// Returns the link-local unicast IPv6 address and subnet for this MAC
129 /// address, as per [RFC 4862].
130 ///
131 /// `eui_magic` is the two bytes that are inserted between the bytes of the
132 /// MAC address to form the identifier. Also see the [`to_ipv6_link_local`]
133 /// method, which uses the default magic value of 0xFFFE.
134 ///
135 /// The subnet prefix length is 128 -
136 /// [`Ipv6::UNICAST_INTERFACE_IDENTIFIER_BITS`].
137 ///
138 /// [RFC 4862]: https://tools.ietf.org/html/rfc4862
139 /// [`to_ipv6_link_local`]: crate::ethernet::Mac::to_ipv6_link_local
140 /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
141 #[inline]
142 pub fn to_ipv6_link_local_with_magic(
143 self,
144 eui_magic: [u8; 2],
145 ) -> AddrSubnet<Ipv6Addr, LinkLocalUnicastAddr<Ipv6Addr>> {
146 let mut ipv6_addr = [0; 16];
147 ipv6_addr[0..2].copy_from_slice(&[0xfe, 0x80]);
148 ipv6_addr[8..16].copy_from_slice(&self.to_eui64_with_magic(eui_magic));
149
150 // We know the call to `unwrap` will not panic because we know we are
151 // passing `AddrSubnet::new` a valid link local address as per RFC 4291.
152 // Specifically, the first 10 bits of the generated address is
153 // `0b1111111010`. `AddrSubnet::new` also validates the prefix length,
154 // and we know that 64 is a valid IPv6 subnet prefix length.
155 //
156 // TODO(ghanan): Investigate whether this unwrap is optimized out in
157 // practice as this code will be on the hot path.
158 AddrSubnet::new(
159 Ipv6Addr::from(ipv6_addr),
160 Ipv6Addr::BYTES * 8 - Ipv6::UNICAST_INTERFACE_IDENTIFIER_BITS,
161 )
162 .unwrap()
163 }
164}
165
166impl AsRef<[u8]> for Mac {
167 fn as_ref(&self) -> &[u8] {
168 &self.0
169 }
170}
171
172impl AsMut<[u8]> for Mac {
173 fn as_mut(&mut self) -> &mut [u8] {
174 &mut self.0
175 }
176}
177
178impl UnicastAddress for Mac {
179 /// Is this a unicast MAC address?
180 ///
181 /// Returns true if the least significant bit of the first byte of the
182 /// address is 0.
183 #[inline]
184 fn is_unicast(&self) -> bool {
185 // https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
186 self.0[0] & 1 == 0
187 }
188}
189
190impl MulticastAddress for Mac {
191 /// Is this a multicast MAC address?
192 ///
193 /// Returns true if the least significant bit of the first byte of the
194 /// address is 1.
195 #[inline]
196 fn is_multicast(&self) -> bool {
197 // https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
198 self.0[0] & 1 == 1
199 }
200}
201
202impl BroadcastAddress for Mac {
203 /// Is this the broadcast MAC address?
204 ///
205 /// Returns true if this is the broadcast MAC address, FF:FF:FF:FF:FF:FF.
206 /// Note that the broadcast address is also considered a multicast address,
207 /// so `addr.is_broadcast()` implies `addr.is_multicast()`.
208 #[inline]
209 fn is_broadcast(&self) -> bool {
210 // https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
211 *self == Mac::BROADCAST
212 }
213}
214
215impl<'a, A: IpAddress> From<&'a MulticastAddr<A>> for Mac {
216 /// Converts a multicast IP address to a MAC address.
217 ///
218 /// This method is equivalent to `MulticastAddr::<Mac>::from(addr).get()`.
219 #[inline]
220 fn from(addr: &'a MulticastAddr<A>) -> Mac {
221 MulticastAddr::<Mac>::from(addr).get()
222 }
223}
224
225impl<A: IpAddress> From<MulticastAddr<A>> for Mac {
226 /// Converts a multicast IP address to a MAC address.
227 ///
228 /// This method is equivalent to `(&addr).into()`.
229 #[inline]
230 fn from(addr: MulticastAddr<A>) -> Mac {
231 (&addr).into()
232 }
233}
234
235impl<'a, A: IpAddress> From<&'a MulticastAddr<A>> for MulticastAddr<Mac> {
236 /// Converts a multicast IP address to a multicast MAC address.
237 ///
238 /// When a multicast IP packet is sent over an Ethernet link, the frame's
239 /// destination MAC address is a multicast MAC address that is derived from
240 /// the destination IP address. This function performs that conversion.
241 ///
242 /// See [RFC 7042 Section 2.1.1] and [Section 2.3.1] for details on how IPv4
243 /// and IPv6 addresses are mapped, respectively.
244 ///
245 /// [RFC 7042 Section 2.1.1]: https://tools.ietf.org/html/rfc7042#section-2.1.1
246 /// [Section 2.3.1]: https://tools.ietf.org/html/rfc7042#section-2.3.1
247 #[inline]
248 fn from(addr: &'a MulticastAddr<A>) -> MulticastAddr<Mac> {
249 // We know the call to `unwrap` will not panic because we are generating
250 // a multicast MAC as defined in RFC 7042 section 2.1.1 and section
251 // 2.3.1 for IPv4 and IPv6 addresses, respectively.
252 MulticastAddr::new(Mac::new(match (*addr).get().into() {
253 IpAddr::V4(addr) => {
254 let ip_bytes = addr.ipv4_bytes();
255 let mut mac_bytes = [0; 6];
256 mac_bytes[0] = 0x01;
257 mac_bytes[1] = 0x00;
258 mac_bytes[2] = 0x5e;
259 mac_bytes[3] = ip_bytes[1] & 0x7f;
260 mac_bytes[4] = ip_bytes[2];
261 mac_bytes[5] = ip_bytes[3];
262 mac_bytes
263 }
264 IpAddr::V6(addr) => {
265 let ip_bytes = addr.ipv6_bytes();
266 let mut mac_bytes = [0; 6];
267 mac_bytes[0] = 0x33;
268 mac_bytes[1] = 0x33;
269 mac_bytes[2] = ip_bytes[12];
270 mac_bytes[3] = ip_bytes[13];
271 mac_bytes[4] = ip_bytes[14];
272 mac_bytes[5] = ip_bytes[15];
273 mac_bytes
274 }
275 }))
276 .unwrap()
277 }
278}
279
280impl<A: IpAddress> From<MulticastAddr<A>> for MulticastAddr<Mac> {
281 fn from(addr: MulticastAddr<A>) -> MulticastAddr<Mac> {
282 (&addr).into()
283 }
284}
285
286macro_rules! impl_from_witness {
287 ($witness:ident) => {
288 impl From<$witness<Mac>> for Mac {
289 fn from(addr: $witness<Mac>) -> Mac {
290 addr.get()
291 }
292 }
293 };
294}
295
296impl_from_witness!(UnicastAddr);
297impl_from_witness!(MulticastAddr);
298impl_from_witness!(BroadcastAddr);
299
300impl Display for Mac {
301 #[inline]
302 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
303 write!(
304 f,
305 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
306 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
307 )
308 }
309}
310
311impl Debug for Mac {
312 #[inline]
313 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
314 Display::fmt(self, f)
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 use crate::ip::Ipv4Addr;
322
323 #[test]
324 fn test_mac_to_eui() {
325 assert_eq!(
326 Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_eui64(),
327 [0x02, 0x1a, 0xaa, 0xff, 0xfe, 0x12, 0x34, 0x56]
328 );
329 assert_eq!(
330 Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_eui64_with_magic([0xfe, 0xfe]),
331 [0x02, 0x1a, 0xaa, 0xfe, 0xfe, 0x12, 0x34, 0x56]
332 );
333 }
334
335 #[test]
336 fn test_to_ipv6_link_local() {
337 assert_eq!(
338 Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_ipv6_link_local(),
339 AddrSubnet::new(
340 Ipv6Addr::new([
341 0xfe80, // IPv6 link-local prefix
342 0, 0, 0, // Padding zeroes
343 0x021a, 0xaaff, 0xfe12, 0x3456, // EUI-64
344 ]),
345 64
346 )
347 .unwrap()
348 );
349 assert_eq!(
350 Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56])
351 .to_ipv6_link_local_with_magic([0xfe, 0xfe]),
352 AddrSubnet::new(
353 Ipv6Addr::new([
354 0xfe80, // IPv6 link-local prefix
355 0, 0, 0, // Padding zeroes
356 0x021a, 0xaafe, 0xfe12, 0x3456, // EUI-64
357 ]),
358 64
359 )
360 .unwrap()
361 );
362 }
363
364 #[test]
365 fn test_map_multicast_ip_to_ethernet_mac() {
366 let ipv4 = Ipv4Addr::new([224, 1, 1, 1]);
367 let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
368 assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
369 let ipv4 = Ipv4Addr::new([224, 129, 1, 1]);
370 let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
371 assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
372 let ipv4 = Ipv4Addr::new([225, 1, 1, 1]);
373 let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
374 assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
375
376 let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0, 3]);
377 let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
378 assert_eq!(mac, Mac::new([0x33, 0x33, 0, 0, 0, 3]));
379 let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 1, 0, 0, 0, 3]);
380 let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
381 assert_eq!(mac, Mac::new([0x33, 0x33, 0, 0, 0, 3]));
382 let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0x100, 3]);
383 let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
384 assert_eq!(mac, Mac::new([0x33, 0x33, 1, 0, 0, 3]));
385 }
386
387 #[test]
388 fn mac_display_leading_zeroes() {
389 assert_eq!(Mac::new([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).to_string(), "00:00:00:00:00:00");
390 }
391}