Skip to main content

stackforge_core/layer/
ethernet.rs

1//! Ethernet II frame layer implementation.
2//!
3//! This module provides complete Ethernet II frame handling including
4//! dispatch hooks for 802.3 detection and Scapy-compatible methods.
5
6use crate::layer::field::{Field, FieldDesc, FieldError, FieldType, FieldValue, MacAddress};
7use crate::layer::{Layer, LayerIndex, LayerKind, ethertype};
8
9/// Ethernet header length in bytes.
10pub const ETHERNET_HEADER_LEN: usize = 14;
11
12/// Maximum value for 802.3 length field (values > 1500 are `EtherTypes`)
13pub const DOT3_MAX_LENGTH: u16 = 1500;
14
15/// Field offsets within Ethernet header.
16pub mod offsets {
17    pub const DST: usize = 0;
18    pub const SRC: usize = 6;
19    pub const TYPE: usize = 12;
20}
21
22/// Field descriptors for dynamic access (Ethernet II).
23pub static FIELDS: &[FieldDesc] = &[
24    FieldDesc::new("dst", offsets::DST, 6, FieldType::Mac),
25    FieldDesc::new("src", offsets::SRC, 6, FieldType::Mac),
26    FieldDesc::new("type", offsets::TYPE, 2, FieldType::U16),
27];
28
29/// Field descriptors for dynamic access (802.3/Dot3).
30pub static DOT3_FIELDS: &[FieldDesc] = &[
31    FieldDesc::new("dst", offsets::DST, 6, FieldType::Mac),
32    FieldDesc::new("src", offsets::SRC, 6, FieldType::Mac),
33    FieldDesc::new("len", offsets::TYPE, 2, FieldType::U16),
34];
35
36/// Frame type discrimination result
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum EthernetFrameType {
39    /// Ethernet II (`EtherType` > 1500)
40    EthernetII,
41    /// IEEE 802.3 (length field <= 1500)
42    Dot3,
43}
44
45/// Dispatch hook to determine frame type (Ethernet II vs 802.3)
46#[inline]
47#[must_use]
48pub fn dispatch_hook(buf: &[u8], offset: usize) -> EthernetFrameType {
49    if buf.len() < offset + ETHERNET_HEADER_LEN {
50        return EthernetFrameType::EthernetII;
51    }
52
53    let type_or_len = u16::from_be_bytes([buf[offset + 12], buf[offset + 13]]);
54
55    if type_or_len <= DOT3_MAX_LENGTH {
56        EthernetFrameType::Dot3
57    } else {
58        EthernetFrameType::EthernetII
59    }
60}
61
62/// Check if a frame at offset is 802.3 (not Ethernet II)
63#[inline]
64#[must_use]
65pub fn is_dot3(buf: &[u8], offset: usize) -> bool {
66    dispatch_hook(buf, offset) == EthernetFrameType::Dot3
67}
68
69/// Check if a frame at offset is Ethernet II
70#[inline]
71#[must_use]
72pub fn is_ethernet_ii(buf: &[u8], offset: usize) -> bool {
73    dispatch_hook(buf, offset) == EthernetFrameType::EthernetII
74}
75
76/// A view into an Ethernet II frame.
77#[derive(Debug, Clone)]
78pub struct EthernetLayer {
79    pub index: LayerIndex,
80}
81
82impl EthernetLayer {
83    #[inline]
84    #[must_use]
85    pub const fn new(start: usize, end: usize) -> Self {
86        Self {
87            index: LayerIndex::new(LayerKind::Ethernet, start, end),
88        }
89    }
90
91    #[inline]
92    #[must_use]
93    pub const fn at_start() -> Self {
94        Self::new(0, ETHERNET_HEADER_LEN)
95    }
96
97    #[inline]
98    #[must_use]
99    pub const fn at_offset(offset: usize) -> Self {
100        Self::new(offset, offset + ETHERNET_HEADER_LEN)
101    }
102
103    #[inline]
104    pub fn validate(buf: &[u8], offset: usize) -> Result<(), FieldError> {
105        if buf.len() < offset + ETHERNET_HEADER_LEN {
106            return Err(FieldError::BufferTooShort {
107                offset,
108                need: ETHERNET_HEADER_LEN,
109                have: buf.len().saturating_sub(offset),
110            });
111        }
112        Ok(())
113    }
114
115    /// Dispatch hook: returns appropriate layer type based on type/length field
116    #[must_use]
117    pub fn dispatch(buf: &[u8], offset: usize) -> EthernetFrameType {
118        dispatch_hook(buf, offset)
119    }
120
121    // ========== Field Readers ==========
122    #[inline]
123    pub fn dst(&self, buf: &[u8]) -> Result<MacAddress, FieldError> {
124        MacAddress::read(buf, self.index.start + offsets::DST)
125    }
126
127    #[inline]
128    pub fn src(&self, buf: &[u8]) -> Result<MacAddress, FieldError> {
129        MacAddress::read(buf, self.index.start + offsets::SRC)
130    }
131
132    #[inline]
133    pub fn ethertype(&self, buf: &[u8]) -> Result<u16, FieldError> {
134        u16::read(buf, self.index.start + offsets::TYPE)
135    }
136
137    // ========== Field Writers ==========
138    #[inline]
139    pub fn set_dst(&self, buf: &mut [u8], mac: MacAddress) -> Result<(), FieldError> {
140        mac.write(buf, self.index.start + offsets::DST)
141    }
142
143    #[inline]
144    pub fn set_src(&self, buf: &mut [u8], mac: MacAddress) -> Result<(), FieldError> {
145        mac.write(buf, self.index.start + offsets::SRC)
146    }
147
148    #[inline]
149    pub fn set_ethertype(&self, buf: &mut [u8], etype: u16) -> Result<(), FieldError> {
150        etype.write(buf, self.index.start + offsets::TYPE)
151    }
152
153    // ========== Dynamic Field Access ==========
154    #[must_use]
155    pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
156        FIELDS
157            .iter()
158            .find(|f| f.name == name)
159            .map(|desc| FieldValue::read(buf, &desc.with_offset(self.index.start)))
160    }
161
162    pub fn set_field(
163        &self,
164        buf: &mut [u8],
165        name: &str,
166        value: FieldValue,
167    ) -> Option<Result<(), FieldError>> {
168        FIELDS
169            .iter()
170            .find(|f| f.name == name)
171            .map(|desc| value.write(buf, &desc.with_offset(self.index.start)))
172    }
173
174    pub fn set_field_value<V: Into<FieldValue>>(
175        &self,
176        buf: &mut [u8],
177        name: &str,
178        value: V,
179    ) -> Option<Result<(), FieldError>> {
180        self.set_field(buf, name, value.into())
181    }
182
183    #[must_use]
184    pub fn field_names() -> &'static [&'static str] {
185        &["dst", "src", "type"]
186    }
187
188    /// Compute hash for packet matching.
189    /// Returns type field + payload hash for matching request/response pairs.
190    #[must_use]
191    pub fn hashret(&self, buf: &[u8]) -> Vec<u8> {
192        let etype = self.ethertype(buf).unwrap_or(0);
193        etype.to_be_bytes().to_vec()
194        // In full implementation, would append: + self.payload_layer().hashret()
195    }
196
197    /// Check if this packet answers another.
198    /// For Ethernet, this delegates to payload matching.
199    #[must_use]
200    pub fn answers(&self, buf: &[u8], other: &EthernetLayer, other_buf: &[u8]) -> bool {
201        // Types must match
202        if self.ethertype(buf) != other.ethertype(other_buf) {
203            return false;
204        }
205        // In full implementation, would delegate to payload:
206        // self.payload_layer().answers(other.payload_layer())
207        true
208    }
209
210    /// Extract padding (Ethernet II has no padding concept at this layer)
211    #[must_use]
212    pub fn extract_padding<'a>(&self, buf: &'a [u8]) -> (&'a [u8], &'a [u8]) {
213        let payload_start = self.index.end.min(buf.len());
214        (&buf[payload_start..], &[])
215    }
216
217    // ========== Utility Methods ==========
218    #[inline]
219    #[must_use]
220    pub fn payload<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
221        &buf[self.index.end..]
222    }
223
224    #[inline]
225    #[must_use]
226    pub fn payload_copy(&self, buf: &[u8]) -> Vec<u8> {
227        buf[self.index.end..].to_vec()
228    }
229
230    #[must_use]
231    pub fn next_layer(&self, buf: &[u8]) -> Option<LayerKind> {
232        self.ethertype(buf).ok().and_then(|t| match t {
233            ethertype::IPV4 => Some(LayerKind::Ipv4),
234            ethertype::IPV6 => Some(LayerKind::Ipv6),
235            ethertype::ARP => Some(LayerKind::Arp),
236            ethertype::VLAN => Some(LayerKind::Raw), // Would be Dot1Q
237            _ => None,
238        })
239    }
240
241    #[inline]
242    #[must_use]
243    pub fn is_broadcast(&self, buf: &[u8]) -> bool {
244        self.dst(buf).map(|m| m.is_broadcast()).unwrap_or(false)
245    }
246
247    #[inline]
248    #[must_use]
249    pub fn is_multicast(&self, buf: &[u8]) -> bool {
250        self.dst(buf).map(|m| m.is_multicast()).unwrap_or(false)
251    }
252
253    #[inline]
254    #[must_use]
255    pub fn is_unicast(&self, buf: &[u8]) -> bool {
256        self.dst(buf).map(|m| m.is_unicast()).unwrap_or(false)
257    }
258
259    #[inline]
260    #[must_use]
261    pub fn header_bytes<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
262        &buf[self.index.start..self.index.end.min(buf.len())]
263    }
264
265    #[inline]
266    #[must_use]
267    pub fn header_copy(&self, buf: &[u8]) -> Vec<u8> {
268        self.header_bytes(buf).to_vec()
269    }
270
271    /// Get `EtherType` name
272    pub fn ethertype_name(&self, buf: &[u8]) -> &'static str {
273        self.ethertype(buf)
274            .map(ethertype::name)
275            .unwrap_or("Unknown")
276    }
277
278    /// Routing for Ethernet layer
279    #[must_use]
280    pub fn route(&self, _buf: &[u8]) -> Option<String> {
281        // Would delegate to payload for actual routing
282        // For now, return None - caller must use payload's route()
283        None
284    }
285}
286
287impl Layer for EthernetLayer {
288    fn kind(&self) -> LayerKind {
289        LayerKind::Ethernet
290    }
291
292    fn summary(&self, buf: &[u8]) -> String {
293        let dst = self.dst(buf).map_or_else(|_| "?".into(), |m| m.to_string());
294        let src = self.src(buf).map_or_else(|_| "?".into(), |m| m.to_string());
295        let etype = self.ethertype(buf).unwrap_or(0);
296        format!("{} > {} ({})", src, dst, ethertype::name(etype))
297    }
298
299    fn header_len(&self, _buf: &[u8]) -> usize {
300        ETHERNET_HEADER_LEN
301    }
302
303    fn hashret(&self, buf: &[u8]) -> Vec<u8> {
304        self.hashret(buf)
305    }
306
307    fn answers(&self, buf: &[u8], other: &Self, other_buf: &[u8]) -> bool {
308        self.answers(buf, other, other_buf)
309    }
310
311    fn extract_padding<'a>(&self, buf: &'a [u8]) -> (&'a [u8], &'a [u8]) {
312        self.extract_padding(buf)
313    }
314}
315
316// ============================================================================
317// IEEE 802.3 Layer
318// ============================================================================
319
320/// IEEE 802.3 frame (uses length field instead of `EtherType`)
321#[derive(Debug, Clone)]
322pub struct Dot3Layer {
323    pub index: LayerIndex,
324}
325
326impl Dot3Layer {
327    #[inline]
328    #[must_use]
329    pub const fn new(start: usize, end: usize) -> Self {
330        Self {
331            index: LayerIndex::new(LayerKind::Raw, start, end),
332        } // Using Raw for now
333    }
334
335    #[inline]
336    #[must_use]
337    pub const fn at_start() -> Self {
338        Self::new(0, ETHERNET_HEADER_LEN)
339    }
340
341    #[inline]
342    #[must_use]
343    pub const fn at_offset(offset: usize) -> Self {
344        Self::new(offset, offset + ETHERNET_HEADER_LEN)
345    }
346
347    #[inline]
348    pub fn validate(buf: &[u8], offset: usize) -> Result<(), FieldError> {
349        if buf.len() < offset + ETHERNET_HEADER_LEN {
350            return Err(FieldError::BufferTooShort {
351                offset,
352                need: ETHERNET_HEADER_LEN,
353                have: buf.len().saturating_sub(offset),
354            });
355        }
356        Ok(())
357    }
358
359    // ========== Field Readers ==========
360    #[inline]
361    pub fn dst(&self, buf: &[u8]) -> Result<MacAddress, FieldError> {
362        MacAddress::read(buf, self.index.start + offsets::DST)
363    }
364
365    #[inline]
366    pub fn src(&self, buf: &[u8]) -> Result<MacAddress, FieldError> {
367        MacAddress::read(buf, self.index.start + offsets::SRC)
368    }
369
370    /// Length field (not `EtherType`!)
371    #[inline]
372    pub fn len_field(&self, buf: &[u8]) -> Result<u16, FieldError> {
373        u16::read(buf, self.index.start + offsets::TYPE)
374    }
375
376    // ========== Field Writers ==========
377    #[inline]
378    pub fn set_dst(&self, buf: &mut [u8], mac: MacAddress) -> Result<(), FieldError> {
379        mac.write(buf, self.index.start + offsets::DST)
380    }
381
382    #[inline]
383    pub fn set_src(&self, buf: &mut [u8], mac: MacAddress) -> Result<(), FieldError> {
384        mac.write(buf, self.index.start + offsets::SRC)
385    }
386
387    #[inline]
388    pub fn set_len(&self, buf: &mut [u8], len: u16) -> Result<(), FieldError> {
389        len.write(buf, self.index.start + offsets::TYPE)
390    }
391
392    /// Extract padding based on length field
393    #[must_use]
394    pub fn extract_padding<'a>(&self, buf: &'a [u8]) -> (&'a [u8], &'a [u8]) {
395        let len = self.len_field(buf).unwrap_or(0) as usize;
396        let payload_start = self.index.end;
397        let payload_end = (payload_start + len).min(buf.len());
398
399        (&buf[payload_start..payload_end], &buf[payload_end..])
400    }
401
402    /// Hash for packet matching
403    #[must_use]
404    pub fn hashret(&self, _buf: &[u8]) -> Vec<u8> {
405        // 802.3 hashret delegates to payload
406        vec![]
407    }
408
409    /// Check if this answers another packet
410    #[must_use]
411    pub fn answers(&self, _buf: &[u8], _other: &Dot3Layer, _other_buf: &[u8]) -> bool {
412        // Delegates to payload
413        true
414    }
415
416    #[must_use]
417    pub fn summary(&self, buf: &[u8]) -> String {
418        let dst = self.dst(buf).map_or_else(|_| "?".into(), |m| m.to_string());
419        let src = self.src(buf).map_or_else(|_| "?".into(), |m| m.to_string());
420        format!("802.3 {src} > {dst}")
421    }
422
423    // ========== Dynamic Field Access ==========
424    #[must_use]
425    pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
426        DOT3_FIELDS
427            .iter()
428            .find(|f| f.name == name)
429            .map(|desc| FieldValue::read(buf, &desc.with_offset(self.index.start)))
430    }
431
432    pub fn set_field(
433        &self,
434        buf: &mut [u8],
435        name: &str,
436        value: FieldValue,
437    ) -> Option<Result<(), FieldError>> {
438        DOT3_FIELDS
439            .iter()
440            .find(|f| f.name == name)
441            .map(|desc| value.write(buf, &desc.with_offset(self.index.start)))
442    }
443
444    #[must_use]
445    pub fn field_names() -> &'static [&'static str] {
446        &["dst", "src", "len"]
447    }
448}
449
450// ============================================================================
451// EthernetBuilder
452// ============================================================================
453
454#[derive(Debug, Clone)]
455pub struct EthernetBuilder {
456    dst: MacAddress,
457    src: MacAddress,
458    ethertype: u16,
459}
460
461impl Default for EthernetBuilder {
462    fn default() -> Self {
463        Self {
464            dst: MacAddress::BROADCAST,
465            src: MacAddress::ZERO,
466            ethertype: 0x9000, // Loopback (Scapy default)
467        }
468    }
469}
470
471impl EthernetBuilder {
472    #[must_use]
473    pub fn new() -> Self {
474        Self::default()
475    }
476
477    #[must_use]
478    pub fn dst(mut self, mac: MacAddress) -> Self {
479        self.dst = mac;
480        self
481    }
482    #[must_use]
483    pub fn src(mut self, mac: MacAddress) -> Self {
484        self.src = mac;
485        self
486    }
487    #[must_use]
488    pub fn ethertype(mut self, etype: u16) -> Self {
489        self.ethertype = etype;
490        self
491    }
492
493    #[must_use]
494    pub fn build(&self) -> Vec<u8> {
495        let mut buf = vec![0u8; ETHERNET_HEADER_LEN];
496        self.build_into(&mut buf)
497            .expect("buffer is correctly sized");
498        buf
499    }
500
501    pub fn build_into(&self, buf: &mut [u8]) -> Result<(), FieldError> {
502        let layer = EthernetLayer::at_start();
503        layer.set_dst(buf, self.dst)?;
504        layer.set_src(buf, self.src)?;
505        layer.set_ethertype(buf, self.ethertype)?;
506        Ok(())
507    }
508
509    #[must_use]
510    pub fn build_with_payload(&self, payload_kind: LayerKind) -> Vec<u8> {
511        let etype = match payload_kind {
512            LayerKind::Ipv4 => ethertype::IPV4,
513            LayerKind::Ipv6 => ethertype::IPV6,
514            LayerKind::Arp => ethertype::ARP,
515            _ => self.ethertype,
516        };
517
518        let mut buf = vec![0u8; ETHERNET_HEADER_LEN];
519        let layer = EthernetLayer::at_start();
520        layer.set_dst(&mut buf, self.dst).unwrap();
521        layer.set_src(&mut buf, self.src).unwrap();
522        layer.set_ethertype(&mut buf, etype).unwrap();
523        buf
524    }
525}
526
527// ============================================================================
528// Dot3Builder
529// ============================================================================
530
531#[derive(Debug, Clone)]
532pub struct Dot3Builder {
533    dst: MacAddress,
534    src: MacAddress,
535    len: Option<u16>, // Auto-calculated if None
536}
537
538impl Default for Dot3Builder {
539    fn default() -> Self {
540        Self {
541            dst: MacAddress::BROADCAST,
542            src: MacAddress::ZERO,
543            len: None,
544        }
545    }
546}
547
548impl Dot3Builder {
549    #[must_use]
550    pub fn new() -> Self {
551        Self::default()
552    }
553
554    #[must_use]
555    pub fn dst(mut self, mac: MacAddress) -> Self {
556        self.dst = mac;
557        self
558    }
559    #[must_use]
560    pub fn src(mut self, mac: MacAddress) -> Self {
561        self.src = mac;
562        self
563    }
564    #[must_use]
565    pub fn len(mut self, len: u16) -> Self {
566        self.len = Some(len);
567        self
568    }
569
570    #[must_use]
571    pub fn build(&self) -> Vec<u8> {
572        let mut buf = vec![0u8; ETHERNET_HEADER_LEN];
573        self.build_into(&mut buf)
574            .expect("buffer is correctly sized");
575        buf
576    }
577
578    pub fn build_into(&self, buf: &mut [u8]) -> Result<(), FieldError> {
579        let layer = Dot3Layer::at_start();
580        layer.set_dst(buf, self.dst)?;
581        layer.set_src(buf, self.src)?;
582        layer.set_len(buf, self.len.unwrap_or(0))?;
583        Ok(())
584    }
585
586    /// Build with auto-calculated length based on payload
587    #[must_use]
588    pub fn build_with_payload(&self, payload_len: usize) -> Vec<u8> {
589        let mut buf = vec![0u8; ETHERNET_HEADER_LEN];
590        let layer = Dot3Layer::at_start();
591        layer.set_dst(&mut buf, self.dst).unwrap();
592        layer.set_src(&mut buf, self.src).unwrap();
593        layer
594            .set_len(&mut buf, self.len.unwrap_or(payload_len as u16))
595            .unwrap();
596        buf
597    }
598}
599
600#[cfg(test)]
601mod tests {
602    use super::*;
603
604    fn sample_ethernet_frame() -> Vec<u8> {
605        vec![
606            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x08, 0x00,
607            0xde, 0xad, 0xbe, 0xef,
608        ]
609    }
610
611    fn sample_dot3_frame() -> Vec<u8> {
612        vec![
613            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x00,
614            0x04, // Length = 4 (< 1500)
615            0xde, 0xad, 0xbe, 0xef,
616        ]
617    }
618
619    #[test]
620    fn test_dispatch_hook() {
621        let eth2 = sample_ethernet_frame();
622        let dot3 = sample_dot3_frame();
623
624        assert_eq!(dispatch_hook(&eth2, 0), EthernetFrameType::EthernetII);
625        assert_eq!(dispatch_hook(&dot3, 0), EthernetFrameType::Dot3);
626
627        assert!(!is_dot3(&eth2, 0));
628        assert!(is_dot3(&dot3, 0));
629    }
630
631    #[test]
632    fn test_ethernet_hashret() {
633        let buf = sample_ethernet_frame();
634        let eth = EthernetLayer::at_start();
635        let hash = eth.hashret(&buf);
636
637        assert_eq!(hash, vec![0x08, 0x00]); // IPv4 EtherType
638    }
639
640    #[test]
641    fn test_ethernet_answers() {
642        let buf1 = sample_ethernet_frame();
643        let buf2 = sample_ethernet_frame();
644        let eth1 = EthernetLayer::at_start();
645        let eth2 = EthernetLayer::at_start();
646
647        assert!(eth1.answers(&buf1, &eth2, &buf2));
648    }
649
650    #[test]
651    fn test_dot3_layer() {
652        let buf = sample_dot3_frame();
653        let dot3 = Dot3Layer::at_start();
654
655        assert_eq!(dot3.len_field(&buf).unwrap(), 4);
656
657        let (payload, padding) = dot3.extract_padding(&buf);
658        assert_eq!(payload, &[0xde, 0xad, 0xbe, 0xef]);
659        assert!(padding.is_empty());
660    }
661
662    #[test]
663    fn test_dot3_with_padding() {
664        let mut buf = sample_dot3_frame();
665        buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); // Add padding
666
667        let dot3 = Dot3Layer::at_start();
668        let (payload, padding) = dot3.extract_padding(&buf);
669
670        assert_eq!(payload.len(), 4);
671        assert_eq!(padding.len(), 4);
672    }
673
674    #[test]
675    fn test_dot3_builder() {
676        let frame = Dot3Builder::new()
677            .dst(MacAddress::BROADCAST)
678            .src(MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]))
679            .len(100)
680            .build();
681
682        let dot3 = Dot3Layer::at_start();
683        assert_eq!(dot3.len_field(&frame).unwrap(), 100);
684    }
685
686    #[test]
687    fn test_ethertype_name() {
688        let buf = sample_ethernet_frame();
689        let eth = EthernetLayer::at_start();
690
691        assert_eq!(eth.ethertype_name(&buf), "IPv4");
692    }
693}