net_types/
ethernet.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Ethernet protocol types.

use core::fmt::{self, Debug, Display, Formatter};

use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};

use crate::ip::{AddrSubnet, IpAddr, IpAddress, Ipv6, Ipv6Addr};
use crate::{
    BroadcastAddr, BroadcastAddress, LinkLocalUnicastAddr, MulticastAddr, MulticastAddress,
    UnicastAddr, UnicastAddress, Witness,
};

/// A media access control (MAC) address.
///
/// MAC addresses are used to identify devices in the Ethernet protocol.
///
/// MAC addresses can be derived from multicast IP addresses; see the `From`
/// implementation for more details.
///
/// # Layout
///
/// `Mac` has the same layout as `[u8; 6]`, which is the layout that most
/// protocols use to represent a MAC address in their packet formats. This can
/// be useful when parsing a MAC address from a packet. For example:
///
/// ```rust
/// # use net_types::ethernet::Mac;
/// /// The header of an Ethernet frame.
/// ///
/// /// `EthernetHeader` has the same layout as the header of an Ethernet frame.
/// #[repr(C)]
/// struct EthernetHeader {
///     dst: Mac,
///     src: Mac,
///     ethertype: [u8; 2],
/// }
/// ```
#[derive(
    Copy, Clone, Eq, PartialEq, Hash, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned,
)]
#[repr(transparent)]
pub struct Mac([u8; Mac::BYTES]);

impl Mac {
    /// The number of bytes in a Mac address.
    pub const BYTES: usize = 6;

    /// The broadcast MAC address.
    ///
    /// The broadcast MAC address, FF:FF:FF:FF:FF:FF, indicates that a frame
    /// should be received by all receivers regardless of their local MAC
    /// address.
    // TODO(https://github.com/rust-lang/rust/issues/73255): Make this
    // `BroadcastAddr<Mac>` once the `const_precise_live_drops` feature has
    // stabilized, and thus it's possible to write a `const fn` which converts
    // from `BroadcastAddr<A>` to `A`.
    pub const BROADCAST: Mac = Mac([0xFF; Self::BYTES]);

    /// The default [RFC 4291] EUI-64 magic value used by the [`to_eui64`]
    /// method.
    ///
    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
    /// [`to_eui64`]: crate::ethernet::Mac::to_eui64
    pub const DEFAULT_EUI_MAGIC: [u8; 2] = [0xff, 0xfe];

    /// The all-zeroes MAC address.
    pub const UNSPECIFIED: Mac = Mac([0x00; Self::BYTES]);

    /// Constructs a new MAC address.
    #[inline]
    pub const fn new(bytes: [u8; Self::BYTES]) -> Mac {
        Mac(bytes)
    }

    /// Gets the bytes of the MAC address.
    #[inline]
    pub const fn bytes(self) -> [u8; Self::BYTES] {
        self.0
    }

    /// Returns the [RFC 4291] EUI-64 interface identifier for this MAC address
    /// with the default EUI magic value.
    ///
    /// `mac.to_eui64()` is equivalent to
    /// `mac.to_eui64_with_magic(Mac::DEFAULT_EUI_MAGIC)`.
    ///
    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
    #[inline]
    pub fn to_eui64(self) -> [u8; 8] {
        self.to_eui64_with_magic(Mac::DEFAULT_EUI_MAGIC)
    }

    /// Returns the [RFC 4291] EUI-64 interface identifier for this MAC address
    /// with a custom EUI magic value.
    ///
    /// `eui_magic` is the two bytes that are inserted between the bytes of the
    /// MAC address to form the identifier. Also see the [`to_eui64`] method,
    /// which uses the default magic value of 0xFFFE.
    ///
    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
    /// [`to_eui64`]: crate::ethernet::Mac::to_eui64
    #[inline]
    pub fn to_eui64_with_magic(self, eui_magic: [u8; 2]) -> [u8; 8] {
        let mut eui = [0; 8];
        eui[0..3].copy_from_slice(&self.0[0..3]);
        eui[3..5].copy_from_slice(&eui_magic);
        eui[5..8].copy_from_slice(&self.0[3..6]);
        eui[0] ^= 0b0000_0010;
        eui
    }

    /// Returns the link-local unicast IPv6 address and subnet for this MAC
    /// address, as per [RFC 4862], with the default EUI magic value.
    ///
    /// `mac.to_ipv6_link_local()` is equivalent to
    /// `mac.to_ipv6_link_local_with_magic(Mac::DEFAULT_EUI_MAGIC)`.
    ///
    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
    #[inline]
    pub fn to_ipv6_link_local(self) -> AddrSubnet<Ipv6Addr, LinkLocalUnicastAddr<Ipv6Addr>> {
        self.to_ipv6_link_local_with_magic(Mac::DEFAULT_EUI_MAGIC)
    }

