⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/net/
ethernet.rs

1//! Ethernet frame parsing and construction
2//!
3//! Implements IEEE 802.3 Ethernet frame handling for the network stack.
4//! Supports parsing incoming frames and constructing outgoing frames
5//! with proper MAC addressing and EtherType identification.
6
7#![allow(dead_code)] // Phase 6 network stack -- functions called as stack matures
8
9use alloc::vec::Vec;
10
11use crate::{error::KernelError, net::MacAddress};
12
13/// Ethernet frame header size: dst(6) + src(6) + ethertype(2) = 14 bytes
14pub const ETHERNET_HEADER_SIZE: usize = 14;
15
16/// Minimum Ethernet frame payload (excluding header)
17pub const ETHERNET_MIN_PAYLOAD: usize = 46;
18
19/// Maximum Ethernet frame payload (standard MTU)
20pub const ETHERNET_MAX_PAYLOAD: usize = 1500;
21
22/// EtherType constants
23pub const ETHERTYPE_IPV4: u16 = 0x0800;
24pub const ETHERTYPE_ARP: u16 = 0x0806;
25pub const ETHERTYPE_IPV6: u16 = 0x86DD;
26
27/// Parsed Ethernet frame
28#[derive(Debug, Clone)]
29pub struct EthernetFrame<'a> {
30    /// Destination MAC address
31    pub dst_mac: MacAddress,
32    /// Source MAC address
33    pub src_mac: MacAddress,
34    /// EtherType field
35    pub ethertype: u16,
36    /// Payload (reference to data after the header)
37    pub payload: &'a [u8],
38}
39
40/// Parse an Ethernet frame from raw bytes.
41///
42/// Returns an `EthernetFrame` with references into the original buffer
43/// for zero-copy payload access.
44pub fn parse_frame(data: &[u8]) -> Result<EthernetFrame<'_>, KernelError> {
45    if data.len() < ETHERNET_HEADER_SIZE {
46        return Err(KernelError::InvalidArgument {
47            name: "ethernet_frame",
48            value: "too_short",
49        });
50    }
51
52    let mut dst = [0u8; 6];
53    let mut src = [0u8; 6];
54    dst.copy_from_slice(&data[0..6]);
55    src.copy_from_slice(&data[6..12]);
56    let ethertype = u16::from_be_bytes([data[12], data[13]]);
57
58    Ok(EthernetFrame {
59        dst_mac: MacAddress(dst),
60        src_mac: MacAddress(src),
61        ethertype,
62        payload: &data[ETHERNET_HEADER_SIZE..],
63    })
64}
65
66/// Construct an Ethernet frame from components.
67///
68/// Builds a complete frame with header and payload, suitable for
69/// transmission via a network device.
70pub fn construct_frame(
71    dst: MacAddress,
72    src: MacAddress,
73    ethertype: u16,
74    payload: &[u8],
75) -> Vec<u8> {
76    let mut frame = Vec::with_capacity(ETHERNET_HEADER_SIZE + payload.len());
77
78    // Destination MAC
79    frame.extend_from_slice(&dst.0);
80    // Source MAC
81    frame.extend_from_slice(&src.0);
82    // EtherType
83    frame.extend_from_slice(&ethertype.to_be_bytes());
84    // Payload
85    frame.extend_from_slice(payload);
86
87    frame
88}
89
90/// Check if a MAC address is a broadcast address (FF:FF:FF:FF:FF:FF)
91pub fn is_broadcast(mac: &MacAddress) -> bool {
92    *mac == MacAddress::BROADCAST
93}
94
95/// Check if a MAC address is an IPv6 multicast address (33:33:xx:xx:xx:xx)
96pub fn is_ipv6_multicast(mac: &MacAddress) -> bool {
97    mac.0[0] == 0x33 && mac.0[1] == 0x33
98}
99
100/// Check if a MAC address matches or is broadcast or is IPv6 multicast
101pub fn is_for_us(frame_dst: &MacAddress, our_mac: &MacAddress) -> bool {
102    *frame_dst == *our_mac || is_broadcast(frame_dst) || is_ipv6_multicast(frame_dst)
103}
104
105/// Dispatch a received Ethernet frame to the appropriate protocol handler.
106///
107/// Routes frames to ARP or IP based on the EtherType field.
108pub fn dispatch_frame(data: &[u8], our_mac: &MacAddress) -> Result<(), KernelError> {
109    let frame = parse_frame(data)?;
110
111    // Drop frames not addressed to us
112    if !is_for_us(&frame.dst_mac, our_mac) {
113        return Ok(());
114    }
115
116    match frame.ethertype {
117        ETHERTYPE_ARP => {
118            super::arp::process_arp_packet(frame.payload, our_mac)?;
119        }
120        ETHERTYPE_IPV4 => {
121            // Parse IP header to get protocol and addresses, then dispatch
122            if frame.payload.len() >= super::ip::Ipv4Header::MIN_SIZE {
123                let ip_header = super::ip::Ipv4Header::from_bytes(frame.payload)?;
124                let header_len = (ip_header.ihl as usize) * 4;
125                if frame.payload.len() >= header_len {
126                    let ip_payload = &frame.payload[header_len..];
127                    let src = super::IpAddress::V4(ip_header.source);
128                    let dst = super::IpAddress::V4(ip_header.destination);
129
130                    match ip_header.protocol {
131                        6 => {
132                            // TCP
133                            let _ = super::tcp::process_packet(src, dst, ip_payload);
134                        }
135                        17 => {
136                            // UDP
137                            let _ = super::udp::process_packet(src, dst, ip_payload);
138                        }
139                        _ => {
140                            // Unknown protocol, drop
141                        }
142                    }
143                }
144            }
145        }
146        ETHERTYPE_IPV6 => {
147            // Parse and dispatch IPv6 packet
148            super::ipv6::process_packet(frame.payload)?;
149        }
150        _ => {
151            // Unknown EtherType, silently drop
152        }
153    }
154
155    Ok(())
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_construct_and_parse() {
164        let dst = MacAddress([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
165        let src = MacAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]);
166        let payload = b"Hello, Ethernet!";
167
168        let frame = construct_frame(dst, src, ETHERTYPE_IPV4, payload);
169        assert_eq!(frame.len(), ETHERNET_HEADER_SIZE + payload.len());
170
171        let parsed = parse_frame(&frame).unwrap();
172        assert_eq!(parsed.dst_mac, dst);
173        assert_eq!(parsed.src_mac, src);
174        assert_eq!(parsed.ethertype, ETHERTYPE_IPV4);
175        assert_eq!(parsed.payload, payload);
176    }
177
178    #[test]
179    fn test_parse_too_short() {
180        let short = [0u8; 10];
181        assert!(parse_frame(&short).is_err());
182    }
183
184    #[test]
185    fn test_is_broadcast() {
186        assert!(is_broadcast(&MacAddress::BROADCAST));
187        assert!(!is_broadcast(&MacAddress::ZERO));
188    }
189}