Skip to main content

strate_net_silo/
main.rs

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            // Stamp pending pings before both polls so they are never
107            // seen as "timed out" with send_ts_ns == 0.
108            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                // Stamp pings queued during IPC before the poll that sends them.
153                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}