Skip to main content

arcbox_packet/ethernet/
mod.rs

1//! Ethernet frame parsing, construction, and ARP handling.
2//!
3//! Provides minimal L2 utilities for the custom network datapath:
4//! - Ethernet header parse/construct
5//! - ARP responder (gateway only)
6//! - UDP/IP/Ethernet packet builder for DHCP and DNS responses
7
8mod checksum;
9mod tcp;
10mod udp;
11
12#[cfg(test)]
13mod tests;
14
15pub use checksum::{ipv4_header_checksum, tcp_checksum};
16pub use tcp::{
17    SynAckParams, SynParams, TcpFrameParams, TcpSynOptions, build_tcp_ack_frame,
18    build_tcp_data_frame, build_tcp_data_frame_partial_csum, build_tcp_fin_frame,
19    build_tcp_rst_frame, build_tcp_syn_ack_frame, build_tcp_syn_frame, parse_tcp_syn_options,
20    tcp_pseudo_header_checksum,
21};
22pub use udp::{MAX_UDP_PAYLOAD, build_udp_ip_ethernet};
23
24use std::net::Ipv4Addr;
25
26/// Ethernet header size in bytes.
27pub const ETH_HEADER_LEN: usize = 14;
28
29/// Minimum frame size for an ARP packet (Ethernet + 28-byte ARP payload).
30const ARP_FRAME_MIN_LEN: usize = ETH_HEADER_LEN + 28;
31
32/// EtherType values.
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum EtherType {
35    Ipv4,
36    Arp,
37    Ipv6,
38    Unknown(u16),
39}
40
41impl EtherType {
42    /// Parses EtherType from raw two-byte big-endian value.
43    #[must_use]
44    pub fn from_raw(raw: u16) -> Self {
45        match raw {
46            0x0800 => Self::Ipv4,
47            0x0806 => Self::Arp,
48            0x86DD => Self::Ipv6,
49            other => Self::Unknown(other),
50        }
51    }
52
53    /// Returns the raw two-byte big-endian value.
54    #[must_use]
55    pub fn to_raw(self) -> u16 {
56        match self {
57            Self::Ipv4 => 0x0800,
58            Self::Arp => 0x0806,
59            Self::Ipv6 => 0x86DD,
60            Self::Unknown(v) => v,
61        }
62    }
63}
64
65/// Parsed Ethernet header fields.
66#[derive(Debug, Clone, Copy)]
67pub struct EthernetHeader {
68    pub dst_mac: [u8; 6],
69    pub src_mac: [u8; 6],
70    pub ethertype: EtherType,
71}
72
73impl EthernetHeader {
74    /// Parses an Ethernet header from the start of `data`.
75    ///
76    /// Returns `None` if the data is shorter than 14 bytes.
77    #[must_use]
78    pub fn parse(data: &[u8]) -> Option<Self> {
79        if data.len() < ETH_HEADER_LEN {
80            return None;
81        }
82        let mut dst_mac = [0u8; 6];
83        let mut src_mac = [0u8; 6];
84        dst_mac.copy_from_slice(&data[0..6]);
85        src_mac.copy_from_slice(&data[6..12]);
86        let raw_type = u16::from_be_bytes([data[12], data[13]]);
87        Some(Self {
88            dst_mac,
89            src_mac,
90            ethertype: EtherType::from_raw(raw_type),
91        })
92    }
93
94    /// Serialises the header into a 14-byte array.
95    #[must_use]
96    pub fn to_bytes(&self) -> [u8; ETH_HEADER_LEN] {
97        let mut buf = [0u8; ETH_HEADER_LEN];
98        buf[0..6].copy_from_slice(&self.dst_mac);
99        buf[6..12].copy_from_slice(&self.src_mac);
100        buf[12..14].copy_from_slice(&self.ethertype.to_raw().to_be_bytes());
101        buf
102    }
103}
104
105/// Returns the payload slice after the 14-byte Ethernet header.
106#[must_use]
107pub fn strip_ethernet_header(frame: &[u8]) -> &[u8] {
108    if frame.len() <= ETH_HEADER_LEN {
109        return &[];
110    }
111    &frame[ETH_HEADER_LEN..]
112}
113
114/// Prepends a 14-byte Ethernet header (IPv4 EtherType) to an IP packet.
115#[must_use]
116pub fn prepend_ethernet_header(ip_packet: &[u8], dst_mac: [u8; 6], src_mac: [u8; 6]) -> Vec<u8> {
117    let hdr = EthernetHeader {
118        dst_mac,
119        src_mac,
120        ethertype: EtherType::Ipv4,
121    };
122    let mut frame = Vec::with_capacity(ETH_HEADER_LEN + ip_packet.len());
123    frame.extend_from_slice(&hdr.to_bytes());
124    frame.extend_from_slice(ip_packet);
125    frame
126}
127
128/// Responds to ARP requests targeting the gateway IP.
129pub struct ArpResponder {
130    gateway_ip: Ipv4Addr,
131    gateway_mac: [u8; 6],
132}
133
134impl ArpResponder {
135    /// Creates a new ARP responder for the given gateway.
136    #[must_use]
137    pub fn new(gateway_ip: Ipv4Addr, gateway_mac: [u8; 6]) -> Self {
138        Self {
139            gateway_ip,
140            gateway_mac,
141        }
142    }
143
144    /// If `frame` is an ARP Request for the gateway IP, returns a complete
145    /// ARP Reply Ethernet frame. Otherwise returns `None`.
146    #[must_use]
147    pub fn handle_arp(&self, frame: &[u8]) -> Option<Vec<u8>> {
148        if frame.len() < ARP_FRAME_MIN_LEN {
149            return None;
150        }
151
152        let arp = &frame[ETH_HEADER_LEN..];
153
154        // Hardware type = Ethernet (1), Protocol type = IPv4 (0x0800)
155        if u16::from_be_bytes([arp[0], arp[1]]) != 1
156            || u16::from_be_bytes([arp[2], arp[3]]) != 0x0800
157        {
158            return None;
159        }
160
161        // HLEN = 6, PLEN = 4, Operation = Request (1)
162        if arp[4] != 6 || arp[5] != 4 || u16::from_be_bytes([arp[6], arp[7]]) != 1 {
163            return None;
164        }
165
166        // Target protocol address (bytes 24..28 of ARP payload)
167        let target_ip = Ipv4Addr::new(arp[24], arp[25], arp[26], arp[27]);
168        if target_ip != self.gateway_ip {
169            return None;
170        }
171
172        // Sender hardware address (bytes 8..14) and sender IP (14..18)
173        let mut sender_mac = [0u8; 6];
174        sender_mac.copy_from_slice(&arp[8..14]);
175        let sender_ip_bytes: [u8; 4] = [arp[14], arp[15], arp[16], arp[17]];
176
177        // Build ARP Reply
178        let mut reply = Vec::with_capacity(ARP_FRAME_MIN_LEN);
179
180        // Ethernet header: dst=sender, src=gateway, EtherType=ARP
181        reply.extend_from_slice(&sender_mac);
182        reply.extend_from_slice(&self.gateway_mac);
183        reply.extend_from_slice(&0x0806u16.to_be_bytes());
184
185        // ARP payload
186        reply.extend_from_slice(&1u16.to_be_bytes()); // Hardware type: Ethernet
187        reply.extend_from_slice(&0x0800u16.to_be_bytes()); // Protocol type: IPv4
188        reply.push(6); // HLEN
189        reply.push(4); // PLEN
190        reply.extend_from_slice(&2u16.to_be_bytes()); // Operation: Reply
191        reply.extend_from_slice(&self.gateway_mac); // Sender hardware addr
192        reply.extend_from_slice(&self.gateway_ip.octets()); // Sender protocol addr
193        reply.extend_from_slice(&sender_mac); // Target hardware addr
194        reply.extend_from_slice(&sender_ip_bytes); // Target protocol addr
195
196        Some(reply)
197    }
198}