1use core::{error::Error, fmt, mem};
2
3use num_traits::FromPrimitive as _;
4
5#[derive(Debug, Eq, PartialEq)]
7pub enum EthernetError {
8 InvalidEtherType(u16),
10}
11
12impl EthernetError {
13 pub fn msg_and_code(&self) -> (&'static str, u16) {
14 match self {
15 Self::InvalidEtherType(id) => ("invalid ID of an encapsulated protocol", *id),
16 }
17 }
18}
19
20impl fmt::Display for EthernetError {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 let (msg, code) = self.msg_and_code();
23 write!(f, "{msg}: {code}")
24 }
25}
26
27impl Error for EthernetError {}
28
29#[repr(C)]
32#[derive(Debug, Copy, Clone)]
33#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
34#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
35pub struct EthHdr {
36 pub dst_addr: [u8; 6],
38 pub src_addr: [u8; 6],
40 pub ether_type: u16,
43}
44
45impl EthHdr {
46 pub const LEN: usize = mem::size_of::<EthHdr>();
47
48 pub fn ether_type(&self) -> Result<EtherType, EthernetError> {
55 EtherType::from_u16(self.ether_type).ok_or(EthernetError::InvalidEtherType(self.ether_type))
56 }
57
58 pub fn new(dst_addr: [u8; 6], src_addr: [u8; 6], eth_type: EtherType) -> Self {
68 EthHdr {
69 dst_addr,
70 src_addr,
71 ether_type: eth_type.into(),
72 }
73 }
74}
75
76#[repr(u16)]
79#[derive(PartialEq, Eq, Debug, Copy, Clone, num_derive::FromPrimitive, num_derive::ToPrimitive)]
80#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
81pub enum EtherType {
82 Loop = 0x0060_u16.to_be(),
83 Ipv4 = 0x0800_u16.to_be(),
84 Arp = 0x0806_u16.to_be(),
85 Ieee8021q = 0x8100_u16.to_be(),
86 Ipv6 = 0x86DD_u16.to_be(),
87 Ieee8021ad = 0x88A8_u16.to_be(),
88 Ieee8021MacSec = 0x88E5_u16.to_be(),
89 Ieee8021ah = 0x88E7_u16.to_be(),
90 Ieee8021mvrp = 0x88F5_u16.to_be(),
91 FibreChannel = 0x8906_u16.to_be(),
92 Infiniband = 0x8915_u16.to_be(),
93 LoopbackIeee8023 = 0x9000_u16.to_be(),
94 Ieee8021QinQ1 = 0x9100_u16.to_be(),
95 Ieee8021QinQ2 = 0x9200_u16.to_be(),
96 Ieee8021QinQ3 = 0x9300_u16.to_be(),
97}
98
99impl From<EtherType> for u16 {
103 fn from(ether_type: EtherType) -> Self {
104 ether_type as u16
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use assert_matches::assert_matches;
112 use core::mem;
113
114 const TEST_DST_MAC: [u8; 6] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55];
116 const TEST_SRC_MAC: [u8; 6] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
117
118 #[test]
119 fn test_ethhdr_len() {
120 assert_eq!(EthHdr::LEN, 14);
121 assert_eq!(mem::size_of::<EthHdr>(), 14);
122 }
123
124 #[test]
125 fn test_ethhdr_new() {
126 let eth_hdr = EthHdr::new(TEST_DST_MAC, TEST_SRC_MAC, EtherType::Ipv4);
127 assert_eq!(eth_hdr.dst_addr, TEST_DST_MAC);
128 assert_eq!(eth_hdr.src_addr, TEST_SRC_MAC);
129 let ether_type_value = eth_hdr.ether_type;
130 assert_eq!(ether_type_value, EtherType::Ipv4 as u16);
131 assert_eq!(ether_type_value, 0x0800_u16.to_be());
132 }
133
134 #[test]
135 fn test_ethhdr_ether_type_method_known() {
136 let eth_hdr = EthHdr {
137 dst_addr: TEST_DST_MAC,
138 src_addr: TEST_SRC_MAC,
139 ether_type: EtherType::Ipv6 as u16,
140 };
141 assert_eq!(eth_hdr.ether_type().unwrap(), EtherType::Ipv6);
142 }
143
144 #[test]
145 fn test_ethhdr_ether_type_method_unknown() {
146 let unknown_type_val = 0x1234_u16;
147 let eth_hdr = EthHdr {
148 dst_addr: TEST_DST_MAC,
149 src_addr: TEST_SRC_MAC,
150 ether_type: unknown_type_val.to_be(),
151 };
152 assert_matches!(eth_hdr.ether_type(), Err(EthernetError::InvalidEtherType(val)) if val == unknown_type_val.to_be());
153 }
154
155 #[test]
156 fn test_ethertype_from_u16_known() {
157 let ipv4_val = 0x0800_u16.to_be();
158 assert_eq!(EtherType::from_u16(ipv4_val), Some(EtherType::Ipv4));
159
160 let ipv6_val = 0x86DD_u16.to_be();
161 assert_eq!(EtherType::from_u16(ipv6_val), Some(EtherType::Ipv6));
162
163 let arp_val = 0x0806_u16.to_be();
164 assert_eq!(EtherType::from_u16(arp_val), Some(EtherType::Arp));
165 }
166
167 #[test]
168 fn test_ethertype_try_from_u16_unknown() {
169 let unknown_val = 0x1234_u16.to_be();
170 assert_eq!(EtherType::from_u16(unknown_val), None);
171 }
172
173 #[test]
174 fn test_u16_from_ethertype() {
175 assert_eq!(u16::from(EtherType::Ipv4), 0x0800_u16.to_be());
176 assert_eq!(u16::from(EtherType::Arp), 0x0806_u16.to_be());
177 assert_eq!(u16::from(EtherType::Ipv6), 0x86DD_u16.to_be());
178 assert_eq!(u16::from(EtherType::Loop), 0x0060_u16.to_be());
179 }
180
181 #[test]
182 fn test_ethertype_variants_unique_values() {
183 let all_types = [
184 EtherType::Loop,
185 EtherType::Ipv4,
186 EtherType::Arp,
187 EtherType::Ieee8021q,
188 EtherType::Ipv6,
189 EtherType::Ieee8021ad,
190 EtherType::Ieee8021MacSec,
191 EtherType::Ieee8021ah,
192 EtherType::Ieee8021mvrp,
193 EtherType::FibreChannel,
194 EtherType::Infiniband,
195 EtherType::LoopbackIeee8023,
196 EtherType::Ieee8021QinQ1,
197 EtherType::Ieee8021QinQ2,
198 EtherType::Ieee8021QinQ3,
199 ];
200
201 for i in 0..all_types.len() {
202 for j in (i + 1)..all_types.len() {
203 let val_i = all_types[i] as u16;
205 let val_j = all_types[j] as u16;
206 assert_ne!(
207 val_i, val_j,
208 "Duplicate EtherType value found: {:?} and {:?} both have value {:#06x}",
209 all_types[i], all_types[j], val_i
210 );
211 }
212 }
213 }
214}