1use alloc::string::String;
2use alloc::vec::Vec;
3
4pub fn arp_cache_lines() -> Vec<String> {
5 let mut lines: Vec<String> = Vec::new();
6
7 unsafe {
8 for i in 0..super::ARP_CACHE_SIZE {
9 let e = super::ARP_CACHE[i];
10 if !e.valid {
11 continue;
12 }
13 lines.push(alloc::format!(
14 "{} -> {} (age={})",
15 super::format_ipv4(e.ip),
16 super::format_mac(e.mac),
17 e.age
18 ));
19 }
20 }
21
22 lines
23}
24
25pub fn build_arp_request(target_ip: [u8; 4], out: &mut [u8]) -> Result<usize, &'static str> {
26 if out.len() < 42 {
27 return Err("buffer too small");
28 }
29
30 let st = super::status();
31
32 out[0..6].copy_from_slice(&[0xFF; 6]);
33 out[6..12].copy_from_slice(&st.mac);
34 out[12] = 0x08;
35 out[13] = 0x06;
36
37 out[14] = 0x00;
38 out[15] = 0x01;
39 out[16] = 0x08;
40 out[17] = 0x00;
41 out[18] = 0x06;
42 out[19] = 0x04;
43 out[20] = 0x00;
44 out[21] = 0x01;
45
46 out[22..28].copy_from_slice(&st.mac);
47 out[28..32].copy_from_slice(&st.ip);
48 out[32..38].copy_from_slice(&[0x00; 6]);
49 out[38..42].copy_from_slice(&target_ip);
50
51 unsafe {
52 super::NET_STATE.tx_packets = super::NET_STATE.tx_packets.saturating_add(1);
53 }
54
55 Ok(42)
56}
57
58
59
60pub fn ingest_ethernet_frame(frame: &[u8]) -> Result<(), &'static str> {
61 unsafe {
64 super::NET_STATE.raw_rx_frames = super::NET_STATE.raw_rx_frames.saturating_add(1);
65 }
66
67 use super::eth_frame::{dispatch, Dispatch, FrameError};
75 let d = dispatch(frame);
78 unsafe {
79 match &d {
80 Ok(Dispatch::Arp) => ETH_CNT_ARP += 1,
81 Ok(Dispatch::Ipv4 { .. }) => ETH_CNT_IPV4 += 1,
82 Ok(Dispatch::Unsupported { .. }) => ETH_CNT_OTHER += 1,
83 Err(_) => ETH_CNT_ERR += 1,
84 }
85 }
86 match d {
87 Ok(Dispatch::Arp) => {
88 parse_arp_packet(frame)?;
89 Ok(())
90 }
91 Ok(Dispatch::Ipv4 { start, end }) => {
92 let payload = frame.get(start..end).ok_or("ipv4 payload out of range")?;
93 parse_ipv4_packet(payload)?;
94 Ok(())
95 }
96 Ok(Dispatch::Unsupported { .. }) => Err("unsupported ethertype"),
97 Err(FrameError::TooShort { .. }) => Err("ethernet frame too short"),
98 Err(FrameError::TruncatedVlan { .. }) => Err("truncated vlan tag"),
99 }
100}
101
102pub static mut ETH_CNT_IPV4: u32 = 0;
104pub static mut ETH_CNT_ARP: u32 = 0;
105pub static mut ETH_CNT_OTHER: u32 = 0;
106pub static mut ETH_CNT_ERR: u32 = 0;
107
108pub fn eth_dispatch_counts() -> (u32, u32, u32, u32) {
110 unsafe { (ETH_CNT_IPV4, ETH_CNT_ARP, ETH_CNT_OTHER, ETH_CNT_ERR) }
111}
112
113pub fn ingest_nic_ethernet_frame(frame: &[u8]) -> Result<(), &'static str> {
114 match super::status().nic_backend {
115 super::NicBackend::SoftwareLoopback => ingest_ethernet_frame(frame),
116 super::NicBackend::UsbEthernet => ingest_ethernet_frame(frame),
117 }
118}
119
120pub fn ingest_nic_ipv4_packet(packet: &[u8]) -> Result<(), &'static str> {
121 match super::status().nic_backend {
122 super::NicBackend::SoftwareLoopback => parse_ipv4_packet(packet),
123 super::NicBackend::UsbEthernet => parse_ipv4_packet(packet),
124 }
125}
126
127pub fn nic_pop_tx_ipv4_packet(out: &mut [u8]) -> Option<usize> {
128 nic_take_tx_ipv4_chunk(out.len(), out)
129}
130
131pub fn nic_take_tx_ipv4_chunk(max_len: usize, out: &mut [u8]) -> Option<usize> {
132 if max_len == 0 || out.is_empty() {
133 return None;
134 }
135
136 unsafe {
137 let mut best_idx: Option<usize> = None;
138 let mut best_age: u32 = 0;
139
140 for i in 0..super::NIC_TX_QUEUE_SIZE {
141 let e = &super::NIC_TX_QUEUE[i];
153 if !e.valid {
154 continue;
155 }
156 if best_idx.is_none() || e.age < best_age {
157 best_idx = Some(i);
158 best_age = e.age;
159 }
160 }
161
162 let idx = best_idx?;
163 let entry = &mut super::NIC_TX_QUEUE[idx];
164 if entry.len == 0 || entry.offset >= entry.len {
165 entry.valid = false;
171 entry.len = 0;
172 entry.offset = 0;
173 return None;
174 }
175
176 let remain = entry.len - entry.offset;
177 let chunk_len = remain.min(max_len).min(out.len());
178 let start = entry.offset;
179 let end = start + chunk_len;
180 out[..chunk_len].copy_from_slice(&entry.payload[start..end]);
181 entry.offset = end;
182
183 if entry.offset >= entry.len {
184 entry.valid = false;
186 entry.len = 0;
187 entry.offset = 0;
188 }
189
190 Some(chunk_len)
191 }
192}
193
194pub fn transmit_nic_ipv4_packet(packet: &[u8]) -> Result<usize, &'static str> {
195 if packet.is_empty() {
196 return Err("empty packet");
197 }
198 if packet.len() > super::NIC_TX_MAX_PAYLOAD {
199 return Err("packet too large");
200 }
201
202 match super::status().nic_backend {
203 super::NicBackend::SoftwareLoopback => {
204 unsafe {
205 super::NET_STATE.tx_packets = super::NET_STATE.tx_packets.saturating_add(1);
206 }
207 Ok(packet.len())
208 }
209 super::NicBackend::UsbEthernet => {
210 unsafe {
224 let mut slot: Option<usize> = None;
225 for i in 0..super::NIC_TX_QUEUE_SIZE {
226 if !super::NIC_TX_QUEUE[i].valid {
227 slot = Some(i);
228 break;
229 }
230 }
231
232 let idx = match slot {
233 Some(v) => v,
234 None => {
235 super::NIC_TX_DROPS = super::NIC_TX_DROPS.saturating_add(1);
241 if super::NIC_TX_DROPS <= 4 || super::NIC_TX_DROPS.is_multiple_of(16) {
242 crate::warn!(
243 "[NET][DIAG] nic tx queue FULL (size={}) dropped_total={}",
244 super::NIC_TX_QUEUE_SIZE,
245 super::NIC_TX_DROPS
246 );
247 }
248 return Err("nic tx queue full");
249 }
250 };
251
252 let entry = &mut super::NIC_TX_QUEUE[idx];
256 let frame_len = build_usb_ethernet_ipv4_frame(packet, &mut entry.payload)?;
257 entry.valid = true;
258 entry.len = frame_len;
259 entry.offset = 0;
260 entry.age = super::NIC_TX_TICK;
261 super::NIC_TX_TICK = super::NIC_TX_TICK.saturating_add(1);
262
263 super::NET_STATE.tx_packets = super::NET_STATE.tx_packets.saturating_add(1);
264 Ok(frame_len)
265 }
266 }
267 }
268}
269
270pub fn transmit_nic_ethernet_frame(frame: &[u8]) -> Result<usize, &'static str> {
271 if frame.is_empty() {
272 return Err("empty frame");
273 }
274 if frame.len() > super::NIC_TX_MAX_PAYLOAD {
275 return Err("frame too large");
276 }
277
278 match super::status().nic_backend {
279 super::NicBackend::SoftwareLoopback => {
280 unsafe {
281 super::NET_STATE.tx_packets = super::NET_STATE.tx_packets.saturating_add(1);
282 }
283 Ok(frame.len())
284 }
285 super::NicBackend::UsbEthernet => {
286 unsafe {
287 let mut slot: Option<usize> = None;
288 for i in 0..super::NIC_TX_QUEUE_SIZE {
289 if !super::NIC_TX_QUEUE[i].valid {
290 slot = Some(i);
291 break;
292 }
293 }
294
295 let idx = match slot {
296 Some(v) => v,
297 None => {
298 super::NIC_TX_DROPS = super::NIC_TX_DROPS.saturating_add(1);
304 if super::NIC_TX_DROPS <= 4 || super::NIC_TX_DROPS.is_multiple_of(16) {
305 crate::warn!(
306 "[NET][DIAG] nic tx queue FULL (size={}) dropped_total={}",
307 super::NIC_TX_QUEUE_SIZE,
308 super::NIC_TX_DROPS
309 );
310 }
311 return Err("nic tx queue full");
312 }
313 };
314
315 let mut entry = super::NicTxEntry::empty();
316 entry.valid = true;
317 entry.len = frame.len();
318 entry.offset = 0;
319 entry.payload[..frame.len()].copy_from_slice(frame);
320 entry.age = super::NIC_TX_TICK;
321 super::NIC_TX_TICK = super::NIC_TX_TICK.saturating_add(1);
322 super::NIC_TX_QUEUE[idx] = entry;
323
324 super::NET_STATE.tx_packets = super::NET_STATE.tx_packets.saturating_add(1);
325 }
326 Ok(frame.len())
327 }
328 }
329}
330
331pub fn get_next_hop_ip(dst_ip: [u8; 4]) -> [u8; 4] {
332 let st = super::status();
333 if dst_ip == [255, 255, 255, 255] {
334 return dst_ip;
335 }
336 let mask = if st.dhcp_subnet == [0; 4] {
337 [255, 255, 255, 0]
338 } else {
339 st.dhcp_subnet
340 };
341 let is_local = (dst_ip[0] & mask[0] == st.ip[0] & mask[0])
342 && (dst_ip[1] & mask[1] == st.ip[1] & mask[1])
343 && (dst_ip[2] & mask[2] == st.ip[2] & mask[2])
344 && (dst_ip[3] & mask[3] == st.ip[3] & mask[3]);
345
346 if is_local {
347 dst_ip
348 } else if st.gateway != [0; 4] {
349 st.gateway
350 } else {
351 dst_ip
352 }
353}
354
355fn build_usb_ethernet_ipv4_frame(packet: &[u8], out: &mut [u8]) -> Result<usize, &'static str> {
356 if packet.len() < 20 {
357 return Err("ipv4 packet too short");
358 }
359
360 let total_len = (((packet[2] as u16) << 8) | packet[3] as u16) as usize;
361 if total_len < 20 || total_len > packet.len() {
362 return Err("invalid ipv4 total length");
363 }
364
365 let dst_ip = [packet[16], packet[17], packet[18], packet[19]];
366 let src_ip = [packet[12], packet[13], packet[14], packet[15]];
367
368 let next_hop = get_next_hop_ip(dst_ip);
369
370 let dst_mac = if dst_ip == [255, 255, 255, 255] {
371 [0xFF; 6]
372 } else if let Some(mac) = arp_cache_lookup(next_hop) {
373 mac
374 } else if src_ip == [0, 0, 0, 0] {
375 [0xFF; 6]
376 } else if unsafe {
377 let now = crate::kernel::timer::get_system_time_ms();
378 super::ARP_NEG_IP == next_hop && now < super::ARP_NEG_UNTIL_MS
379 } {
380 crate::debug!(
383 "ARP: skipping resolve for {} (recently failed, negative-cached)",
384 super::format_ipv4(next_hop)
385 );
386 return Err("destination mac unresolved (recently failed)");
387 } else {
388 crate::warn!("[NET] ARP: resolving {}", super::format_ipv4(next_hop));
389 let mut resolved_mac = None;
390 'outer: for attempt in 0..4u32 {
392 let mut arp_req = [0u8; 42];
393 match build_arp_request(next_hop, &mut arp_req) {
396 Ok(arp_len) => {
397 if let Err(e) = transmit_nic_ethernet_frame(&arp_req[..arp_len]) {
398 crate::warn!(
399 "[NET] ARP: request transmit failed for {}: {}",
400 super::format_ipv4(next_hop),
401 e
402 );
403 }
404 }
405 Err(e) => {
406 crate::warn!(
407 "[NET] ARP: failed to build request for {}: {}",
408 super::format_ipv4(next_hop),
409 e
410 );
411 }
412 }
413 let start = crate::kernel::timer::get_system_time_ms();
414 while crate::kernel::timer::get_system_time_ms().wrapping_sub(start) < 500 {
415 crate::kernel::usb::poll_ethernet_data_plane_only();
416 if let Some(mac) = arp_cache_lookup(next_hop) {
417 resolved_mac = Some(mac);
418 break 'outer;
419 }
420 crate::kernel::scheduler::yield_now();
423 }
424 crate::warn!(
425 "ARP: no reply for {} (attempt {})",
426 super::format_ipv4(next_hop),
427 attempt + 1
428 );
429 }
430
431 if let Some(mac) = resolved_mac {
432 crate::warn!(
433 "ARP: resolved {} -> {}",
434 super::format_ipv4(next_hop),
435 super::format_mac(mac)
436 );
437 mac
438 } else {
439 unsafe {
441 super::ARP_NEG_IP = next_hop;
442 super::ARP_NEG_UNTIL_MS = crate::kernel::timer::get_system_time_ms() + 3000;
443 }
444 return Err("destination mac unresolved");
445 }
446 };
447
448 let frame_len = 14usize + total_len;
449 if frame_len > out.len() {
450 return Err("ethernet frame too large");
451 }
452
453 let st = super::status();
454 out[0..6].copy_from_slice(&dst_mac);
455 out[6..12].copy_from_slice(&st.mac);
456 out[12] = 0x08;
457 out[13] = 0x00;
458 out[14..14 + total_len].copy_from_slice(&packet[..total_len]);
459 Ok(frame_len)
460}
461
462pub(crate) fn arp_cache_lookup(ip: [u8; 4]) -> Option<[u8; 6]> {
463 unsafe {
464 let table = &*core::ptr::addr_of!(super::ARP_CACHE);
465 super::arp_cache::lookup(table, ip)
466 }
467}
468
469fn arp_cache_upsert(ip: [u8; 4], mac: [u8; 6]) {
474 unsafe {
475 let age = super::ARP_TICK;
476 super::ARP_TICK = super::ARP_TICK.saturating_add(1);
477
478 let table = &mut *core::ptr::addr_of_mut!(super::ARP_CACHE);
479 let outcome = super::arp_cache::upsert(table, ip, mac, age);
480
481 if matches!(
482 outcome,
483 super::arp_cache::UpsertOutcome::Inserted | super::arp_cache::UpsertOutcome::Evicted
484 ) {
485 crate::warn!(
486 "ARP: learned {} -> {}",
487 super::format_ipv4(ip),
488 super::format_mac(mac)
489 );
490 }
491 }
492}
493
494fn parse_arp_packet(frame: &[u8]) -> Result<(), &'static str> {
495 if frame.len() < 42 {
496 return Err("arp frame too short");
497 }
498
499 let htype = ((frame[14] as u16) << 8) | frame[15] as u16;
500 let ptype = ((frame[16] as u16) << 8) | frame[17] as u16;
501 let hlen = frame[18];
502 let plen = frame[19];
503 let oper = ((frame[20] as u16) << 8) | frame[21] as u16;
504
505 if htype != 1 || ptype != 0x0800 || hlen != 6 || plen != 4 {
506 return Err("unsupported arp format");
507 }
508 if oper != 1 && oper != 2 {
509 return Err("unsupported arp operation");
510 }
511
512 let sender_mac = [
513 frame[22], frame[23], frame[24], frame[25], frame[26], frame[27],
514 ];
515 let sender_ip = [frame[28], frame[29], frame[30], frame[31]];
516 let target_ip = [frame[38], frame[39], frame[40], frame[41]];
517
518 arp_cache_upsert(sender_ip, sender_mac);
519
520 unsafe {
521 super::NET_STATE.rx_packets = super::NET_STATE.rx_packets.saturating_add(1);
522
523 if oper == 1 && target_ip == super::NET_STATE.ip {
524 let mut reply = [0u8; 42];
525
526 reply[0..6].copy_from_slice(&sender_mac);
527 reply[6..12].copy_from_slice(&super::NET_STATE.mac);
528 reply[12] = 0x08;
529 reply[13] = 0x06;
530
531 reply[14] = 0x00;
532 reply[15] = 0x01;
533 reply[16] = 0x08;
534 reply[17] = 0x00;
535 reply[18] = 6;
536 reply[19] = 4;
537 reply[20] = 0x00;
538 reply[21] = 0x02;
539
540 reply[22..28].copy_from_slice(&super::NET_STATE.mac);
541 reply[28..32].copy_from_slice(&super::NET_STATE.ip);
542
543 reply[32..38].copy_from_slice(&sender_mac);
544 reply[38..42].copy_from_slice(&sender_ip);
545
546 if let Err(e) = transmit_nic_ethernet_frame(&reply) {
549 crate::warn!(
550 "[NET] ARP: reply transmit failed to {}: {}",
551 super::format_ipv4(sender_ip),
552 e
553 );
554 }
555 }
556 }
557
558 Ok(())
559}
560
561fn parse_ipv4_packet(packet: &[u8]) -> Result<(), &'static str> {
562 if packet.len() < 20 {
563 return Err("ipv4 packet too short");
564 }
565
566 let version = packet[0] >> 4;
567 if version != 4 {
568 return Err("invalid ipv4 version");
569 }
570
571 let ihl = ((packet[0] & 0x0F) as usize) * 4;
572 if ihl < 20 || packet.len() < ihl {
573 return Err("invalid ipv4 header length");
574 }
575
576 let total_len = (((packet[2] as u16) << 8) | packet[3] as u16) as usize;
577 if total_len < ihl || total_len > packet.len() {
578 return Err("invalid ipv4 total length");
579 }
580
581 match packet[9] {
582 1 => {
583 let src_ip = [packet[12], packet[13], packet[14], packet[15]];
587 match super::icmp::parse_icmp_echo_reply_ipv4(packet) {
588 Ok(reply) => {
589 crate::warn!(
590 "ICMP: echo reply accepted from {} (id={:#06X} seq={})",
591 super::format_ipv4(src_ip),
592 reply.identifier,
593 reply.sequence
594 );
595 unsafe {
596 super::LAST_PING_REPLY = Some(reply);
597 }
598 }
599 Err(e) => {
600 crate::warn!(
601 "ICMP: packet from {} rejected: {}",
602 super::format_ipv4(src_ip),
603 e
604 );
605 }
606 }
607 Ok(())
608 }
609 17 => super::udp::parse_udp_ipv4_packet(packet),
610 6 => super::tcp::parse_tcp_ipv4_packet(packet),
611 _ => Err("unsupported ipv4 protocol"),
612 }
613}
614
615