1use std::fmt::Debug;
8
9use super::Incomplete;
10use super::bytes::{InnerBytes, NetworkBytes, NetworkBytesMut};
11use crate::dumbo::MacAddr;
12
13const DST_MAC_OFFSET: usize = 0;
14const SRC_MAC_OFFSET: usize = 6;
15const ETHERTYPE_OFFSET: usize = 12;
16
17pub const PAYLOAD_OFFSET: usize = 14;
22
23pub const ETHERTYPE_ARP: u16 = 0x0806;
25pub const ETHERTYPE_IPV4: u16 = 0x0800;
27
28#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
30pub enum EthernetError {
31 SliceTooShort,
33}
34
35#[derive(Debug)]
37pub struct EthernetFrame<'a, T: 'a> {
38 bytes: InnerBytes<'a, T>,
39}
40
41#[allow(clippy::len_without_is_empty)]
42impl<T: NetworkBytes + Debug> EthernetFrame<'_, T> {
43 #[inline]
50 pub fn from_bytes_unchecked(bytes: T) -> Self {
51 EthernetFrame {
52 bytes: InnerBytes::new(bytes),
53 }
54 }
55
56 #[inline]
58 pub fn from_bytes(bytes: T) -> Result<Self, EthernetError> {
59 if bytes.len() < PAYLOAD_OFFSET {
60 return Err(EthernetError::SliceTooShort);
61 }
62
63 Ok(EthernetFrame::from_bytes_unchecked(bytes))
64 }
65
66 #[inline]
68 pub fn dst_mac(&self) -> MacAddr {
69 MacAddr::from_bytes_unchecked(&self.bytes[DST_MAC_OFFSET..SRC_MAC_OFFSET])
70 }
71
72 #[inline]
74 pub fn src_mac(&self) -> MacAddr {
75 MacAddr::from_bytes_unchecked(&self.bytes[SRC_MAC_OFFSET..ETHERTYPE_OFFSET])
76 }
77
78 #[inline]
80 pub fn ethertype(&self) -> u16 {
81 self.bytes.ntohs_unchecked(ETHERTYPE_OFFSET)
82 }
83
84 #[inline]
86 pub fn payload_offset(&self) -> usize {
87 PAYLOAD_OFFSET
88 }
89
90 #[inline]
92 pub fn payload(&self) -> &[u8] {
93 self.bytes.split_at(self.payload_offset()).1
94 }
95
96 #[inline]
98 pub fn len(&self) -> usize {
99 self.bytes.len()
100 }
101}
102
103impl<T: NetworkBytesMut + Debug> EthernetFrame<'_, T> {
104 fn new_with_header(
106 buf: T,
107 dst_mac: MacAddr,
108 src_mac: MacAddr,
109 ethertype: u16,
110 ) -> Result<Self, EthernetError> {
111 if buf.len() < PAYLOAD_OFFSET {
112 return Err(EthernetError::SliceTooShort);
113 }
114
115 let mut frame = EthernetFrame::from_bytes_unchecked(buf);
116
117 frame
118 .set_dst_mac(dst_mac)
119 .set_src_mac(src_mac)
120 .set_ethertype(ethertype);
121
122 Ok(frame)
123 }
124
125 #[inline]
128 pub fn write_incomplete(
129 buf: T,
130 dst_mac: MacAddr,
131 src_mac: MacAddr,
132 ethertype: u16,
133 ) -> Result<Incomplete<Self>, EthernetError> {
134 Ok(Incomplete::new(Self::new_with_header(
135 buf, dst_mac, src_mac, ethertype,
136 )?))
137 }
138
139 #[inline]
141 pub fn set_dst_mac(&mut self, addr: MacAddr) -> &mut Self {
142 self.bytes[DST_MAC_OFFSET..SRC_MAC_OFFSET].copy_from_slice(addr.get_bytes());
143 self
144 }
145
146 #[inline]
148 pub fn set_src_mac(&mut self, addr: MacAddr) -> &mut Self {
149 self.bytes[SRC_MAC_OFFSET..ETHERTYPE_OFFSET].copy_from_slice(addr.get_bytes());
150 self
151 }
152
153 #[inline]
155 pub fn set_ethertype(&mut self, value: u16) -> &mut Self {
156 self.bytes.htons_unchecked(ETHERTYPE_OFFSET, value);
157 self
158 }
159
160 #[inline]
162 pub fn payload_mut(&mut self) -> &mut [u8] {
163 let offset = self.payload_offset();
165 self.bytes.split_at_mut(offset).1
166 }
167}
168
169impl<'a, T: NetworkBytes + Debug> Incomplete<EthernetFrame<'a, T>> {
170 #[inline]
176 pub fn with_payload_len_unchecked(mut self, payload_len: usize) -> EthernetFrame<'a, T> {
177 let payload_offset = self.inner.payload_offset();
178 self.inner
179 .bytes
180 .shrink_unchecked(payload_offset + payload_len);
181 self.inner
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use std::str::FromStr;
188
189 use super::*;
190
191 #[test]
192 fn test_ethernet_frame() {
193 let mut a = [0u8; 10000];
194 let mut bad_array = [0u8; 1];
195
196 let dst_mac = MacAddr::from_str("01:23:45:67:89:ab").unwrap();
197 let src_mac = MacAddr::from_str("cd:ef:01:23:45:67").unwrap();
198 let ethertype = 1289;
199
200 assert_eq!(
201 EthernetFrame::from_bytes(bad_array.as_ref()).unwrap_err(),
202 EthernetError::SliceTooShort
203 );
204 assert_eq!(
205 EthernetFrame::new_with_header(bad_array.as_mut(), dst_mac, src_mac, ethertype)
206 .unwrap_err(),
207 EthernetError::SliceTooShort
208 );
209
210 {
211 let mut f1 =
212 EthernetFrame::new_with_header(a.as_mut(), dst_mac, src_mac, ethertype).unwrap();
213
214 assert_eq!(f1.dst_mac(), dst_mac);
215 assert_eq!(f1.src_mac(), src_mac);
216 assert_eq!(f1.ethertype(), ethertype);
217 f1.payload_mut()[1] = 132;
218 }
219
220 {
221 let f2 = EthernetFrame::from_bytes(a.as_ref()).unwrap();
222
223 assert_eq!(f2.dst_mac(), dst_mac);
224 assert_eq!(f2.src_mac(), src_mac);
225 assert_eq!(f2.ethertype(), ethertype);
226 assert_eq!(f2.payload()[1], 132);
227 assert_eq!(f2.len(), f2.bytes.len());
228 }
229
230 {
231 let f3 =
232 EthernetFrame::write_incomplete(a.as_mut(), dst_mac, src_mac, ethertype).unwrap();
233 let f3_complete = f3.with_payload_len_unchecked(123);
234 assert_eq!(f3_complete.len(), f3_complete.payload_offset() + 123);
235 }
236 }
237}
238
239#[cfg(kani)]
240#[allow(dead_code)] mod kani_proofs {
242 use super::*;
243 use crate::utils::net::mac::MAC_ADDR_LEN;
244
245 pub const MAX_FRAME_SIZE: usize = 1514;
248
249 const MAC_ADDR_LEN_USIZE: usize = MAC_ADDR_LEN as usize;
250
251 impl<'a, T: NetworkBytesMut + Debug> EthernetFrame<'a, T> {
252 fn is_valid(&self) -> bool {
253 self.len() >= PAYLOAD_OFFSET
254 }
255 }
256
257 #[kani::proof]
261 fn verify_from_bytes_unchecked() {
262 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
264 let slice_length = bytes.len();
265
266 let ethernet = EthernetFrame::from_bytes_unchecked(bytes.as_mut());
268
269 assert_eq!(ethernet.len(), slice_length);
271 assert!(
272 !(ethernet.is_valid()) || (ethernet.payload().len() == slice_length - PAYLOAD_OFFSET)
273 );
274 }
275
276 #[kani::proof]
277 fn verify_from_bytes() {
278 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
280 let slice_length = bytes.len();
281
282 let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
284
285 if slice_length >= PAYLOAD_OFFSET {
287 let ethernet = ethernet.unwrap();
288 assert!(ethernet.is_valid());
289 assert_eq!(ethernet.len(), slice_length);
290 assert_eq!(ethernet.payload().len(), slice_length - PAYLOAD_OFFSET);
291 } else {
292 ethernet.unwrap_err();
293 }
294 }
295
296 #[kani::proof]
297 fn verify_dst_mac() {
298 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
300
301 let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
303 kani::assume(ethernet.is_ok());
304 let mut ethernet = ethernet.unwrap();
305
306 let mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
308 let dst_mac = MacAddr::from(mac_bytes);
309 ethernet.set_dst_mac(dst_mac);
310
311 let dst_addr = EthernetFrame::dst_mac(ðernet);
313
314 assert_eq!(dst_addr.get_bytes().len(), MAC_ADDR_LEN as usize);
318
319 let i: usize = kani::any();
321 kani::assume(i < mac_bytes.len());
322 assert_eq!(mac_bytes[i], dst_addr.get_bytes()[i]);
323 }
324
325 #[kani::proof]
326 fn verify_src_mac() {
327 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
329
330 let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
332 kani::assume(ethernet.is_ok());
333 let mut ethernet = ethernet.unwrap();
334
335 let mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
337 let src_mac = MacAddr::from(mac_bytes);
338 ethernet.set_src_mac(src_mac);
339
340 let src_addr = EthernetFrame::src_mac(ðernet);
342
343 assert_eq!(src_addr.get_bytes().len(), MAC_ADDR_LEN as usize);
347
348 let i: usize = kani::any();
350 kani::assume(i < mac_bytes.len());
351 assert_eq!(mac_bytes[i], src_addr.get_bytes()[i]);
352 }
353
354 #[kani::proof]
355 fn verify_src_mac_isolation() {
356 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
358
359 let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
361 kani::assume(ethernet.is_ok());
362 let mut ethernet = ethernet.unwrap();
363
364 let mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
366 let src_mac = MacAddr::from(mac_bytes);
367 ethernet.set_src_mac(src_mac);
368
369 let payload_offset = ethernet.payload_offset();
370
371 if kani::any() {
372 let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] = kani::any();
373 let dst_mac = MacAddr::from(dst_mac_bytes);
374 ethernet.set_dst_mac(dst_mac);
375 }
376 if kani::any() {
377 let ethertype_in: u16 = kani::any();
378 ethernet.set_ethertype(ethertype_in);
379 }
380
381 assert_eq!(ethernet.payload_offset(), payload_offset);
383
384 let src_addr = EthernetFrame::src_mac(ðernet);
386
387 assert_eq!(src_addr.get_bytes().len(), MAC_ADDR_LEN as usize);
391
392 let i: usize = kani::any();
394 kani::assume(i < mac_bytes.len());
395 assert_eq!(mac_bytes[i], src_addr.get_bytes()[i]);
396 }
397
398 #[kani::proof]
399 fn verify_ethertype() {
400 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
402
403 let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
405 kani::assume(ethernet.is_ok());
406 let mut ethernet = ethernet.unwrap();
407
408 let ethertype_in: u16 = kani::any();
410 ethernet.set_ethertype(ethertype_in);
411
412 let ethertype_out = ethernet.ethertype();
414
415 assert_eq!(ethertype_in, ethertype_out);
419 }
420
421 #[kani::proof]
422 #[kani::unwind(1515)]
423 fn verify_payload() {
424 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
426
427 let ethernet = EthernetFrame::from_bytes(bytes.as_mut());
429 kani::assume(ethernet.is_ok());
430 let ethernet = ethernet.unwrap();
431
432 let payload_offset = ethernet.payload_offset();
434
435 let payload = ethernet.payload();
437
438 let payload_mut = ethernet.payload();
440
441 assert_eq!(payload_offset, PAYLOAD_OFFSET);
445
446 assert_eq!(payload, payload_mut);
448 }
449
450 #[kani::proof]
451 fn verify_new_with_header() {
452 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
454 let bytes_length = bytes.len();
455
456 let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
458 kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
459 let dst_mac = MacAddr::from(dst_mac_bytes);
460
461 let src_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
463 kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
464 let src_mac = MacAddr::from(src_mac_bytes);
465
466 let ethertype: u16 = kani::any();
468
469 let frame =
471 EthernetFrame::new_with_header(bytes.as_mut(), dst_mac, src_mac, ethertype).unwrap();
472
473 assert_eq!(frame.dst_mac(), dst_mac);
475 assert_eq!(frame.src_mac(), src_mac);
476 assert_eq!(frame.ethertype(), ethertype);
477 assert_eq!(frame.len(), bytes_length);
478 assert!(frame.is_valid() && (frame.payload().len() == bytes_length - PAYLOAD_OFFSET));
479 }
480
481 #[kani::proof]
482 fn verify_write_incomplete() {
483 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
485
486 let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
488 kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
489 let dst_mac = MacAddr::from(dst_mac_bytes);
490
491 let src_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
493 kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
494 let src_mac = MacAddr::from(src_mac_bytes);
495
496 let ethertype: u16 = kani::any();
498
499 let incomplete_frame =
501 EthernetFrame::write_incomplete(bytes.as_mut(), dst_mac, src_mac, ethertype).unwrap();
502
503 assert_eq!(incomplete_frame.inner.dst_mac(), dst_mac);
505 assert_eq!(incomplete_frame.inner.src_mac(), src_mac);
506 assert_eq!(incomplete_frame.inner.ethertype(), ethertype);
507 }
508
509 #[kani::proof]
510 #[kani::solver(cadical)]
511 fn verify_with_payload_len_unchecked() {
512 let mut bytes: [u8; MAX_FRAME_SIZE] = kani::Arbitrary::any_array::<MAX_FRAME_SIZE>();
514
515 let dst_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
517 kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
518 let dst_mac = MacAddr::from(dst_mac_bytes);
519
520 let src_mac_bytes: [u8; MAC_ADDR_LEN as usize] =
522 kani::Arbitrary::any_array::<MAC_ADDR_LEN_USIZE>();
523 let src_mac = MacAddr::from(src_mac_bytes);
524
525 let ethertype: u16 = kani::any();
527
528 let incomplete_frame =
530 EthernetFrame::write_incomplete(bytes.as_mut(), dst_mac, src_mac, ethertype).unwrap();
531 let incomplete_frame_payload_offset = incomplete_frame.inner.payload_offset();
532 let incomplete_frame_len = incomplete_frame.inner.len();
533
534 let payload_len: usize = kani::any();
536 kani::assume(payload_len <= incomplete_frame_len - incomplete_frame_payload_offset);
537
538 let unchecked_frame = incomplete_frame.with_payload_len_unchecked(payload_len);
540
541 assert!(unchecked_frame.is_valid());
543 assert_eq!(unchecked_frame.dst_mac(), dst_mac);
544 assert_eq!(unchecked_frame.src_mac(), src_mac);
545 assert_eq!(unchecked_frame.ethertype(), ethertype);
546 }
547}