Skip to main content

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 [inividual/group] bit (the least significant bit
194    /// of the first byte of the address) is 1 and the address is not the
195    /// broadcast address.
196    ///
197    /// [individual/group]: https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast_(I/G_bit)
198    #[inline]
199    fn is_multicast(&self) -> bool {
200        (self.0[0] & 1 == 1) && !self.is_broadcast()
201    }
202}
203
204impl BroadcastAddress for Mac {
205    /// Is this the broadcast MAC address?
206    ///
207    /// Returns true if this is the broadcast MAC address, FF:FF:FF:FF:FF:FF.
208    /// Note that the broadcast address is not considered a multicast address.
209    #[inline]
210    fn is_broadcast(&self) -> bool {
211        // https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
212        *self == Mac::BROADCAST
213    }
214}
215
216impl<'a, A: IpAddress> From<&'a MulticastAddr<A>> for Mac {
217    /// Converts a multicast IP address to a MAC address.
218    ///
219    /// This method is equivalent to `MulticastAddr::<Mac>::from(addr).get()`.
220    #[inline]
221    fn from(addr: &'a MulticastAddr<A>) -> Mac {
222        MulticastAddr::<Mac>::from(addr).get()
223    }
224}
225
226impl<A: IpAddress> From<MulticastAddr<A>> for Mac {
227    /// Converts a multicast IP address to a MAC address.
228    ///
229    /// This method is equivalent to `(&addr).into()`.
230    #[inline]
231    fn from(addr: MulticastAddr<A>) -> Mac {
232        (&addr).into()
233    }
234}
235
236impl<'a, A: IpAddress> From<&'a MulticastAddr<A>> for MulticastAddr<Mac> {
237    /// Converts a multicast IP address to a multicast MAC address.
238    ///
239    /// When a multicast IP packet is sent over an Ethernet link, the frame's
240    /// destination MAC address is a multicast MAC address that is derived from
241    /// the destination IP address. This function performs that conversion.
242    ///
243    /// See [RFC 7042 Section 2.1.1] and [Section 2.3.1] for details on how IPv4
244    /// and IPv6 addresses are mapped, respectively.
245    ///
246    /// [RFC 7042 Section 2.1.1]: https://tools.ietf.org/html/rfc7042#section-2.1.1
247    /// [Section 2.3.1]: https://tools.ietf.org/html/rfc7042#section-2.3.1
248    #[inline]
249    fn from(addr: &'a MulticastAddr<A>) -> MulticastAddr<Mac> {
250        // We know the call to `unwrap` will not panic because we are generating
251        // a multicast MAC as defined in RFC 7042 section 2.1.1 and section
252        // 2.3.1 for IPv4 and IPv6 addresses, respectively.
253        MulticastAddr::new(Mac::new(match (*addr).get().into() {
254            IpAddr::V4(addr) => {
255                let ip_bytes = addr.ipv4_bytes();
256                let mut mac_bytes = [0; 6];
257                mac_bytes[0] = 0x01;
258                mac_bytes[1] = 0x00;
259                mac_bytes[2] = 0x5e;
260                mac_bytes[3] = ip_bytes[1] & 0x7f;
261                mac_bytes[4] = ip_bytes[2];
262                mac_bytes[5] = ip_bytes[3];
263                mac_bytes
264            }
265            IpAddr::V6(addr) => {
266                let ip_bytes = addr.ipv6_bytes();
267                let mut mac_bytes = [0; 6];
268                mac_bytes[0] = 0x33;
269                mac_bytes[1] = 0x33;
270                mac_bytes[2] = ip_bytes[12];
271                mac_bytes[3] = ip_bytes[13];
272                mac_bytes[4] = ip_bytes[14];
273                mac_bytes[5] = ip_bytes[15];
274                mac_bytes
275            }
276        }))
277        .unwrap()
278    }
279}
280
281impl<A: IpAddress> From<MulticastAddr<A>> for MulticastAddr<Mac> {
282    fn from(addr: MulticastAddr<A>) -> MulticastAddr<Mac> {
283        (&addr).into()
284    }
285}
286
287macro_rules! impl_from_witness {
288    ($witness:ident) => {
289        impl From<$witness<Mac>> for Mac {
290            fn from(addr: $witness<Mac>) -> Mac {
291                addr.get()
292            }
293        }
294    };
295}
296
297impl_from_witness!(UnicastAddr);
298impl_from_witness!(MulticastAddr);
299impl_from_witness!(BroadcastAddr);
300
301impl Display for Mac {
302    #[inline]
303    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
304        write!(
305            f,
306            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
307            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
308        )
309    }
310}
311
312impl Debug for Mac {
313    #[inline]
314    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
315        Display::fmt(self, f)
316    }
317}
318
319#[cfg(test)]
320mod tests {
321    use super::*;
322    use crate::ip::Ipv4Addr;
323
324    #[test]
325    fn test_mac_to_eui() {
326        assert_eq!(
327            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_eui64(),
328            [0x02, 0x1a, 0xaa, 0xff, 0xfe, 0x12, 0x34, 0x56]
329        );
330        assert_eq!(
331            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_eui64_with_magic([0xfe, 0xfe]),
332            [0x02, 0x1a, 0xaa, 0xfe, 0xfe, 0x12, 0x34, 0x56]
333        );
334    }
335
336    #[test]
337    fn test_to_ipv6_link_local() {
338        assert_eq!(
339            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_ipv6_link_local(),
340            AddrSubnet::new(
341                Ipv6Addr::new([
342                    0xfe80, // IPv6 link-local prefix
343                    0, 0, 0, // Padding zeroes
344                    0x021a, 0xaaff, 0xfe12, 0x3456, // EUI-64
345                ]),
346                64
347            )
348            .unwrap()
349        );
350        assert_eq!(
351            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56])
352                .to_ipv6_link_local_with_magic([0xfe, 0xfe]),
353            AddrSubnet::new(
354                Ipv6Addr::new([
355                    0xfe80, // IPv6 link-local prefix
356                    0, 0, 0, // Padding zeroes
357                    0x021a, 0xaafe, 0xfe12, 0x3456, // EUI-64
358                ]),
359                64
360            )
361            .unwrap()
362        );
363    }
364
365    #[test]
366    fn test_map_multicast_ip_to_ethernet_mac() {
367        let ipv4 = Ipv4Addr::new([224, 1, 1, 1]);
368        let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
369        assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
370        let ipv4 = Ipv4Addr::new([224, 129, 1, 1]);
371        let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
372        assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
373        let ipv4 = Ipv4Addr::new([225, 1, 1, 1]);
374        let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
375        assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
376
377        let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0, 3]);
378        let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
379        assert_eq!(mac, Mac::new([0x33, 0x33, 0, 0, 0, 3]));
380        let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 1, 0, 0, 0, 3]);
381        let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
382        assert_eq!(mac, Mac::new([0x33, 0x33, 0, 0, 0, 3]));
383        let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0x100, 3]);
384        let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
385        assert_eq!(mac, Mac::new([0x33, 0x33, 1, 0, 0, 3]));
386    }
387
388    #[test]
389    fn mac_display_leading_zeroes() {
390        assert_eq!(Mac::new([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).to_string(), "00:00:00:00:00:00");
391    }
392}