    /// Returns the link-local unicast IPv6 address and subnet for this MAC
    /// address, as per [RFC 4862].
    ///
    /// `eui_magic` is the two bytes that are inserted between the bytes of the
    /// MAC address to form the identifier. Also see the [`to_ipv6_link_local`]
    /// method, which uses the default magic value of 0xFFFE.
    ///
    /// The subnet prefix length is 128 -
    /// [`Ipv6::UNICAST_INTERFACE_IDENTIFIER_BITS`].
    ///
    /// [RFC 4862]: https://tools.ietf.org/html/rfc4862
    /// [`to_ipv6_link_local`]: crate::ethernet::Mac::to_ipv6_link_local
    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
    #[inline]
    pub fn to_ipv6_link_local_with_magic(
        self,
        eui_magic: [u8; 2],
    ) -> AddrSubnet<Ipv6Addr, LinkLocalUnicastAddr<Ipv6Addr>> {
        let mut ipv6_addr = [0; 16];
        ipv6_addr[0..2].copy_from_slice(&[0xfe, 0x80]);
        ipv6_addr[8..16].copy_from_slice(&self.to_eui64_with_magic(eui_magic));

        // We know the call to `unwrap` will not panic because we know we are
        // passing `AddrSubnet::new` a valid link local address as per RFC 4291.
        // Specifically, the first 10 bits of the generated address is
        // `0b1111111010`. `AddrSubnet::new` also validates the prefix length,
        // and we know that 64 is a valid IPv6 subnet prefix length.
        //
        // TODO(ghanan): Investigate whether this unwrap is optimized out in
        //               practice as this code will be on the hot path.
        AddrSubnet::new(
            Ipv6Addr::from(ipv6_addr),
            Ipv6Addr::BYTES * 8 - Ipv6::UNICAST_INTERFACE_IDENTIFIER_BITS,
        )
        .unwrap()
    }
}

impl AsRef<[u8]> for Mac {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl AsMut<[u8]> for Mac {
    fn as_mut(&mut self) -> &mut [u8] {
        &mut self.0
    }
}

impl UnicastAddress for Mac {
    /// Is this a unicast MAC address?
    ///
    /// Returns true if the least significant bit of the first byte of the
    /// address is 0.
    #[inline]
    fn is_unicast(&self) -> bool {
        // https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
        self.0[0] & 1 == 0
    }
}

impl MulticastAddress for Mac {
    /// Is this a multicast MAC address?
    ///
    /// Returns true if the least significant bit of the first byte of the
    /// address is 1.
    #[inline]
    fn is_multicast(&self) -> bool {
        // https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
        self.0[0] & 1 == 1
    }
}

impl BroadcastAddress for Mac {
    /// Is this the broadcast MAC address?
    ///
    /// Returns true if this is the broadcast MAC address, FF:FF:FF:FF:FF:FF.
    /// Note that the broadcast address is also considered a multicast address,
    /// so `addr.is_broadcast()` implies `addr.is_multicast()`.
    #[inline]
    fn is_broadcast(&self) -> bool {
        // https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
        *self == Mac::BROADCAST
    }
}

impl<'a, A: IpAddress> From<&'a MulticastAddr<A>> for Mac {
    /// Converts a multicast IP address to a MAC address.
    ///
    /// This method is equivalent to `MulticastAddr::<Mac>::from(addr).get()`.
    #[inline]
    fn from(addr: &'a MulticastAddr<A>) -> Mac {
        MulticastAddr::<Mac>::from(addr).get()
    }
}

impl<A: IpAddress> From<MulticastAddr<A>> for Mac {
    /// Converts a multicast IP address to a MAC address.
    ///
    /// This method is equivalent to `(&addr).into()`.
    #[inline]
    fn from(addr: MulticastAddr<A>) -> Mac {
        (&addr).into()
    }
}

impl<'a, A: IpAddress> From<&'a MulticastAddr<A>> for MulticastAddr<Mac> {
    /// Converts a multicast IP address to a multicast MAC address.
    ///
    /// When a multicast IP packet is sent over an Ethernet link, the frame's
    /// destination MAC address is a multicast MAC address that is derived from
    /// the destination IP address. This function performs that conversion.
    ///
    /// See [RFC 7042 Section 2.1.1] and [Section 2.3.1] for details on how IPv4
    /// and IPv6 addresses are mapped, respectively.
    ///
    /// [RFC 7042 Section 2.1.1]: https://tools.ietf.org/html/rfc7042#section-2.1.1
    /// [Section 2.3.1]: https://tools.ietf.org/html/rfc7042#section-2.3.1
    #[inline]
    fn from(addr: &'a MulticastAddr<A>) -> MulticastAddr<Mac> {
        // We know the call to `unwrap` will not panic because we are generating
        // a multicast MAC as defined in RFC 7042 section 2.1.1 and section
        // 2.3.1 for IPv4 and IPv6 addresses, respectively.
        MulticastAddr::new(Mac::new(match (*addr).get().into() {
            IpAddr::V4(addr) => {
                let ip_bytes = addr.ipv4_bytes();
                let mut mac_bytes = [0; 6];
                mac_bytes[0] = 0x01;
                mac_bytes[1] = 0x00;
                mac_bytes[2] = 0x5e;
                mac_bytes[3] = ip_bytes[1] & 0x7f;
                mac_bytes[4] = ip_bytes[2];
                mac_bytes[5] = ip_bytes[3];
                mac_bytes
            }
            IpAddr::V6(addr) => {
                let ip_bytes = addr.ipv6_bytes();
                let mut mac_bytes = [0; 6];
                mac_bytes[0] = 0x33;
                mac_bytes[1] = 0x33;
                mac_bytes[2] = ip_bytes[12];
                mac_bytes[3] = ip_bytes[13];
                mac_bytes[4] = ip_bytes[14];
                mac_bytes[5] = ip_bytes[15];
                mac_bytes
            }
        }))
        .unwrap()
    }
}

impl<A: IpAddress> From<MulticastAddr<A>> for MulticastAddr<Mac> {
    fn from(addr: MulticastAddr<A>) -> MulticastAddr<Mac> {
        (&addr).into()
    }
}

macro_rules! impl_from_witness {
    ($witness:ident) => {
        impl From<$witness<Mac>> for Mac {
            fn from(addr: $witness<Mac>) -> Mac {
                addr.get()
            }
        }
    };
}

impl_from_witness!(UnicastAddr);
impl_from_witness!(MulticastAddr);
impl_from_witness!(BroadcastAddr);

impl Display for Mac {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
        )
    }
}

