Nothing to see here, move along
at main 216 lines 6.2 kB view raw
1use core::sync::atomic::{AtomicBool, Ordering}; 2use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter}; 3use smoltcp::phy::{self, Device, DeviceCapabilities, Medium}; 4use smoltcp::time::Instant; 5 6static SLAAC_READY: AtomicBool = AtomicBool::new(false); 7static mut SLAAC_PREFIX: [u8; 16] = [0u8; 16]; 8static mut SLAAC_PREFIX_LEN: u8 = 0; 9static mut SLAAC_ROUTER: [u8; 16] = [0u8; 16]; 10 11pub struct SlaacInfo { 12 pub prefix: [u8; 16], 13 pub prefix_len: u8, 14 pub router: [u8; 16], 15} 16 17pub fn take_slaac_prefix() -> Option<SlaacInfo> { 18 match SLAAC_READY.swap(false, Ordering::Acquire) { 19 true => { 20 let prefix = unsafe { (&raw const SLAAC_PREFIX).read() }; 21 let len = unsafe { (&raw const SLAAC_PREFIX_LEN).read() }; 22 let router = unsafe { (&raw const SLAAC_ROUTER).read() }; 23 Some(SlaacInfo { 24 prefix, 25 prefix_len: len, 26 router, 27 }) 28 } 29 false => None, 30 } 31} 32 33const ETHERTYPE_ARP: u16 = 0x0806; 34const ETHERTYPE_IPV6: u16 = 0x86DD; 35const IPV6_NEXT_ICMPV6: u8 = 58; 36const ICMPV6_ROUTER_ADVERT: u8 = 134; 37const NDOPT_PREFIX_INFO: u8 = 3; 38const PREFIX_FLAG_AUTONOMOUS: u8 = 0x40; 39 40const ARP_TABLE_SIZE: usize = 16; 41 42pub struct ArpEntry { 43 pub ip: [u8; 4], 44 pub mac: [u8; 6], 45 pub timestamp_ms: i64, 46 pub valid: bool, 47} 48 49impl ArpEntry { 50 const fn empty() -> Self { 51 Self { 52 ip: [0; 4], 53 mac: [0; 6], 54 timestamp_ms: 0, 55 valid: false, 56 } 57 } 58} 59 60static mut ARP_TABLE: [ArpEntry; ARP_TABLE_SIZE] = [const { ArpEntry::empty() }; ARP_TABLE_SIZE]; 61 62#[allow(clippy::deref_addrof)] 63fn inspect_for_arp(frame: &[u8], now_ms: i64) { 64 if frame.len() < 42 { 65 return; 66 } 67 let ethertype = ((frame[12] as u16) << 8) | frame[13] as u16; 68 if ethertype != ETHERTYPE_ARP { 69 return; 70 } 71 let sender_ip: [u8; 4] = [frame[28], frame[29], frame[30], frame[31]]; 72 let sender_mac: [u8; 6] = [ 73 frame[22], frame[23], frame[24], frame[25], frame[26], frame[27], 74 ]; 75 76 let table: &mut [ArpEntry; ARP_TABLE_SIZE] = unsafe { &mut *(&raw mut ARP_TABLE) }; 77 78 let existing = (0..ARP_TABLE_SIZE).find(|&i| table[i].valid && table[i].ip == sender_ip); 79 let slot = match existing { 80 Some(i) => i, 81 None => { 82 let free = (0..ARP_TABLE_SIZE).find(|&i| !table[i].valid); 83 match free { 84 Some(i) => i, 85 None => (0..ARP_TABLE_SIZE) 86 .min_by_key(|&i| table[i].timestamp_ms) 87 .unwrap_or(0), 88 } 89 } 90 }; 91 92 table[slot].ip = sender_ip; 93 table[slot].mac = sender_mac; 94 table[slot].timestamp_ms = now_ms; 95 table[slot].valid = true; 96} 97 98#[allow(clippy::deref_addrof)] 99pub fn arp_table() -> &'static [ArpEntry; ARP_TABLE_SIZE] { 100 unsafe { &*(&raw const ARP_TABLE) } 101} 102 103fn inspect_for_ra(frame: &[u8]) { 104 if frame.len() < 70 { 105 return; 106 } 107 let ethertype = ((frame[12] as u16) << 8) | frame[13] as u16; 108 if ethertype != ETHERTYPE_IPV6 { 109 return; 110 } 111 if frame[20] != IPV6_NEXT_ICMPV6 { 112 return; 113 } 114 if frame[54] != ICMPV6_ROUTER_ADVERT { 115 return; 116 } 117 118 let mut offset = 70usize; 119 core::iter::from_fn(|| match offset + 2 <= frame.len() { 120 false => None, 121 true => { 122 let opt_type = frame[offset]; 123 let opt_len = frame[offset + 1] as usize * 8; 124 match opt_len == 0 || offset + opt_len > frame.len() { 125 true => None, 126 false => { 127 if opt_type == NDOPT_PREFIX_INFO 128 && opt_len == 32 129 && frame[offset + 3] & PREFIX_FLAG_AUTONOMOUS != 0 130 { 131 let prefix_len = frame[offset + 2]; 132 let mut prefix = [0u8; 16]; 133 prefix.copy_from_slice(&frame[offset + 16..offset + 32]); 134 let mut router = [0u8; 16]; 135 router.copy_from_slice(&frame[22..38]); 136 unsafe { 137 (&raw mut SLAAC_PREFIX).write(prefix); 138 (&raw mut SLAAC_PREFIX_LEN).write(prefix_len); 139 (&raw mut SLAAC_ROUTER).write(router); 140 } 141 SLAAC_READY.store(true, Ordering::Release); 142 } 143 offset += opt_len; 144 Some(()) 145 } 146 } 147 } 148 }) 149 .take(16) 150 .count(); 151} 152 153pub struct PacketDevice { 154 pub rx: PacketRingReader, 155 pub tx: PacketRingWriter, 156} 157 158impl Device for PacketDevice { 159 type RxToken<'a> = RxToken; 160 type TxToken<'a> = TxToken<'a>; 161 162 fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 163 let mut buf = [0u8; 1514]; 164 self.rx.try_pop(&mut buf).map(|len| { 165 inspect_for_ra(&buf[..len]); 166 inspect_for_arp(&buf[..len], timestamp.total_millis()); 167 (RxToken { buf, len }, TxToken { tx: &self.tx }) 168 }) 169 } 170 171 fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> { 172 match self.tx.has_space() { 173 true => Some(TxToken { tx: &self.tx }), 174 false => None, 175 } 176 } 177 178 fn capabilities(&self) -> DeviceCapabilities { 179 let mut caps = DeviceCapabilities::default(); 180 caps.max_transmission_unit = 1514; 181 caps.medium = Medium::Ethernet; 182 caps.max_burst_size = None; 183 caps 184 } 185} 186 187pub struct RxToken { 188 buf: [u8; 1514], 189 len: usize, 190} 191 192impl phy::RxToken for RxToken { 193 fn consume<R, F>(self, f: F) -> R 194 where 195 F: FnOnce(&[u8]) -> R, 196 { 197 f(&self.buf[..self.len]) 198 } 199} 200 201pub struct TxToken<'a> { 202 tx: &'a PacketRingWriter, 203} 204 205impl<'a> phy::TxToken for TxToken<'a> { 206 fn consume<R, F>(self, len: usize, f: F) -> R 207 where 208 F: FnOnce(&mut [u8]) -> R, 209 { 210 let mut buf = [0u8; 1514]; 211 let actual = len.min(buf.len()); 212 let result = f(&mut buf[..actual]); 213 self.tx.try_push(&buf[..actual]); 214 result 215 } 216}