1#![no_std]
2#![no_main]
3#![feature(alloc_error_handler)]
4
5extern crate alloc;
6
7mod config;
8mod content;
9mod device;
10mod icmp;
11mod ip;
12mod ipc;
13mod state;
14mod transport;
15mod vfs;
16
17use core::{alloc::Layout, panic::PanicInfo};
18use smoltcp::{iface::PollResult, socket::tcp, time::Instant};
19use state::NetworkStrate;
20use strate_net::{syscalls::*, IpcMessage, OPCODE_CLOSE, OPCODE_OPEN, OPCODE_READ, OPCODE_WRITE};
21
22alloc_freelist::define_freelist_brk_allocator!(
23 pub struct BumpAllocator;
24 brk = strat9_syscall::call::brk;
25 heap_max = 16 * 1024 * 1024;
26);
27
28#[global_allocator]
29static GLOBAL_ALLOCATOR: BumpAllocator = BumpAllocator;
30
31#[alloc_error_handler]
32fn alloc_error(_layout: Layout) -> ! {
33 let _ = call::debug_log(b"[strate-net] OOM\n");
34 exit(12);
35}
36
37#[panic_handler]
38fn panic(info: &PanicInfo) -> ! {
39 call::handle_panic("strate-net", info)
40}
41
42struct BufWriter<'a> {
43 buf: &'a mut [u8],
44 pos: usize,
45}
46
47impl core::fmt::Write for BufWriter<'_> {
48 fn write_str(&mut self, s: &str) -> core::fmt::Result {
49 let bytes = s.as_bytes();
50 let avail = self.buf.len().saturating_sub(self.pos);
51 let n = bytes.len().min(avail);
52 self.buf[self.pos..self.pos + n].copy_from_slice(&bytes[..n]);
53 self.pos += n;
54 Ok(())
55 }
56}
57
58const NANOS_PER_SEC: u64 = 1_000_000_000;
59const NANOS_PER_MICRO: u64 = 1_000;
60
61const IPC_BATCH_SIZE: usize = 16;
62
63fn log_errno(prefix: &str, err: strate_net::syscalls::Error) {
64 use core::fmt::Write;
65
66 let mut buf = [0u8; 96];
67 let len = {
68 let mut w = BufWriter {
69 buf: &mut buf,
70 pos: 0,
71 };
72 let _ = writeln!(w, "{}{}", prefix, err.to_errno());
73 w.pos
74 };
75 if let Ok(s) = core::str::from_utf8(&buf[..len]) {
76 debug_log(s);
77 }
78}
79
80fn now_instant() -> Instant {
81 match clock_gettime_ns() {
82 Ok(ns) => Instant::from_micros((ns / NANOS_PER_MICRO) as i64),
83 Err(_) => Instant::from_micros(0),
84 }
85}
86
87fn sleep_micros(micros: u64) {
88 if micros == 0 {
89 return;
90 }
91 let nanos = micros.saturating_mul(NANOS_PER_MICRO);
92 let req = TimeSpec {
93 tv_sec: (nanos / NANOS_PER_SEC) as i64,
94 tv_nsec: (nanos % NANOS_PER_SEC) as i64,
95 };
96 let _ = nanosleep(&req);
97}
98
99impl NetworkStrate {
100 fn serve(&mut self, port: u64) -> ! {
101 log("[strate-net] Starting DHCP...\n");
102 self.enable_dhcp();
103
104 loop {
105 let now = now_instant();
106 let send_ns = clock_gettime_ns().unwrap_or(0);
109 for ping in self.pending_pings.iter_mut() {
110 if ping.send_ts_ns == 0 {
111 ping.send_ts_ns = send_ns;
112 }
113 }
114 let poll_result = self
115 .interface
116 .poll(now, &mut self.device, &mut self.sockets);
117
118 self.process_icmp();
119
120 self.lingering_sockets.retain(|&handle| {
121 let socket = self.sockets.get::<tcp::Socket>(handle);
122 if socket.state() == tcp::State::Closed {
123 self.sockets.remove(handle);
124 false
125 } else {
126 true
127 }
128 });
129
130 self.process_dhcp();
131 self.process_ipv6_slaac();
132
133 let mut msg = IpcMessage::new(0);
134 let mut got_ipc = false;
135 for _ in 0..IPC_BATCH_SIZE {
136 if ipc_try_recv(port, &mut msg).is_err() {
137 break;
138 }
139 got_ipc = true;
140 let reply = match msg.msg_type {
141 OPCODE_OPEN => self.handle_open(&msg),
142 OPCODE_READ => self.handle_read(&msg),
143 OPCODE_WRITE => self.handle_write(&msg),
144 OPCODE_CLOSE => self.handle_close(&msg),
145 _ => IpcMessage::error_reply(msg.sender, -22),
146 };
147 let _ = call::ipc_reply(&reply);
148 }
149
150 if got_ipc {
151 let now2 = now_instant();
152 let send_ns2 = clock_gettime_ns().unwrap_or(0);
154 for ping in self.pending_pings.iter_mut() {
155 if ping.send_ts_ns == 0 {
156 ping.send_ts_ns = send_ns2;
157 }
158 }
159 let _ = self
160 .interface
161 .poll(now2, &mut self.device, &mut self.sockets);
162 self.process_icmp();
163 self.process_ipv6_slaac();
164 }
165
166 if !got_ipc && poll_result == PollResult::None {
167 const MAX_SLEEP_US: u64 = 1_000;
168 if let Some(delay) = self.interface.poll_delay(now, &self.sockets) {
169 let micros = delay.total_micros().min(MAX_SLEEP_US);
170 if micros > 0 {
171 sleep_micros(micros);
172 } else {
173 let _ = proc_yield();
174 }
175 } else {
176 sleep_micros(MAX_SLEEP_US);
177 }
178 }
179 }
180 }
181}
182
183fn log(msg: &str) {
184 let _ = call::debug_log(msg.as_bytes());
185}
186
187fn wait_for_kernel_mac(max_attempts: usize) -> Option<[u8; 6]> {
188 let mut mac = [0u8; 6];
189
190 for attempt in 0..max_attempts {
191 if net_info(0, &mut mac).is_ok() && mac != [0; 6] {
192 if attempt != 0 {
193 log("[strate-net] Kernel NIC became available\n");
194 }
195 return Some(mac);
196 }
197
198 if attempt == 0 {
199 log("[strate-net] Waiting for kernel NIC registration...\n");
200 }
201
202 sleep_micros(1000);
203 }
204
205 None
206}
207
208#[unsafe(no_mangle)]
209pub extern "C" fn _start() -> ! {
210 log("[strate-net] Starting network silo\n");
211
212 let port = match call::ipc_create_port(0) {
213 Ok(p) => p as u64,
214 Err(e) => {
215 log("[strate-net] Failed to create port: ");
216 log_error_code(e);
217 log("\n");
218 exit(1);
219 }
220 };
221
222 if let Err(e) = call::ipc_bind_port(port as usize, b"/net") {
223 log("[strate-net] Failed to bind to /net: ");
224 log_error_code(e);
225 log("\n");
226 exit(2);
227 }
228
229 log("[strate-net] Bound to /net\n");
230
231 let mac = match wait_for_kernel_mac(2048) {
232 Some(mac) => {
233 log("[strate-net] MAC acquired from kernel\n");
234 mac
235 }
236 None => {
237 log("[strate-net] No NIC found after waiting, using fallback MAC\n");
238 [0x52, 0x54, 0x00, 0x12, 0x34, 0x56]
239 }
240 };
241
242 let mut strate = NetworkStrate::new(mac);
243 strate.serve(port);
244}
245
246fn log_error_code(e: strate_net::syscalls::Error) {
247 use core::fmt::Write;
248
249 let mut buf = [0u8; 32];
250 let n = {
251 let mut w = BufWriter {
252 buf: &mut buf,
253 pos: 0,
254 };
255 let _ = write!(w, "{}", e.to_errno());
256 w.pos
257 };
258 if let Ok(s) = core::str::from_utf8(&buf[..n]) {
259 log(s);
260 }
261}