impl Debug for Mac {
    #[inline]
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        Display::fmt(self, f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ip::Ipv4Addr;

    #[test]
    fn test_mac_to_eui() {
        assert_eq!(
            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_eui64(),
            [0x02, 0x1a, 0xaa, 0xff, 0xfe, 0x12, 0x34, 0x56]
        );
        assert_eq!(
            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_eui64_with_magic([0xfe, 0xfe]),
            [0x02, 0x1a, 0xaa, 0xfe, 0xfe, 0x12, 0x34, 0x56]
        );
    }

    #[test]
    fn test_to_ipv6_link_local() {
        assert_eq!(
            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56]).to_ipv6_link_local(),
            AddrSubnet::new(
                Ipv6Addr::new([
                    0xfe80, // IPv6 link-local prefix
                    0, 0, 0, // Padding zeroes
                    0x021a, 0xaaff, 0xfe12, 0x3456, // EUI-64
                ]),
                64
            )
            .unwrap()
        );
        assert_eq!(
            Mac::new([0x00, 0x1a, 0xaa, 0x12, 0x34, 0x56])
                .to_ipv6_link_local_with_magic([0xfe, 0xfe]),
            AddrSubnet::new(
                Ipv6Addr::new([
                    0xfe80, // IPv6 link-local prefix
                    0, 0, 0, // Padding zeroes
                    0x021a, 0xaafe, 0xfe12, 0x3456, // EUI-64
                ]),
                64
            )
            .unwrap()
        );
    }

    #[test]
    fn test_map_multicast_ip_to_ethernet_mac() {
        let ipv4 = Ipv4Addr::new([224, 1, 1, 1]);
        let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
        assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
        let ipv4 = Ipv4Addr::new([224, 129, 1, 1]);
        let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
        assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));
        let ipv4 = Ipv4Addr::new([225, 1, 1, 1]);
        let mac = Mac::from(&MulticastAddr::new(ipv4).unwrap());
        assert_eq!(mac, Mac::new([0x01, 0x00, 0x5e, 0x1, 0x1, 0x1]));

        let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0, 3]);
        let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
        assert_eq!(mac, Mac::new([0x33, 0x33, 0, 0, 0, 3]));
        let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 1, 0, 0, 0, 3]);
        let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
        assert_eq!(mac, Mac::new([0x33, 0x33, 0, 0, 0, 3]));
        let ipv6 = Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0x100, 3]);
        let mac = Mac::from(&MulticastAddr::new(ipv6).unwrap());
        assert_eq!(mac, Mac::new([0x33, 0x33, 1, 0, 0, 3]));
    }

    #[test]
    fn mac_display_leading_zeroes() {
        assert_eq!(Mac::new([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).to_string(), "00:00:00:00:00:00");
    }
}