Nothing to see here, move along
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}