use core::sync::atomic::{AtomicBool, Ordering}; use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter}; use smoltcp::phy::{self, Device, DeviceCapabilities, Medium}; use smoltcp::time::Instant; static SLAAC_READY: AtomicBool = AtomicBool::new(false); static mut SLAAC_PREFIX: [u8; 16] = [0u8; 16]; static mut SLAAC_PREFIX_LEN: u8 = 0; static mut SLAAC_ROUTER: [u8; 16] = [0u8; 16]; pub struct SlaacInfo { pub prefix: [u8; 16], pub prefix_len: u8, pub router: [u8; 16], } pub fn take_slaac_prefix() -> Option { match SLAAC_READY.swap(false, Ordering::Acquire) { true => { let prefix = unsafe { (&raw const SLAAC_PREFIX).read() }; let len = unsafe { (&raw const SLAAC_PREFIX_LEN).read() }; let router = unsafe { (&raw const SLAAC_ROUTER).read() }; Some(SlaacInfo { prefix, prefix_len: len, router, }) } false => None, } } const ETHERTYPE_ARP: u16 = 0x0806; const ETHERTYPE_IPV6: u16 = 0x86DD; const IPV6_NEXT_ICMPV6: u8 = 58; const ICMPV6_ROUTER_ADVERT: u8 = 134; const NDOPT_PREFIX_INFO: u8 = 3; const PREFIX_FLAG_AUTONOMOUS: u8 = 0x40; const ARP_TABLE_SIZE: usize = 16; pub struct ArpEntry { pub ip: [u8; 4], pub mac: [u8; 6], pub timestamp_ms: i64, pub valid: bool, } impl ArpEntry { const fn empty() -> Self { Self { ip: [0; 4], mac: [0; 6], timestamp_ms: 0, valid: false, } } } static mut ARP_TABLE: [ArpEntry; ARP_TABLE_SIZE] = [const { ArpEntry::empty() }; ARP_TABLE_SIZE]; #[allow(clippy::deref_addrof)] fn inspect_for_arp(frame: &[u8], now_ms: i64) { if frame.len() < 42 { return; } let ethertype = ((frame[12] as u16) << 8) | frame[13] as u16; if ethertype != ETHERTYPE_ARP { return; } let sender_ip: [u8; 4] = [frame[28], frame[29], frame[30], frame[31]]; let sender_mac: [u8; 6] = [ frame[22], frame[23], frame[24], frame[25], frame[26], frame[27], ]; let table: &mut [ArpEntry; ARP_TABLE_SIZE] = unsafe { &mut *(&raw mut ARP_TABLE) }; let existing = (0..ARP_TABLE_SIZE).find(|&i| table[i].valid && table[i].ip == sender_ip); let slot = match existing { Some(i) => i, None => { let free = (0..ARP_TABLE_SIZE).find(|&i| !table[i].valid); match free { Some(i) => i, None => (0..ARP_TABLE_SIZE) .min_by_key(|&i| table[i].timestamp_ms) .unwrap_or(0), } } }; table[slot].ip = sender_ip; table[slot].mac = sender_mac; table[slot].timestamp_ms = now_ms; table[slot].valid = true; } #[allow(clippy::deref_addrof)] pub fn arp_table() -> &'static [ArpEntry; ARP_TABLE_SIZE] { unsafe { &*(&raw const ARP_TABLE) } } fn inspect_for_ra(frame: &[u8]) { if frame.len() < 70 { return; } let ethertype = ((frame[12] as u16) << 8) | frame[13] as u16; if ethertype != ETHERTYPE_IPV6 { return; } if frame[20] != IPV6_NEXT_ICMPV6 { return; } if frame[54] != ICMPV6_ROUTER_ADVERT { return; } let mut offset = 70usize; core::iter::from_fn(|| match offset + 2 <= frame.len() { false => None, true => { let opt_type = frame[offset]; let opt_len = frame[offset + 1] as usize * 8; match opt_len == 0 || offset + opt_len > frame.len() { true => None, false => { if opt_type == NDOPT_PREFIX_INFO && opt_len == 32 && frame[offset + 3] & PREFIX_FLAG_AUTONOMOUS != 0 { let prefix_len = frame[offset + 2]; let mut prefix = [0u8; 16]; prefix.copy_from_slice(&frame[offset + 16..offset + 32]); let mut router = [0u8; 16]; router.copy_from_slice(&frame[22..38]); unsafe { (&raw mut SLAAC_PREFIX).write(prefix); (&raw mut SLAAC_PREFIX_LEN).write(prefix_len); (&raw mut SLAAC_ROUTER).write(router); } SLAAC_READY.store(true, Ordering::Release); } offset += opt_len; Some(()) } } } }) .take(16) .count(); } pub struct PacketDevice { pub rx: PacketRingReader, pub tx: PacketRingWriter, } impl Device for PacketDevice { type RxToken<'a> = RxToken; type TxToken<'a> = TxToken<'a>; fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let mut buf = [0u8; 1514]; self.rx.try_pop(&mut buf).map(|len| { inspect_for_ra(&buf[..len]); inspect_for_arp(&buf[..len], timestamp.total_millis()); (RxToken { buf, len }, TxToken { tx: &self.tx }) }) } fn transmit(&mut self, _timestamp: Instant) -> Option> { match self.tx.has_space() { true => Some(TxToken { tx: &self.tx }), false => None, } } fn capabilities(&self) -> DeviceCapabilities { let mut caps = DeviceCapabilities::default(); caps.max_transmission_unit = 1514; caps.medium = Medium::Ethernet; caps.max_burst_size = None; caps } } pub struct RxToken { buf: [u8; 1514], len: usize, } impl phy::RxToken for RxToken { fn consume(self, f: F) -> R where F: FnOnce(&[u8]) -> R, { f(&self.buf[..self.len]) } } pub struct TxToken<'a> { tx: &'a PacketRingWriter, } impl<'a> phy::TxToken for TxToken<'a> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, { let mut buf = [0u8; 1514]; let actual = len.min(buf.len()); let result = f(&mut buf[..actual]); self.tx.try_push(&buf[..actual]); result } }