1#![allow(dead_code)] use alloc::{collections::BTreeMap, vec::Vec};
9
10use spin::Mutex;
11
12use crate::{
13 error::KernelError,
14 net::{Ipv4Address, MacAddress},
15};
16
17const ARP_HTYPE_ETHERNET: u16 = 1;
19const ARP_PTYPE_IPV4: u16 = 0x0800;
21const ARP_OP_REQUEST: u16 = 1;
23const ARP_OP_REPLY: u16 = 2;
25const ARP_PACKET_SIZE: usize = 28;
27
28const ARP_CACHE_MAX: usize = 128;
30
31#[derive(Debug, Clone)]
33struct ArpEntry {
34 mac: MacAddress,
35 timestamp: u64,
37}
38
39static ARP_CACHE: Mutex<BTreeMap<Ipv4Address, ArpEntry>> = Mutex::new(BTreeMap::new());
41
42static ARP_TICK: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
44
45fn current_tick() -> u64 {
47 ARP_TICK.load(core::sync::atomic::Ordering::Relaxed)
48}
49
50pub fn tick() {
52 ARP_TICK.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
53}
54
55const ARP_ENTRY_MAX_AGE: u64 = 300;
58
59pub fn resolve(ip: Ipv4Address) -> Option<MacAddress> {
64 let cache = ARP_CACHE.lock();
65 if let Some(entry) = cache.get(&ip) {
66 let age = current_tick().wrapping_sub(entry.timestamp);
67 if age < ARP_ENTRY_MAX_AGE {
68 return Some(entry.mac);
69 }
70 }
71 None
72}
73
74pub fn update_cache(ip: Ipv4Address, mac: MacAddress) {
76 let mut cache = ARP_CACHE.lock();
77
78 if cache.len() >= ARP_CACHE_MAX && !cache.contains_key(&ip) {
80 let oldest_key = cache
82 .iter()
83 .min_by_key(|(_, e)| e.timestamp)
84 .map(|(k, _)| *k);
85 if let Some(key) = oldest_key {
86 cache.remove(&key);
87 }
88 }
89
90 cache.insert(
91 ip,
92 ArpEntry {
93 mac,
94 timestamp: current_tick(),
95 },
96 );
97}
98
99pub fn process_arp_packet(data: &[u8], our_mac: &MacAddress) -> Result<(), KernelError> {
104 if data.len() < ARP_PACKET_SIZE {
105 return Err(KernelError::InvalidArgument {
106 name: "arp_packet",
107 value: "too_short",
108 });
109 }
110
111 let htype = u16::from_be_bytes([data[0], data[1]]);
112 let ptype = u16::from_be_bytes([data[2], data[3]]);
113 let hlen = data[4];
114 let plen = data[5];
115 let operation = u16::from_be_bytes([data[6], data[7]]);
116
117 if htype != ARP_HTYPE_ETHERNET || ptype != ARP_PTYPE_IPV4 || hlen != 6 || plen != 4 {
119 return Err(KernelError::InvalidArgument {
120 name: "arp_format",
121 value: "unsupported",
122 });
123 }
124
125 let mut sender_mac_bytes = [0u8; 6];
127 sender_mac_bytes.copy_from_slice(&data[8..14]);
128 let sender_mac = MacAddress(sender_mac_bytes);
129 let sender_ip = Ipv4Address([data[14], data[15], data[16], data[17]]);
130
131 let target_ip = Ipv4Address([data[24], data[25], data[26], data[27]]);
132
133 update_cache(sender_ip, sender_mac);
135
136 match operation {
137 ARP_OP_REQUEST => {
138 let our_ip = get_interface_ip();
140 if target_ip == our_ip {
141 let reply = build_arp_reply(*our_mac, our_ip, sender_mac, sender_ip);
143 send_arp_frame(&reply, *our_mac, sender_mac);
144 }
145 }
146 ARP_OP_REPLY => {
147 println!(
149 "[ARP] Learned {}.{}.{}.{} -> {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
150 sender_ip.0[0],
151 sender_ip.0[1],
152 sender_ip.0[2],
153 sender_ip.0[3],
154 sender_mac.0[0],
155 sender_mac.0[1],
156 sender_mac.0[2],
157 sender_mac.0[3],
158 sender_mac.0[4],
159 sender_mac.0[5],
160 );
161 }
162 _ => {
163 }
165 }
166
167 Ok(())
168}
169
170pub fn send_arp_request(target_ip: Ipv4Address) {
174 let our_mac = get_interface_mac();
175 let our_ip = get_interface_ip();
176
177 let packet = build_arp_request(our_mac, our_ip, target_ip);
178 send_arp_frame(&packet, our_mac, MacAddress::BROADCAST);
179}
180
181fn build_arp_request(
183 sender_mac: MacAddress,
184 sender_ip: Ipv4Address,
185 target_ip: Ipv4Address,
186) -> Vec<u8> {
187 let mut pkt = Vec::with_capacity(ARP_PACKET_SIZE);
188
189 pkt.extend_from_slice(&ARP_HTYPE_ETHERNET.to_be_bytes());
191 pkt.extend_from_slice(&ARP_PTYPE_IPV4.to_be_bytes());
193 pkt.push(6);
195 pkt.push(4);
197 pkt.extend_from_slice(&ARP_OP_REQUEST.to_be_bytes());
199 pkt.extend_from_slice(&sender_mac.0);
201 pkt.extend_from_slice(&sender_ip.0);
203 pkt.extend_from_slice(&[0u8; 6]);
205 pkt.extend_from_slice(&target_ip.0);
207
208 pkt
209}
210
211fn build_arp_reply(
213 sender_mac: MacAddress,
214 sender_ip: Ipv4Address,
215 target_mac: MacAddress,
216 target_ip: Ipv4Address,
217) -> Vec<u8> {
218 let mut pkt = Vec::with_capacity(ARP_PACKET_SIZE);
219
220 pkt.extend_from_slice(&ARP_HTYPE_ETHERNET.to_be_bytes());
221 pkt.extend_from_slice(&ARP_PTYPE_IPV4.to_be_bytes());
222 pkt.push(6);
223 pkt.push(4);
224 pkt.extend_from_slice(&ARP_OP_REPLY.to_be_bytes());
225 pkt.extend_from_slice(&sender_mac.0);
226 pkt.extend_from_slice(&sender_ip.0);
227 pkt.extend_from_slice(&target_mac.0);
228 pkt.extend_from_slice(&target_ip.0);
229
230 pkt
231}
232
233fn send_arp_frame(arp_data: &[u8], src_mac: MacAddress, dst_mac: MacAddress) {
235 let frame = super::ethernet::construct_frame(
236 dst_mac,
237 src_mac,
238 super::ethernet::ETHERTYPE_ARP,
239 arp_data,
240 );
241
242 let _pkt = super::Packet::from_bytes(&frame);
244
245 super::device::with_device_mut("eth0", |dev| {
247 let _ = dev.transmit(&_pkt);
248 });
249}
250
251fn get_interface_ip() -> Ipv4Address {
256 super::ip::get_interface_ip()
257}
258
259fn get_interface_mac() -> MacAddress {
261 super::device::with_device("eth0", |dev| dev.mac_address()).unwrap_or(MacAddress::ZERO)
262}
263
264pub fn get_cache_entries() -> Vec<(Ipv4Address, MacAddress)> {
266 let cache = ARP_CACHE.lock();
267 let now = current_tick();
268 cache
269 .iter()
270 .filter(|(_, entry)| now.wrapping_sub(entry.timestamp) < ARP_ENTRY_MAX_AGE)
271 .map(|(ip, entry)| (*ip, entry.mac))
272 .collect()
273}
274
275pub fn flush_cache() {
277 let mut cache = ARP_CACHE.lock();
278 cache.clear();
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn test_arp_cache_insert_and_resolve() {
287 let ip = Ipv4Address::new(10, 0, 0, 1);
288 let mac = MacAddress([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
289
290 update_cache(ip, mac);
291 assert_eq!(resolve(ip), Some(mac));
292 }
293
294 #[test]
295 fn test_arp_request_build() {
296 let sender_mac = MacAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]);
297 let sender_ip = Ipv4Address::new(10, 0, 2, 15);
298 let target_ip = Ipv4Address::new(10, 0, 2, 1);
299
300 let pkt = build_arp_request(sender_mac, sender_ip, target_ip);
301 assert_eq!(pkt.len(), ARP_PACKET_SIZE);
302
303 assert_eq!(u16::from_be_bytes([pkt[6], pkt[7]]), ARP_OP_REQUEST);
305 }
306}