s2n_quic_core/inet/
ethernet.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::inet::Unspecified;
5use core::fmt;
6
7// NOTE: duvet doesn't know how to parse this RFC since it doesn't follow more modern formatting
8//# https://www.rfc-editor.org/rfc/rfc826
9//# Packet format:
10//# --------------
11//#
12//# To communicate mappings from <protocol, address> pairs to 48.bit
13//# Ethernet addresses, a packet format that embodies the Address
14//# Resolution protocol is needed.  The format of the packet follows.
15//#
16//#    Ethernet transmission layer (not necessarily accessible to
17//#         the user):
18//#        48.bit: Ethernet address of destination
19//#        48.bit: Ethernet address of sender
20const MAC_LEN: usize = 48 / 8;
21
22define_inet_type!(
23    pub struct MacAddress {
24        octets: [u8; MAC_LEN],
25    }
26);
27
28impl fmt::Debug for MacAddress {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        f.debug_tuple("MacAddress")
31            .field(&format_args!("{self}"))
32            .finish()
33    }
34}
35
36impl fmt::Display for MacAddress {
37    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
38        let [a, b, c, d, e, f] = self.octets;
39        write!(fmt, "{a:02x}:{b:02x}:{c:02x}:{d:02x}:{e:02x}:{f:02x}")
40    }
41}
42
43impl MacAddress {
44    pub const UNSPECIFIED: Self = Self {
45        octets: [0; MAC_LEN],
46    };
47}
48
49impl Unspecified for MacAddress {
50    #[inline]
51    fn is_unspecified(&self) -> bool {
52        self.octets == [0; MAC_LEN]
53    }
54}
55
56define_inet_type!(
57    pub struct EtherType {
58        id: [u8; 2],
59    }
60);
61
62impl fmt::Debug for EtherType {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        f.debug_tuple("EtherType")
65            .field(&format_args!("{self}"))
66            .finish()
67    }
68}
69
70impl fmt::Display for EtherType {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        match *self {
73            Self::IPV4 => "IPv4",
74            Self::ARP => "ARP",
75            Self::IPV6 => "IPv6",
76            Self::VLAN => "VLAN",
77            Self::PPP => "PPP",
78            Self { id: [a, b] } => return write!(f, "[unknown 0x{a:02x}{b:02x}]"),
79        }
80        .fmt(f)
81    }
82}
83
84macro_rules! impl_type {
85    ($fun:ident, $cap:ident, $val:expr) => {
86        pub const $cap: Self = Self { id: $val };
87
88        #[inline]
89        pub const fn $fun(self) -> bool {
90            matches!(self, Self::$cap)
91        }
92    };
93}
94
95impl EtherType {
96    // https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml#ieee-802-numbers-1
97    // NOTE: these variants were added as the ones we think we'll need. feel free to add more as
98    //       needed.
99    impl_type!(is_ipv4, IPV4, [0x08, 0x00]);
100    impl_type!(is_arp, ARP, [0x08, 0x06]);
101    impl_type!(is_ipv6, IPV6, [0x86, 0xDD]);
102    impl_type!(is_ppp, PPP, [0x88, 0x0B]);
103    impl_type!(is_vlan, VLAN, [0x88, 0xA8]);
104}
105
106// NOTE: duvet doesn't know how to parse this RFC since it doesn't follow more modern formatting
107//# https://www.rfc-editor.org/rfc/rfc826
108//# Packet format:
109//# --------------
110//#
111//# To communicate mappings from <protocol, address> pairs to 48.bit
112//# Ethernet addresses, a packet format that embodies the Address
113//# Resolution protocol is needed.  The format of the packet follows.
114//#
115//#    Ethernet transmission layer (not necessarily accessible to
116//#         the user):
117//#        48.bit: Ethernet address of destination
118//#        48.bit: Ethernet address of sender
119//#        16.bit: Protocol type = ether_type$ADDRESS_RESOLUTION
120
121define_inet_type!(
122    pub struct Header {
123        destination: MacAddress,
124        source: MacAddress,
125        ethertype: EtherType,
126    }
127);
128
129impl fmt::Debug for Header {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        f.debug_struct("ethernet::Header")
132            .field("destination", &self.destination)
133            .field("source", &self.source)
134            .field("ethertype", &self.ethertype)
135            .finish()
136    }
137}
138
139impl Header {
140    /// Swaps the direction of the header
141    #[inline]
142    pub fn swap(&mut self) {
143        core::mem::swap(&mut self.source, &mut self.destination)
144    }
145
146    #[inline]
147    pub const fn destination(&self) -> &MacAddress {
148        &self.destination
149    }
150
151    #[inline]
152    pub fn destination_mut(&mut self) -> &mut MacAddress {
153        &mut self.destination
154    }
155
156    #[inline]
157    pub const fn source(&self) -> &MacAddress {
158        &self.source
159    }
160
161    #[inline]
162    pub fn source_mut(&mut self) -> &mut MacAddress {
163        &mut self.source
164    }
165
166    #[inline]
167    pub const fn ethertype(&self) -> &EtherType {
168        &self.ethertype
169    }
170
171    #[inline]
172    pub fn ethertype_mut(&mut self) -> &mut EtherType {
173        &mut self.ethertype
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use bolero::check;
181    use s2n_codec::DecoderBuffer;
182
183    #[test]
184    #[cfg_attr(miri, ignore)]
185    fn snapshot_test() {
186        let mut buffer = vec![0u8; core::mem::size_of::<Header>()];
187        for (idx, byte) in buffer.iter_mut().enumerate() {
188            *byte = idx as u8;
189        }
190        let decoder = DecoderBuffer::new(&buffer);
191        let (header, _) = decoder.decode::<&Header>().unwrap();
192        insta::assert_debug_snapshot!("snapshot_test", header);
193
194        buffer.fill(255);
195        let decoder = DecoderBuffer::new(&buffer);
196        let (header, _) = decoder.decode::<&Header>().unwrap();
197        insta::assert_debug_snapshot!("snapshot_filled_test", header);
198    }
199
200    #[test]
201    fn header_round_trip_test() {
202        check!().for_each(|buffer| {
203            s2n_codec::assert_codec_round_trip_bytes!(Header, buffer);
204        });
205    }
206}