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