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
7use alloc::{collections::BTreeMap, string::String};
8use core::{
9    alloc::Layout,
10    panic::PanicInfo,
11    sync::atomic::{AtomicUsize, Ordering},
12};
13use strate_net::{syscalls::*, IpcMessage, OPCODE_CLOSE, OPCODE_OPEN, OPCODE_READ, OPCODE_WRITE};
14
15alloc_freelist::define_freelist_brk_allocator!(
16    pub struct BumpAllocator;
17    brk = strat9_syscall::call::brk;
18    heap_max = 16 * 1024 * 1024;
19);
20
21#[global_allocator]
22static GLOBAL_ALLOCATOR: BumpAllocator = BumpAllocator;
23
24#[alloc_error_handler]
25/// Implements alloc error.
26fn alloc_error(_layout: Layout) -> ! {
27    let _ = call::debug_log(b"[strate-net] OOM\n");
28    exit(12);
29}
30
31#[panic_handler]
32/// Implements panic.
33fn panic(info: &PanicInfo) -> ! {
34    let _ = call::debug_log(b"[strate-net] PANIC: ");
35    let msg = info.message();
36    let mut buf = [0u8; 256];
37    use core::fmt::Write;
38    let mut cursor = BufWriter {
39        buf: &mut buf,
40        pos: 0,
41    };
42    let _ = write!(cursor, "{}", msg);
43    let written = cursor.pos;
44    if written > 0 {
45        let _ = call::debug_log(&buf[..written]);
46    }
47    let _ = call::debug_log(b"\n");
48    exit(255);
49}
50
51/// Minimal fmt::Write adapter over a fixed byte buffer.
52struct BufWriter<'a> {
53    buf: &'a mut [u8],
54    pos: usize,
55}
56
57impl core::fmt::Write for BufWriter<'_> {
58    /// Writes str.
59    fn write_str(&mut self, s: &str) -> core::fmt::Result {
60        let bytes = s.as_bytes();
61        let avail = self.buf.len().saturating_sub(self.pos);
62        let n = bytes.len().min(avail);
63        self.buf[self.pos..self.pos + n].copy_from_slice(&bytes[..n]);
64        self.pos += n;
65        Ok(())
66    }
67}
68
69// ---------------------------------------------------------------------------
70// Network strate logic
71// ---------------------------------------------------------------------------
72
73use smoltcp::{
74    iface::{Config, Interface, SocketHandle, SocketSet},
75    phy::{self, Device, DeviceCapabilities, Medium},
76    socket::{dhcpv4, dns, icmp, tcp, udp},
77    time::Instant,
78    wire::{DnsQueryType, EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address},
79};
80
81/// Implements icmp checksum.
82fn icmp_checksum(data: &[u8]) -> u16 {
83    let mut sum: u32 = 0;
84    let mut i = 0;
85    while i + 1 < data.len() {
86        sum += u16::from_be_bytes([data[i], data[i + 1]]) as u32;
87        i += 2;
88    }
89    if i < data.len() {
90        sum += (data[i] as u32) << 8;
91    }
92    while sum >> 16 != 0 {
93        sum = (sum & 0xFFFF) + (sum >> 16);
94    }
95    !(sum as u16)
96}
97
98const MAX_FRAME_SIZE: usize = 1514;
99const NANOS_PER_SEC: u64 = 1_000_000_000;
100const NANOS_PER_MICRO: u64 = 1_000;
101static RX_ERR_LOG_BUDGET: AtomicUsize = AtomicUsize::new(16);
102static TX_ERR_LOG_BUDGET: AtomicUsize = AtomicUsize::new(16);
103
104/// Implements log errno.
105fn log_errno(prefix: &str, err: strate_net::syscalls::Error) {
106    use core::fmt::Write;
107    let mut buf = [0u8; 96];
108    let len = {
109        let mut w = BufWriter {
110            buf: &mut buf,
111            pos: 0,
112        };
113        let _ = write!(w, "{}{}\n", prefix, err.to_errno());
114        w.pos
115    };
116    if let Ok(s) = core::str::from_utf8(&buf[..len]) {
117        debug_log(s);
118    }
119}
120
121/// Implements now instant.
122fn now_instant() -> Instant {
123    match clock_gettime_ns() {
124        Ok(ns) => Instant::from_micros((ns / NANOS_PER_MICRO) as i64),
125        Err(_) => Instant::from_micros(0),
126    }
127}
128
129/// Implements sleep micros.
130fn sleep_micros(micros: u64) {
131    if micros == 0 {
132        return;
133    }
134    let nanos = micros.saturating_mul(NANOS_PER_MICRO);
135    let req = TimeSpec {
136        tv_sec: (nanos / NANOS_PER_SEC) as i64,
137        tv_nsec: (nanos % NANOS_PER_SEC) as i64,
138    };
139    let _ = nanosleep(&req);
140}
141
142struct Strat9NetDevice;
143
144impl Device for Strat9NetDevice {
145    type RxToken<'a> = Strat9RxToken;
146    type TxToken<'a> = Strat9TxToken;
147
148    /// Implements receive.
149    fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
150        let mut buf = [0u8; MAX_FRAME_SIZE];
151        match net_recv(&mut buf) {
152            Ok(n) if n > 0 => {
153                let len = core::cmp::min(n, MAX_FRAME_SIZE);
154                Some((Strat9RxToken { buf, len }, Strat9TxToken))
155            }
156            Err(e) => {
157                if e.to_errno() != 11 {
158                    if RX_ERR_LOG_BUDGET.load(Ordering::Relaxed) > 0 {
159                        RX_ERR_LOG_BUDGET.fetch_sub(1, Ordering::Relaxed);
160                        log_errno("[strate-net] net_recv errno=", e);
161                    }
162                }
163                None
164            }
165            _ => None,
166        }
167    }
168
169    /// Implements transmit.
170    fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
171        Some(Strat9TxToken)
172    }
173
174    /// Implements capabilities.
175    fn capabilities(&self) -> DeviceCapabilities {
176        let mut caps = DeviceCapabilities::default();
177        caps.max_transmission_unit = MAX_FRAME_SIZE;
178        caps.medium = Medium::Ethernet;
179        caps
180    }
181}
182
183struct Strat9RxToken {
184    buf: [u8; MAX_FRAME_SIZE],
185    len: usize,
186}
187
188impl phy::RxToken for Strat9RxToken {
189    fn consume<R, F>(self, f: F) -> R
190    where
191        F: FnOnce(&[u8]) -> R,
192    {
193        f(&self.buf[..self.len])
194    }
195}
196
197struct Strat9TxToken;
198
199impl phy::TxToken for Strat9TxToken {
200    fn consume<R, F>(self, len: usize, f: F) -> R
201    where
202        F: FnOnce(&mut [u8]) -> R,
203    {
204        if len > MAX_FRAME_SIZE {
205            log("[strate-net] TX frame too large\n");
206            return f(&mut []);
207        }
208        let mut buf = [0u8; MAX_FRAME_SIZE];
209        let ret = f(&mut buf[..len]);
210        if let Err(e) = net_send(&buf[..len]) {
211            if TX_ERR_LOG_BUDGET.load(Ordering::Relaxed) > 0 {
212                TX_ERR_LOG_BUDGET.fetch_sub(1, Ordering::Relaxed);
213                log_errno("[strate-net] net_send errno=", e);
214            }
215        }
216        ret
217    }
218}
219
220// ---------------------------------------------------------------------------
221// IP configuration obtained via DHCP
222// ---------------------------------------------------------------------------
223
224#[derive(Debug, Clone)]
225struct IpConfig {
226    /// Assigned address + prefix length (e.g. 192.168.1.100/24)
227    address: smoltcp::wire::Ipv4Cidr,
228    /// Host address only (e.g. 192.168.1.100)
229    host: Ipv4Address,
230    /// Prefix length (e.g. 24)
231    prefix_len: u8,
232    /// Netmask derived from prefix (e.g. 255.255.255.0)
233    netmask: Ipv4Address,
234    /// Broadcast derived from host+prefix (e.g. 192.168.1.255)
235    broadcast: Ipv4Address,
236    /// Default gateway (optional)
237    gateway: Option<Ipv4Address>,
238    /// Up to 3 DNS servers
239    dns: [Option<Ipv4Address>; 3],
240}
241
242/// Encode an IPv4Cidr as a human-readable string ("a.b.c.d/p\n").
243fn ipv4_cidr_to_str(cidr: &smoltcp::wire::Ipv4Cidr, buf: &mut [u8]) -> usize {
244    use core::fmt::Write;
245    let mut w = BufWriter { buf, pos: 0 };
246    let a = cidr.address().octets();
247    let _ = write!(
248        w,
249        "{}.{}.{}.{}/{}\n",
250        a[0],
251        a[1],
252        a[2],
253        a[3],
254        cidr.prefix_len()
255    );
256    w.pos
257}
258
259/// Encode an Ipv4Address as a human-readable string ("a.b.c.d\n").
260fn ipv4_addr_to_str(addr: &Ipv4Address, buf: &mut [u8]) -> usize {
261    use core::fmt::Write;
262    let mut w = BufWriter { buf, pos: 0 };
263    let a = addr.octets();
264    let _ = write!(w, "{}.{}.{}.{}\n", a[0], a[1], a[2], a[3]);
265    w.pos
266}
267
268/// Implements u8 to str.
269fn u8_to_str(v: u8, buf: &mut [u8]) -> usize {
270    use core::fmt::Write;
271    let mut w = BufWriter { buf, pos: 0 };
272    let _ = write!(w, "{}\n", v);
273    w.pos
274}
275
276/// Implements mask from prefix.
277fn mask_from_prefix(prefix: u8) -> Ipv4Address {
278    let mask: u32 = if prefix == 0 {
279        0
280    } else if prefix >= 32 {
281        u32::MAX
282    } else {
283        u32::MAX << (32 - prefix)
284    };
285    let b = mask.to_be_bytes();
286    Ipv4Address::new(b[0], b[1], b[2], b[3])
287}
288
289/// Implements broadcast from host prefix.
290fn broadcast_from_host_prefix(host: Ipv4Address, prefix: u8) -> Ipv4Address {
291    let h = u32::from_be_bytes(host.octets());
292    let m = u32::from_be_bytes(mask_from_prefix(prefix).octets());
293    let b = (h & m) | (!m);
294    let o = b.to_be_bytes();
295    Ipv4Address::new(o[0], o[1], o[2], o[3])
296}
297
298/// Implements route to str.
299fn route_to_str(gateway: &Ipv4Address, buf: &mut [u8]) -> usize {
300    use core::fmt::Write;
301    let mut w = BufWriter { buf, pos: 0 };
302    let a = gateway.octets();
303    let _ = write!(w, "default via {}.{}.{}.{}\n", a[0], a[1], a[2], a[3]);
304    w.pos
305}
306
307/// Implements dns list to str.
308fn dns_list_to_str(dns: &[Option<Ipv4Address>; 3], buf: &mut [u8]) -> usize {
309    use core::fmt::Write;
310    let mut w = BufWriter { buf, pos: 0 };
311    let mut wrote_any = false;
312    for server in dns.iter().flatten() {
313        let a = server.octets();
314        let _ = write!(w, "{}.{}.{}.{}\n", a[0], a[1], a[2], a[3]);
315        wrote_any = true;
316    }
317    if !wrote_any {
318        let _ = write!(w, "0.0.0.0\n");
319    }
320    w.pos
321}
322
323/// Parses ipv4 cidr.
324fn parse_ipv4_cidr(s: &str) -> Option<smoltcp::wire::Ipv4Cidr> {
325    let slash = s.find('/')?;
326    let ip = parse_ipv4(&s[..slash])?;
327    let prefix = s[slash + 1..].parse::<u8>().ok()?;
328    if prefix > 32 {
329        return None;
330    }
331    Some(smoltcp::wire::Ipv4Cidr::new(ip, prefix))
332}
333
334// ---------------------------------------------------------------------------
335// IPC reply builders  (match the layout expected by kernel IpcScheme)
336// ---------------------------------------------------------------------------
337//
338// OPEN success reply  (msg_type = 0x80):
339//   payload[0..4]  = status : u32 LE  (0 = OK)
340//   payload[4..12] = file_id: u64 LE
341//   payload[12..20]= size   : u64 LE  (u64::MAX ⇒ unknown)
342//   payload[20..24]= flags  : u32 LE  (FileFlags bits)
343//
344// READ success reply  (msg_type = 0x80):
345//   payload[0..4]  = status     : u32 LE (0 = OK)
346//   payload[4..8]  = bytes_read : u32 LE
347//   payload[8..48] = data (up to 40 bytes inline)
348//
349// CLOSE / generic OK reply (msg_type = 0x80):
350//   payload[0..4] = status : u32 LE (0 = OK)
351
352/// Implements reply open.
353fn reply_open(sender: u64, file_id: u64, size: u64, flags: u32) -> IpcMessage {
354    let mut msg = IpcMessage::new(0x80);
355    msg.sender = sender;
356    msg.payload[0..4].copy_from_slice(&0u32.to_le_bytes());
357    msg.payload[4..12].copy_from_slice(&file_id.to_le_bytes());
358    msg.payload[12..20].copy_from_slice(&size.to_le_bytes());
359    msg.payload[20..24].copy_from_slice(&flags.to_le_bytes());
360    msg
361}
362
363/// Implements reply read.
364fn reply_read(sender: u64, data: &[u8]) -> IpcMessage {
365    let mut msg = IpcMessage::new(0x80);
366    msg.sender = sender;
367    msg.payload[0..4].copy_from_slice(&0u32.to_le_bytes());
368    // Max inline data: 48 - 8 = 40 bytes
369    let n = data.len().min(40);
370    msg.payload[4..8].copy_from_slice(&(n as u32).to_le_bytes());
371    msg.payload[8..8 + n].copy_from_slice(&data[..n]);
372    msg
373}
374
375/// Implements reply write.
376fn reply_write(sender: u64, n: usize) -> IpcMessage {
377    let mut msg = IpcMessage::new(0x80);
378    msg.sender = sender;
379    msg.payload[0..4].copy_from_slice(&0u32.to_le_bytes());
380    msg.payload[4..8].copy_from_slice(&(n as u32).to_le_bytes());
381    msg
382}
383
384/// Implements reply ok.
385fn reply_ok(sender: u64) -> IpcMessage {
386    let mut msg = IpcMessage::new(0x80);
387    msg.sender = sender;
388    msg.payload[0..4].copy_from_slice(&0u32.to_le_bytes());
389    msg
390}
391
392/// Parses ipv4.
393fn parse_ipv4(s: &str) -> Option<Ipv4Address> {
394    let mut octets = [0u8; 4];
395    let mut idx = 0;
396    let mut val: u16 = 0;
397    let mut has_digit = false;
398    for &b in s.as_bytes() {
399        if b == b'.' {
400            if !has_digit || idx >= 3 {
401                return None;
402            }
403            if val > 255 {
404                return None;
405            }
406            octets[idx] = val as u8;
407            idx += 1;
408            val = 0;
409            has_digit = false;
410        } else if b >= b'0' && b <= b'9' {
411            val = val * 10 + (b - b'0') as u16;
412            has_digit = true;
413        } else {
414            break;
415        }
416    }
417    if !has_digit || idx != 3 || val > 255 {
418        return None;
419    }
420    octets[3] = val as u8;
421    Some(Ipv4Address::new(octets[0], octets[1], octets[2], octets[3]))
422}
423
424// ---------------------------------------------------------------------------
425// NetworkStrate  – main state machine
426// ---------------------------------------------------------------------------
427
428struct PendingPing {
429    seq: u16,
430    send_ts_ns: u64,
431}
432
433#[derive(Clone, Copy)]
434struct TcpListenerState {
435    socket: SocketHandle,
436    port: u16,
437    auto_relisten: bool,
438}
439
440/// State for an outgoing TCP connection (`tcp/connect/<ip>/<port>`).
441#[derive(Copy, Clone)]
442struct TcpConnState {
443    socket: SocketHandle,
444    local_port: u16,
445    remote: IpEndpoint,
446}
447
448/// State for a UDP scheme handle bound on a local port (`udp/bind/<port>`).
449#[derive(Copy, Clone)]
450struct UdpBoundState {
451    socket: SocketHandle,
452    local_port: u16,
453}
454
455/// State for a UDP scheme handle with a fixed remote endpoint
456/// (`udp/connect/<ip>/<port>` or `udp/send/<ip>/<port>`).
457#[derive(Copy, Clone)]
458struct UdpConnState {
459    socket: SocketHandle,
460    local_port: u16,
461    remote: IpEndpoint,
462}
463
464struct NetworkStrate {
465    device: Strat9NetDevice,
466    interface: Interface,
467    sockets: SocketSet<'static>,
468    dhcp_handle: SocketHandle,
469    dns_handle: SocketHandle,
470    icmp_handle: SocketHandle,
471    ip_config: Option<IpConfig>,
472    /// VFS handles: file_id → virtual path ("/net/*")
473    open_handles: BTreeMap<u64, String>,
474    tcp_listeners: BTreeMap<u64, TcpListenerState>,
475    tcp_connections: BTreeMap<u64, TcpConnState>,
476    udp_bound: BTreeMap<u64, UdpBoundState>,
477    udp_connections: BTreeMap<u64, UdpConnState>,
478    next_fid: u64,
479    /// Last ping that was sent, waiting for reply
480    pending_ping: Option<PendingPing>,
481    /// Received reply: (seq, rtt_us)
482    ping_reply: Option<(u16, u64)>,
483    ping_ident: u16,
484    dhcp_enabled: bool,
485}
486
487impl NetworkStrate {
488    /// Creates a new instance.
489    fn new(mac: [u8; 6]) -> Self {
490        let mut device = Strat9NetDevice;
491        let config = Config::new(EthernetAddress(mac).into());
492        let interface = Interface::new(config, &mut device, Instant::from_micros(0));
493        let mut sockets = SocketSet::new(alloc::vec![]);
494
495        let dhcp_socket = dhcpv4::Socket::new();
496        let dhcp_handle = sockets.add(dhcp_socket);
497
498        let dns_socket = dns::Socket::new(&[], alloc::vec![]);
499        let dns_handle = sockets.add(dns_socket);
500
501        let icmp_rx_buf = icmp::PacketBuffer::new(
502            alloc::vec![icmp::PacketMetadata::EMPTY; 4],
503            alloc::vec![0u8; 1024],
504        );
505        let icmp_tx_buf = icmp::PacketBuffer::new(
506            alloc::vec![icmp::PacketMetadata::EMPTY; 4],
507            alloc::vec![0u8; 1024],
508        );
509        let icmp_socket = icmp::Socket::new(icmp_rx_buf, icmp_tx_buf);
510        let icmp_handle = sockets.add(icmp_socket);
511
512        Self {
513            device,
514            interface,
515            sockets,
516            dhcp_handle,
517            dns_handle,
518            icmp_handle,
519            ip_config: None,
520            open_handles: BTreeMap::new(),
521            tcp_listeners: BTreeMap::new(),
522            tcp_connections: BTreeMap::new(),
523            udp_bound: BTreeMap::new(),
524            udp_connections: BTreeMap::new(),
525            next_fid: 1,
526            pending_ping: None,
527            ping_reply: None,
528            ping_ident: 0x9001,
529            dhcp_enabled: true,
530        }
531    }
532
533    /// Returns true if a local UDP port is already in use by an opened UDP handle.
534    fn udp_port_in_use(&self, port: u16) -> bool {
535        self.udp_bound.values().any(|s| s.local_port == port)
536            || self.udp_connections.values().any(|s| s.local_port == port)
537    }
538
539    /// Allocates an ephemeral UDP local port from the dynamic range.
540    fn alloc_udp_ephemeral_port(&self) -> Option<u16> {
541        const BASE: u16 = 49_152;
542        const COUNT: usize = 16_384;
543        let start = (self.next_fid as usize) % COUNT;
544        for step in 0..COUNT {
545            let port = BASE + ((start + step) % COUNT) as u16;
546            if !self.udp_port_in_use(port) {
547                return Some(port);
548            }
549        }
550        None
551    }
552
553    /// Creates and binds an internal UDP transport endpoint on `local_port`,
554    /// then registers it in the smoltcp socket set.
555    fn create_udp_socket(&mut self, local_port: u16) -> core::result::Result<SocketHandle, i32> {
556        let rx_buf = udp::PacketBuffer::new(
557            alloc::vec![udp::PacketMetadata::EMPTY; 16],
558            alloc::vec![0u8; 4096],
559        );
560        let tx_buf = udp::PacketBuffer::new(
561            alloc::vec![udp::PacketMetadata::EMPTY; 16],
562            alloc::vec![0u8; 4096],
563        );
564        let mut socket = udp::Socket::new(rx_buf, tx_buf);
565        if socket.bind(local_port).is_err() {
566            return Err(-98); // EADDRINUSE
567        }
568        Ok(self.sockets.add(socket))
569    }
570
571    /// Returns a textual name for a TCP state.
572    fn tcp_state_name(state: tcp::State) -> &'static str {
573        match state {
574            tcp::State::Closed => "CLOSED",
575            tcp::State::Listen => "LISTEN",
576            tcp::State::SynSent => "SYN-SENT",
577            tcp::State::SynReceived => "SYN-RECEIVED",
578            tcp::State::Established => "ESTABLISHED",
579            tcp::State::FinWait1 => "FIN-WAIT-1",
580            tcp::State::FinWait2 => "FIN-WAIT-2",
581            tcp::State::CloseWait => "CLOSE-WAIT",
582            tcp::State::Closing => "CLOSING",
583            tcp::State::LastAck => "LAST-ACK",
584            tcp::State::TimeWait => "TIME-WAIT",
585        }
586    }
587
588    // -----------------------------------------------------------------------
589    // DHCP event processing
590    // -----------------------------------------------------------------------
591
592    /// Implements process dhcp.
593    fn process_dhcp(&mut self) {
594        if !self.dhcp_enabled {
595            return;
596        }
597        let event = self
598            .sockets
599            .get_mut::<dhcpv4::Socket>(self.dhcp_handle)
600            .poll();
601
602        match event {
603            Some(dhcpv4::Event::Configured(config)) => {
604                log("[strate-net] DHCP: address acquired\n");
605
606                // Apply address to the interface
607                self.interface.update_ip_addrs(|addrs| {
608                    let cidr = IpCidr::new(
609                        IpAddress::Ipv4(config.address.address()),
610                        config.address.prefix_len(),
611                    );
612                    if let Some(slot) = addrs.iter_mut().next() {
613                        *slot = cidr;
614                    } else {
615                        // Ignore error: heap alloc failure is a hard stop
616                        let _ = addrs.push(cidr);
617                    }
618                });
619
620                // Apply default route
621                let _ = self.interface.routes_mut().remove_default_ipv4_route();
622                if let Some(gw) = config.router {
623                    self.interface.routes_mut().add_default_ipv4_route(gw).ok();
624                }
625
626                // Collect up to 3 DNS servers
627                let mut dns = [None::<Ipv4Address>; 3];
628                for (slot, &server) in dns.iter_mut().zip(config.dns_servers.iter()) {
629                    *slot = Some(server);
630                }
631
632                let host = config.address.address();
633                let prefix_len = config.address.prefix_len();
634                let netmask = mask_from_prefix(prefix_len);
635                let broadcast = broadcast_from_host_prefix(host, prefix_len);
636                self.ip_config = Some(IpConfig {
637                    address: config.address,
638                    host,
639                    prefix_len,
640                    netmask,
641                    broadcast,
642                    gateway: config.router,
643                    dns,
644                });
645                self.refresh_dns_servers();
646            }
647            Some(dhcpv4::Event::Deconfigured) => {
648                log("[strate-net] DHCP: deconfigured (lease expired?)\n");
649                self.ip_config = None;
650                // Clear interface addresses
651                self.interface.update_ip_addrs(|addrs| addrs.clear());
652                let _ = self.interface.routes_mut().remove_default_ipv4_route();
653                self.refresh_dns_servers();
654            }
655            None => {}
656        }
657    }
658
659    /// Implements refresh dns servers.
660    fn refresh_dns_servers(&mut self) {
661        let mut servers = [IpAddress::Ipv4(Ipv4Address::new(0, 0, 0, 0)); 3];
662        let mut count = 0usize;
663
664        if let Some(ref cfg) = self.ip_config {
665            for s in cfg.dns.iter().flatten() {
666                if *s != Ipv4Address::new(0, 0, 0, 0) && count < servers.len() {
667                    servers[count] = IpAddress::Ipv4(*s);
668                    count += 1;
669                }
670            }
671            if count == 0 {
672                if let Some(gw) = cfg.gateway {
673                    servers[0] = IpAddress::Ipv4(gw);
674                    count = 1;
675                }
676            }
677        }
678
679        let socket = self.sockets.get_mut::<dns::Socket>(self.dns_handle);
680        socket.update_servers(&servers[..count]);
681    }
682
683    /// Implements apply ipv4 config.
684    fn apply_ipv4_config(
685        &mut self,
686        address: smoltcp::wire::Ipv4Cidr,
687        gateway: Option<Ipv4Address>,
688        dns: [Option<Ipv4Address>; 3],
689    ) {
690        let host = address.address();
691        let prefix_len = address.prefix_len();
692        let netmask = mask_from_prefix(prefix_len);
693        let broadcast = broadcast_from_host_prefix(host, prefix_len);
694
695        self.interface.update_ip_addrs(|addrs| {
696            let cidr = IpCidr::new(IpAddress::Ipv4(host), prefix_len);
697            if let Some(slot) = addrs.iter_mut().next() {
698                *slot = cidr;
699            } else {
700                let _ = addrs.push(cidr);
701            }
702        });
703
704        let _ = self.interface.routes_mut().remove_default_ipv4_route();
705        if let Some(gw) = gateway {
706            let _ = self.interface.routes_mut().add_default_ipv4_route(gw);
707        }
708
709        self.ip_config = Some(IpConfig {
710            address,
711            host,
712            prefix_len,
713            netmask,
714            broadcast,
715            gateway,
716            dns,
717        });
718        self.refresh_dns_servers();
719    }
720
721    /// Implements resolve hostname blocking.
722    fn resolve_hostname_blocking(&mut self, name: &str) -> core::result::Result<Ipv4Address, i32> {
723        if let Some(ip) = parse_ipv4(name) {
724            return Ok(ip);
725        }
726        if self.ip_config.is_none() {
727            return Err(-11);
728        }
729
730        let query = {
731            let cx = self.interface.context();
732            let socket = self.sockets.get_mut::<dns::Socket>(self.dns_handle);
733            match socket.start_query(cx, name, DnsQueryType::A) {
734                Ok(q) => q,
735                Err(_) => return Err(-22),
736            }
737        };
738
739        let deadline_ns = clock_gettime_ns()
740            .unwrap_or(0)
741            .saturating_add(3_000_000_000);
742        loop {
743            let now = now_instant();
744            let _ = self
745                .interface
746                .poll(now, &mut self.device, &mut self.sockets);
747            self.process_dhcp();
748            self.process_icmp();
749
750            let res = self
751                .sockets
752                .get_mut::<dns::Socket>(self.dns_handle)
753                .get_query_result(query);
754            match res {
755                Ok(addrs) => {
756                    for addr in addrs {
757                        let IpAddress::Ipv4(v4) = addr;
758                        return Ok(v4);
759                    }
760                    return Err(-2);
761                }
762                Err(dns::GetQueryResultError::Failed) => return Err(-2),
763                Err(dns::GetQueryResultError::Pending) => {
764                    if clock_gettime_ns().unwrap_or(0) >= deadline_ns {
765                        return Err(-110);
766                    }
767                    sleep_micros(10_000);
768                }
769            }
770        }
771    }
772
773    // -----------------------------------------------------------------------
774    // ICMP echo processing
775    // -----------------------------------------------------------------------
776
777    /// Implements process icmp.
778    fn process_icmp(&mut self) {
779        let socket = self.sockets.get_mut::<icmp::Socket>(self.icmp_handle);
780        if !socket.can_recv() {
781            return;
782        }
783        let now_ns = clock_gettime_ns().unwrap_or(0);
784        while socket.can_recv() {
785            let Ok((data, _addr)) = socket.recv() else {
786                break;
787            };
788            // data is the raw ICMP payload after IP header
789            if data.len() < 8 {
790                continue;
791            }
792            // ICMP type=0 (echo reply), code=0
793            if data[0] != 0 {
794                continue;
795            }
796            let ident = u16::from_be_bytes([data[4], data[5]]);
797            let seq = u16::from_be_bytes([data[6], data[7]]);
798            if ident != self.ping_ident {
799                continue;
800            }
801            if let Some(ref pending) = self.pending_ping {
802                if seq == pending.seq {
803                    let rtt_us = now_ns.saturating_sub(pending.send_ts_ns) / 1000;
804                    self.ping_reply = Some((seq, rtt_us));
805                    self.pending_ping = None;
806                }
807            }
808        }
809    }
810
811    /// Implements send ping.
812    fn send_ping(&mut self, target: Ipv4Address, seq: u16) -> bool {
813        // One in-flight ping at a time, and don't overwrite unread replies.
814        if self.pending_ping.is_some() || self.ping_reply.is_some() {
815            return false;
816        }
817        let socket = self.sockets.get_mut::<icmp::Socket>(self.icmp_handle);
818        if !socket.is_open() {
819            socket.bind(icmp::Endpoint::Ident(self.ping_ident)).ok();
820        }
821        if !socket.can_send() {
822            return false;
823        }
824        // Build ICMP echo request manually: type(1)+code(1)+cksum(2)+ident(2)+seq(2)+payload
825        let payload_len = 40;
826        let icmp_len = 8 + payload_len;
827        let Ok(buf) = socket.send(icmp_len, IpAddress::Ipv4(target)) else {
828            return false;
829        };
830        buf[0] = 8; // type = echo request
831        buf[1] = 0; // code
832        buf[2] = 0; // checksum (filled later)
833        buf[3] = 0;
834        buf[4..6].copy_from_slice(&self.ping_ident.to_be_bytes());
835        buf[6..8].copy_from_slice(&seq.to_be_bytes());
836        for i in 8..icmp_len {
837            buf[i] = 0xAA;
838        }
839        // Compute ICMP checksum
840        let cksum = icmp_checksum(buf);
841        buf[2..4].copy_from_slice(&cksum.to_be_bytes());
842
843        let now_ns = clock_gettime_ns().unwrap_or(0);
844        self.pending_ping = Some(PendingPing {
845            seq,
846            send_ts_ns: now_ns,
847        });
848        true
849    }
850
851    // -----------------------------------------------------------------------
852    // VFS / IPC handlers
853    // -----------------------------------------------------------------------
854
855    /// Implements handle open.
856    fn handle_open(&mut self, msg: &IpcMessage) -> IpcMessage {
857        let path_len = u16::from_le_bytes([msg.payload[4], msg.payload[5]]) as usize;
858        if path_len > 42 {
859            return IpcMessage::error_reply(msg.sender, -22);
860        }
861        let path_bytes = &msg.payload[6..6 + path_len];
862        let path = match core::str::from_utf8(path_bytes) {
863            Ok(p) => p.trim_start_matches('/'),
864            Err(_) => return IpcMessage::error_reply(msg.sender, -22),
865        };
866
867        match path {
868            "" => {
869                let fid = self.alloc_fid();
870                self.open_handles.insert(fid, String::from(""));
871                reply_open(msg.sender, fid, u64::MAX, 1)
872            }
873            "ip" | "address" | "prefix" | "netmask" | "broadcast" | "gateway" | "route"
874            | "routes" | "dns" | "resolve" | "ping" | "tcp" | "tcp/listeners"
875            | "tcp/connections" | "tcp/stats" | "udp" | "dhcp" => {
876                let fid = self.alloc_fid();
877                self.open_handles.insert(fid, String::from(path));
878                reply_open(msg.sender, fid, u64::MAX, 0)
879            }
880            p if p.starts_with("resolve/") => {
881                if p.len() <= 8 {
882                    return IpcMessage::error_reply(msg.sender, -22);
883                }
884                let fid = self.alloc_fid();
885                self.open_handles.insert(fid, String::from(path));
886                reply_open(msg.sender, fid, u64::MAX, 0)
887            }
888            p if p.starts_with("ping/") => {
889                let fid = self.alloc_fid();
890                self.open_handles.insert(fid, String::from(path));
891                reply_open(msg.sender, fid, u64::MAX, 0)
892            }
893            p if p.starts_with("tcp/connect/") => {
894                let rest = &p[12..];
895                let parts: alloc::vec::Vec<&str> = rest.split('/').collect();
896                if parts.len() < 2 || parts.len() > 3 {
897                    return IpcMessage::error_reply(msg.sender, -22);
898                }
899                let Some(ip) = parse_ipv4(parts[0]) else {
900                    return IpcMessage::error_reply(msg.sender, -22);
901                };
902                let Some(port) = parts[1].parse::<u16>().ok() else {
903                    return IpcMessage::error_reply(msg.sender, -22);
904                };
905                if port == 0 {
906                    return IpcMessage::error_reply(msg.sender, -22);
907                }
908
909                let rx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 4096]);
910                let tx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 4096]);
911                let sock = tcp::Socket::new(rx_buf, tx_buf);
912                let handle = self.sockets.add(sock);
913
914                let local_port = if parts.len() == 3 {
915                    let Some(lp) = parts[2].parse::<u16>().ok() else {
916                        self.sockets.remove(handle);
917                        return IpcMessage::error_reply(msg.sender, -22);
918                    };
919                    if lp == 0 {
920                        self.sockets.remove(handle);
921                        return IpcMessage::error_reply(msg.sender, -22);
922                    }
923                    lp
924                } else {
925                    49152 + (self.next_fid as u16 % 16384)
926                };
927                let remote = (smoltcp::wire::IpAddress::Ipv4(ip), port);
928                let conn_socket = self.sockets.get_mut::<tcp::Socket>(handle);
929                if conn_socket
930                    .connect(self.interface.context(), remote, local_port)
931                    .is_err()
932                {
933                    self.sockets.remove(handle);
934                    return IpcMessage::error_reply(msg.sender, -111);
935                }
936
937                let fid = self.alloc_fid();
938                self.open_handles.insert(fid, String::from(path));
939                self.tcp_connections.insert(
940                    fid,
941                    TcpConnState {
942                        socket: handle,
943                        local_port,
944                        remote: IpEndpoint::new(IpAddress::Ipv4(ip), port),
945                    },
946                );
947                reply_open(msg.sender, fid, u64::MAX, 0)
948            }
949            p if p.starts_with("tcp/listen-once/") => {
950                let port_str = &p[16..];
951                let Some(port) = port_str.parse::<u16>().ok() else {
952                    return IpcMessage::error_reply(msg.sender, -22);
953                };
954                if port == 0 {
955                    return IpcMessage::error_reply(msg.sender, -22);
956                }
957
958                let rx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 4096]);
959                let tx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 4096]);
960                let mut sock = tcp::Socket::new(rx_buf, tx_buf);
961                if sock.listen(port).is_err() {
962                    return IpcMessage::error_reply(msg.sender, -98);
963                }
964                let socket = self.sockets.add(sock);
965
966                let fid = self.alloc_fid();
967                self.open_handles.insert(fid, String::from(path));
968                self.tcp_listeners.insert(
969                    fid,
970                    TcpListenerState {
971                        socket,
972                        port,
973                        auto_relisten: false,
974                    },
975                );
976                reply_open(msg.sender, fid, u64::MAX, 0)
977            }
978            p if p.starts_with("tcp/listen/") => {
979                let port_str = &p[11..];
980                let Some(port) = port_str.parse::<u16>().ok() else {
981                    return IpcMessage::error_reply(msg.sender, -22);
982                };
983                if port == 0 {
984                    return IpcMessage::error_reply(msg.sender, -22);
985                }
986
987                let rx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 4096]);
988                let tx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 4096]);
989                let mut sock = tcp::Socket::new(rx_buf, tx_buf);
990                if sock.listen(port).is_err() {
991                    return IpcMessage::error_reply(msg.sender, -98);
992                }
993                let socket = self.sockets.add(sock);
994
995                let fid = self.alloc_fid();
996                self.open_handles.insert(fid, String::from(path));
997                self.tcp_listeners.insert(
998                    fid,
999                    TcpListenerState {
1000                        socket,
1001                        port,
1002                        auto_relisten: true,
1003                    },
1004                );
1005                reply_open(msg.sender, fid, u64::MAX, 0)
1006            }
1007            p if p.starts_with("udp/bind/") => {
1008                let port_str = &p[9..];
1009                let Some(port) = port_str.parse::<u16>().ok() else {
1010                    return IpcMessage::error_reply(msg.sender, -22);
1011                };
1012                if port == 0 || self.udp_port_in_use(port) {
1013                    return IpcMessage::error_reply(msg.sender, -98);
1014                }
1015                let Ok(socket) = self.create_udp_socket(port) else {
1016                    return IpcMessage::error_reply(msg.sender, -98);
1017                };
1018
1019                let fid = self.alloc_fid();
1020                self.open_handles.insert(fid, String::from(path));
1021                self.udp_bound.insert(
1022                    fid,
1023                    UdpBoundState {
1024                        socket,
1025                        local_port: port,
1026                    },
1027                );
1028                reply_open(msg.sender, fid, u64::MAX, 0)
1029            }
1030            p if p.starts_with("udp/connect/") => {
1031                let rest = &p[12..];
1032                let parts: alloc::vec::Vec<&str> = rest.splitn(2, '/').collect();
1033                if parts.len() != 2 {
1034                    return IpcMessage::error_reply(msg.sender, -22);
1035                }
1036                let Some(ip) = parse_ipv4(parts[0]) else {
1037                    return IpcMessage::error_reply(msg.sender, -22);
1038                };
1039                let Some(remote_port) = parts[1].parse::<u16>().ok() else {
1040                    return IpcMessage::error_reply(msg.sender, -22);
1041                };
1042                if remote_port == 0 {
1043                    return IpcMessage::error_reply(msg.sender, -22);
1044                }
1045
1046                let Some(local_port) = self.alloc_udp_ephemeral_port() else {
1047                    return IpcMessage::error_reply(msg.sender, -28); // ENOSPC
1048                };
1049                let Ok(socket) = self.create_udp_socket(local_port) else {
1050                    return IpcMessage::error_reply(msg.sender, -98);
1051                };
1052
1053                let fid = self.alloc_fid();
1054                self.open_handles.insert(fid, String::from(path));
1055                self.udp_connections.insert(
1056                    fid,
1057                    UdpConnState {
1058                        socket,
1059                        local_port,
1060                        remote: IpEndpoint::new(IpAddress::Ipv4(ip), remote_port),
1061                    },
1062                );
1063                reply_open(msg.sender, fid, u64::MAX, 0)
1064            }
1065            p if p.starts_with("udp/send/") => {
1066                let rest = &p[9..];
1067                let parts: alloc::vec::Vec<&str> = rest.splitn(2, '/').collect();
1068                if parts.len() != 2 {
1069                    return IpcMessage::error_reply(msg.sender, -22);
1070                }
1071                let Some(ip) = parse_ipv4(parts[0]) else {
1072                    return IpcMessage::error_reply(msg.sender, -22);
1073                };
1074                let Some(remote_port) = parts[1].parse::<u16>().ok() else {
1075                    return IpcMessage::error_reply(msg.sender, -22);
1076                };
1077                if remote_port == 0 {
1078                    return IpcMessage::error_reply(msg.sender, -22);
1079                }
1080
1081                let Some(local_port) = self.alloc_udp_ephemeral_port() else {
1082                    return IpcMessage::error_reply(msg.sender, -28); // ENOSPC
1083                };
1084                let Ok(socket) = self.create_udp_socket(local_port) else {
1085                    return IpcMessage::error_reply(msg.sender, -98);
1086                };
1087
1088                let fid = self.alloc_fid();
1089                self.open_handles.insert(fid, String::from(path));
1090                self.udp_connections.insert(
1091                    fid,
1092                    UdpConnState {
1093                        socket,
1094                        local_port,
1095                        remote: IpEndpoint::new(IpAddress::Ipv4(ip), remote_port),
1096                    },
1097                );
1098                reply_open(msg.sender, fid, u64::MAX, 0)
1099            }
1100            p if p.starts_with("route/add/")
1101                || p.starts_with("route/del/")
1102                || p.starts_with("route/default/set/")
1103                || p == "route/default/clear" =>
1104            {
1105                let fid = self.alloc_fid();
1106                self.open_handles.insert(fid, String::from(path));
1107                reply_open(msg.sender, fid, u64::MAX, 0)
1108            }
1109            p if p.starts_with("ip/set/")
1110                || p.starts_with("dns/set/")
1111                || p == "dhcp/enable"
1112                || p == "dhcp/disable" =>
1113            {
1114                let fid = self.alloc_fid();
1115                self.open_handles.insert(fid, String::from(path));
1116                reply_open(msg.sender, fid, u64::MAX, 0)
1117            }
1118            _ => IpcMessage::error_reply(msg.sender, -2),
1119        }
1120    }
1121
1122    /// Implements handle tcp read.
1123    fn handle_tcp_read(&mut self, sender: u64, listener: TcpListenerState) -> IpcMessage {
1124        let socket = self.sockets.get_mut::<tcp::Socket>(listener.socket);
1125        if !socket.is_open() || (!socket.is_listening() && !socket.is_active()) {
1126            if listener.auto_relisten {
1127                let _ = socket.listen(listener.port);
1128            } else {
1129                return IpcMessage::error_reply(sender, -104);
1130            }
1131        }
1132
1133        let mut data = [0u8; 40];
1134        if socket.can_recv() {
1135            match socket.recv_slice(&mut data) {
1136                Ok(n) => return reply_read(sender, &data[..n]),
1137                Err(_) => return IpcMessage::error_reply(sender, -5),
1138            }
1139        }
1140
1141        if socket.is_open() && !socket.may_recv() && !socket.may_send() {
1142            socket.abort();
1143            if listener.auto_relisten {
1144                let _ = socket.listen(listener.port);
1145            } else {
1146                return IpcMessage::error_reply(sender, -104);
1147            }
1148        }
1149        IpcMessage::error_reply(sender, -11)
1150    }
1151
1152    /// Implements handle tcp write.
1153    fn handle_tcp_write(
1154        &mut self,
1155        sender: u64,
1156        listener: TcpListenerState,
1157        msg: &IpcMessage,
1158    ) -> IpcMessage {
1159        let socket = self.sockets.get_mut::<tcp::Socket>(listener.socket);
1160        if !socket.is_open() || (!socket.is_listening() && !socket.is_active()) {
1161            if listener.auto_relisten {
1162                let _ = socket.listen(listener.port);
1163            } else {
1164                return IpcMessage::error_reply(sender, -104);
1165            }
1166        }
1167
1168        let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1169        let data_len = core::cmp::min(data_len, msg.payload.len().saturating_sub(18));
1170        let data = &msg.payload[18..18 + data_len];
1171
1172        if !socket.can_send() {
1173            return IpcMessage::error_reply(sender, -11);
1174        }
1175
1176        match socket.send_slice(data) {
1177            Ok(n) => reply_write(sender, n),
1178            Err(_) => IpcMessage::error_reply(sender, -11),
1179        }
1180    }
1181
1182    /// Read from an outgoing TCP connection.
1183    fn handle_tcp_conn_read(&mut self, sender: u64, conn: TcpConnState) -> IpcMessage {
1184        let socket = self.sockets.get_mut::<tcp::Socket>(conn.socket);
1185        if !socket.is_open() {
1186            return IpcMessage::error_reply(sender, -104); // ECONNRESET
1187        }
1188        let state = socket.state();
1189        if state == tcp::State::SynSent || state == tcp::State::SynReceived {
1190            return IpcMessage::error_reply(sender, -115); // EINPROGRESS
1191        }
1192        let mut data = [0u8; 40];
1193        if socket.can_recv() {
1194            match socket.recv_slice(&mut data) {
1195                Ok(n) => reply_read(sender, &data[..n]),
1196                Err(_) => IpcMessage::error_reply(sender, -5),
1197            }
1198        } else {
1199            IpcMessage::error_reply(sender, -11) // EAGAIN
1200        }
1201    }
1202
1203    /// Write to an outgoing TCP connection.
1204    fn handle_tcp_conn_write(
1205        &mut self,
1206        sender: u64,
1207        conn: TcpConnState,
1208        msg: &IpcMessage,
1209    ) -> IpcMessage {
1210        let socket = self.sockets.get_mut::<tcp::Socket>(conn.socket);
1211        if !socket.is_open() {
1212            return IpcMessage::error_reply(sender, -104);
1213        }
1214        let state = socket.state();
1215        if state == tcp::State::SynSent || state == tcp::State::SynReceived {
1216            return IpcMessage::error_reply(sender, -115); // EINPROGRESS
1217        }
1218        let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1219        let data_len = core::cmp::min(data_len, msg.payload.len().saturating_sub(18));
1220        let data = &msg.payload[18..18 + data_len];
1221
1222        if !socket.can_send() {
1223            return IpcMessage::error_reply(sender, -11);
1224        }
1225        match socket.send_slice(data) {
1226            Ok(n) => reply_write(sender, n),
1227            Err(_) => IpcMessage::error_reply(sender, -11),
1228        }
1229    }
1230
1231    /// Read from a UDP scheme handle bound on a local port.
1232    ///
1233    /// Returned bytes are encoded as:
1234    /// - [0..4]  source IPv4
1235    /// - [4..6]  source UDP port (big-endian)
1236    /// - [6..]   datagram payload (truncated to fit inline IPC reply)
1237    fn handle_udp_bound_read(&mut self, sender: u64, state: UdpBoundState) -> IpcMessage {
1238        let socket = self.sockets.get_mut::<udp::Socket>(state.socket);
1239        let Ok((data, meta)) = socket.recv() else {
1240            return IpcMessage::error_reply(sender, -11); // EAGAIN
1241        };
1242
1243        let IpAddress::Ipv4(src_ip) = meta.endpoint.addr;
1244
1245        // Layout: [src_ip: 4 bytes][src_port: 2 bytes][payload: up to 1472 bytes]
1246        let mut out = [0u8; 1478];
1247        out[0..4].copy_from_slice(&src_ip.octets());
1248        out[4..6].copy_from_slice(&meta.endpoint.port.to_be_bytes());
1249        let data_n = core::cmp::min(data.len(), out.len().saturating_sub(6));
1250        out[6..6 + data_n].copy_from_slice(&data[..data_n]);
1251        reply_read(sender, &out[..6 + data_n])
1252    }
1253
1254    /// Write to a bound UDP scheme handle is not supported directly.
1255    ///
1256    /// Use `/net/udp/connect/<ip>/<port>` for bidirectional traffic or
1257    /// `/net/udp/send/<ip>/<port>` for datagram sends with a fixed peer.
1258    fn handle_udp_bound_write(
1259        &mut self,
1260        sender: u64,
1261        state: UdpBoundState,
1262        msg: &IpcMessage,
1263    ) -> IpcMessage {
1264        let _ = state;
1265        let _ = msg;
1266        IpcMessage::error_reply(sender, -95) // EOPNOTSUPP
1267    }
1268
1269    /// Read from a connected UDP scheme handle.
1270    fn handle_udp_conn_read(&mut self, sender: u64, conn: UdpConnState) -> IpcMessage {
1271        let socket = self.sockets.get_mut::<udp::Socket>(conn.socket);
1272        while socket.can_recv() {
1273            let Ok((data, meta)) = socket.recv() else {
1274                break;
1275            };
1276            if meta.endpoint.addr == conn.remote.addr && meta.endpoint.port == conn.remote.port {
1277                let n = core::cmp::min(data.len(), 1472); // max UDP payload at 1500-byte MTU
1278                return reply_read(sender, &data[..n]);
1279            }
1280        }
1281        IpcMessage::error_reply(sender, -11) // EAGAIN
1282    }
1283
1284    /// Write to a connected UDP scheme handle.
1285    fn handle_udp_conn_write(
1286        &mut self,
1287        sender: u64,
1288        conn: UdpConnState,
1289        msg: &IpcMessage,
1290    ) -> IpcMessage {
1291        let socket = self.sockets.get_mut::<udp::Socket>(conn.socket);
1292        let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1293        let data_len = core::cmp::min(data_len, msg.payload.len().saturating_sub(18));
1294        let data = &msg.payload[18..18 + data_len];
1295        if !socket.can_send() {
1296            return IpcMessage::error_reply(sender, -11);
1297        }
1298        match socket.send_slice(data, conn.remote) {
1299            Ok(()) => reply_write(sender, data_len),
1300            Err(udp::SendError::BufferFull) => IpcMessage::error_reply(sender, -11),
1301            Err(udp::SendError::Unaddressable) => IpcMessage::error_reply(sender, -22),
1302        }
1303    }
1304
1305    /// Implements handle read.
1306    fn handle_read(&mut self, msg: &IpcMessage) -> IpcMessage {
1307        let file_id = u64::from_le_bytes(msg.payload[0..8].try_into().unwrap_or([0u8; 8]));
1308        let offset = u64::from_le_bytes(msg.payload[8..16].try_into().unwrap_or([0u8; 8]));
1309
1310        if let Some(listener) = self.tcp_listeners.get(&file_id).copied() {
1311            return self.handle_tcp_read(msg.sender, listener);
1312        }
1313        if let Some(conn) = self.tcp_connections.get(&file_id).copied() {
1314            return self.handle_tcp_conn_read(msg.sender, conn);
1315        }
1316        if let Some(state) = self.udp_bound.get(&file_id).copied() {
1317            return self.handle_udp_bound_read(msg.sender, state);
1318        }
1319        if let Some(conn) = self.udp_connections.get(&file_id).copied() {
1320            return self.handle_udp_conn_read(msg.sender, conn);
1321        }
1322
1323        let path = match self.open_handles.get(&file_id) {
1324            Some(p) => p.clone(),
1325            None => return IpcMessage::error_reply(msg.sender, -9),
1326        };
1327
1328        let mut tmp = [0u8; 64];
1329
1330        match path.as_str() {
1331            "" => {
1332                let listing =
1333                    b"ip\naddress\nprefix\nnetmask\nbroadcast\ngateway\nroute\nroutes\ndns\ndhcp\nresolve\nping\ntcp\nudp\n";
1334                let start = (offset as usize).min(listing.len());
1335                reply_read(msg.sender, &listing[start..])
1336            }
1337            "ip" => {
1338                if let Some(ref cfg) = self.ip_config {
1339                    let n = ipv4_cidr_to_str(&cfg.address, &mut tmp);
1340                    let start = (offset as usize).min(n);
1341                    reply_read(msg.sender, &tmp[start..n])
1342                } else {
1343                    reply_read(msg.sender, b"0.0.0.0/0\n")
1344                }
1345            }
1346            "address" => {
1347                if let Some(ref cfg) = self.ip_config {
1348                    let n = ipv4_addr_to_str(&cfg.host, &mut tmp);
1349                    let start = (offset as usize).min(n);
1350                    reply_read(msg.sender, &tmp[start..n])
1351                } else {
1352                    reply_read(msg.sender, b"0.0.0.0\n")
1353                }
1354            }
1355            "prefix" => {
1356                if let Some(ref cfg) = self.ip_config {
1357                    let n = u8_to_str(cfg.prefix_len, &mut tmp);
1358                    let start = (offset as usize).min(n);
1359                    reply_read(msg.sender, &tmp[start..n])
1360                } else {
1361                    reply_read(msg.sender, b"0\n")
1362                }
1363            }
1364            "netmask" => {
1365                if let Some(ref cfg) = self.ip_config {
1366                    let n = ipv4_addr_to_str(&cfg.netmask, &mut tmp);
1367                    let start = (offset as usize).min(n);
1368                    reply_read(msg.sender, &tmp[start..n])
1369                } else {
1370                    reply_read(msg.sender, b"0.0.0.0\n")
1371                }
1372            }
1373            "broadcast" => {
1374                if let Some(ref cfg) = self.ip_config {
1375                    let n = ipv4_addr_to_str(&cfg.broadcast, &mut tmp);
1376                    let start = (offset as usize).min(n);
1377                    reply_read(msg.sender, &tmp[start..n])
1378                } else {
1379                    reply_read(msg.sender, b"0.0.0.0\n")
1380                }
1381            }
1382            "gateway" => {
1383                if let Some(ref cfg) = self.ip_config {
1384                    if let Some(gw) = cfg.gateway {
1385                        let n = ipv4_addr_to_str(&gw, &mut tmp);
1386                        let start = (offset as usize).min(n);
1387                        return reply_read(msg.sender, &tmp[start..n]);
1388                    }
1389                }
1390                reply_read(msg.sender, b"0.0.0.0\n")
1391            }
1392            "dns" => {
1393                if let Some(ref cfg) = self.ip_config {
1394                    let n = dns_list_to_str(&cfg.dns, &mut tmp);
1395                    let start = (offset as usize).min(n);
1396                    return reply_read(msg.sender, &tmp[start..n]);
1397                }
1398                reply_read(msg.sender, b"0.0.0.0\n")
1399            }
1400            "dhcp" => {
1401                if self.dhcp_enabled {
1402                    reply_read(msg.sender, b"on\n")
1403                } else {
1404                    reply_read(msg.sender, b"off\n")
1405                }
1406            }
1407            "route" => {
1408                if let Some(ref cfg) = self.ip_config {
1409                    if let Some(gw) = cfg.gateway {
1410                        let n = route_to_str(&gw, &mut tmp);
1411                        let start = (offset as usize).min(n);
1412                        return reply_read(msg.sender, &tmp[start..n]);
1413                    }
1414                }
1415                reply_read(msg.sender, b"none\n")
1416            }
1417            "routes" => {
1418                use core::fmt::Write;
1419                let mut out = [0u8; 256];
1420                let n = {
1421                    let mut w = BufWriter {
1422                        buf: &mut out,
1423                        pos: 0,
1424                    };
1425                    let mut any = false;
1426                    self.interface.routes_mut().update(|table| {
1427                        for r in table.iter() {
1428                            let (IpCidr::Ipv4(c), IpAddress::Ipv4(gw)) = (r.cidr, r.via_router);
1429                            let ca = c.address().octets();
1430                            let ga = gw.octets();
1431                            let _ = write!(
1432                                w,
1433                                "{}.{}.{}.{}/{} via {}.{}.{}.{}\n",
1434                                ca[0],
1435                                ca[1],
1436                                ca[2],
1437                                ca[3],
1438                                c.prefix_len(),
1439                                ga[0],
1440                                ga[1],
1441                                ga[2],
1442                                ga[3]
1443                            );
1444                            any = true;
1445                        }
1446                    });
1447                    if !any {
1448                        let _ = write!(w, "none\n");
1449                    }
1450                    w.pos
1451                };
1452                let start = (offset as usize).min(n);
1453                reply_read(msg.sender, &out[start..n])
1454            }
1455            "resolve" => reply_read(msg.sender, b"use /net/resolve/<hostname>\n"),
1456            "tcp" => reply_read(
1457                msg.sender,
1458                b"use /net/tcp/listen/<port>, /net/tcp/listen-once/<port>, /net/tcp/connect/<ip>/<port>[/<local_port>], /net/tcp/listeners, /net/tcp/connections, /net/tcp/stats\n",
1459            ),
1460            "tcp/listeners" => {
1461                use core::fmt::Write;
1462                let mut out = [0u8; 512];
1463                let n = {
1464                    let mut w = BufWriter {
1465                        buf: &mut out,
1466                        pos: 0,
1467                    };
1468                    if self.tcp_listeners.is_empty() {
1469                        let _ = write!(w, "none\n");
1470                    } else {
1471                        for (fid, listener) in self.tcp_listeners.iter() {
1472                            let socket = self.sockets.get::<tcp::Socket>(listener.socket);
1473                            let mode = if listener.auto_relisten { "auto" } else { "once" };
1474                            let _ = write!(
1475                                w,
1476                                "fid={} port={} state={} mode={}\n",
1477                                fid,
1478                                listener.port,
1479                                Self::tcp_state_name(socket.state()),
1480                                mode
1481                            );
1482                        }
1483                    }
1484                    w.pos
1485                };
1486                let start = (offset as usize).min(n);
1487                reply_read(msg.sender, &out[start..n])
1488            }
1489            "tcp/connections" => {
1490                use core::fmt::Write;
1491                let mut out = [0u8; 768];
1492                let n = {
1493                    let mut w = BufWriter {
1494                        buf: &mut out,
1495                        pos: 0,
1496                    };
1497                    if self.tcp_connections.is_empty() {
1498                        let _ = write!(w, "none\n");
1499                    } else {
1500                        for (fid, conn) in self.tcp_connections.iter() {
1501                            let socket = self.sockets.get::<tcp::Socket>(conn.socket);
1502                            let local = socket.local_endpoint();
1503                            let remote = socket.remote_endpoint();
1504                            match (local, remote) {
1505                                (Some(l), Some(r)) => {
1506                                    let _ = write!(
1507                                        w,
1508                                        "fid={} local={} remote={} state={}\n",
1509                                        fid,
1510                                        l,
1511                                        r,
1512                                        Self::tcp_state_name(socket.state())
1513                                    );
1514                                }
1515                                _ => {
1516                                    let _ = write!(
1517                                        w,
1518                                        "fid={} local_port={} remote={} state={}\n",
1519                                        fid,
1520                                        conn.local_port,
1521                                        conn.remote,
1522                                        Self::tcp_state_name(socket.state())
1523                                    );
1524                                }
1525                            }
1526                        }
1527                    }
1528                    w.pos
1529                };
1530                let start = (offset as usize).min(n);
1531                reply_read(msg.sender, &out[start..n])
1532            }
1533            "tcp/stats" => {
1534                use core::fmt::Write;
1535                let mut out = [0u8; 256];
1536                let n = {
1537                    let mut w = BufWriter {
1538                        buf: &mut out,
1539                        pos: 0,
1540                    };
1541                    let listeners = self.tcp_listeners.len();
1542                    let connections = self.tcp_connections.len();
1543                    let mut established = 0usize;
1544                    let mut connecting = 0usize;
1545                    let mut closing = 0usize;
1546                    for conn in self.tcp_connections.values() {
1547                        let socket = self.sockets.get::<tcp::Socket>(conn.socket);
1548                        match socket.state() {
1549                            tcp::State::Established => established += 1,
1550                            tcp::State::SynSent | tcp::State::SynReceived => connecting += 1,
1551                            tcp::State::Closed => {}
1552                            _ => closing += 1,
1553                        }
1554                    }
1555                    let _ = write!(
1556                        w,
1557                        "listeners={}\nconnections={}\nestablished={}\nconnecting={}\nclosing={}\n",
1558                        listeners,
1559                        connections,
1560                        established,
1561                        connecting,
1562                        closing
1563                    );
1564                    w.pos
1565                };
1566                let start = (offset as usize).min(n);
1567                reply_read(msg.sender, &out[start..n])
1568            }
1569            "udp" => reply_read(
1570                msg.sender,
1571                b"use /net/udp/bind/<port>, /net/udp/connect/<ip>/<port> or /net/udp/send/<ip>/<port>\n",
1572            ),
1573            p if p.starts_with("resolve/") => {
1574                let name = &p[8..];
1575                match self.resolve_hostname_blocking(name) {
1576                    Ok(addr) => {
1577                        let n = ipv4_addr_to_str(&addr, &mut tmp);
1578                        let start = (offset as usize).min(n);
1579                        reply_read(msg.sender, &tmp[start..n])
1580                    }
1581                    Err(e) => IpcMessage::error_reply(msg.sender, e),
1582                }
1583            }
1584            p if p.starts_with("ping/") => {
1585                if let Some((seq, rtt_us)) = self.ping_reply.take() {
1586                    let mut buf = [0u8; 10];
1587                    buf[0..2].copy_from_slice(&seq.to_le_bytes());
1588                    buf[2..10].copy_from_slice(&rtt_us.to_le_bytes());
1589                    let start = (offset as usize).min(buf.len());
1590                    reply_read(msg.sender, &buf[start..])
1591                } else {
1592                    reply_read(msg.sender, &[])
1593                }
1594            }
1595            _ => IpcMessage::error_reply(msg.sender, -9),
1596        }
1597    }
1598
1599    /// Implements handle write.
1600    fn handle_write(&mut self, msg: &IpcMessage) -> IpcMessage {
1601        let file_id = u64::from_le_bytes(msg.payload[0..8].try_into().unwrap_or([0u8; 8]));
1602
1603        if let Some(listener) = self.tcp_listeners.get(&file_id).copied() {
1604            return self.handle_tcp_write(msg.sender, listener, msg);
1605        }
1606        if let Some(conn) = self.tcp_connections.get(&file_id).copied() {
1607            return self.handle_tcp_conn_write(msg.sender, conn, msg);
1608        }
1609        if let Some(state) = self.udp_bound.get(&file_id).copied() {
1610            return self.handle_udp_bound_write(msg.sender, state, msg);
1611        }
1612        if let Some(conn) = self.udp_connections.get(&file_id).copied() {
1613            return self.handle_udp_conn_write(msg.sender, conn, msg);
1614        }
1615
1616        let path = match self.open_handles.get(&file_id) {
1617            Some(p) => p.clone(),
1618            None => return IpcMessage::error_reply(msg.sender, -9),
1619        };
1620
1621        if path.starts_with("ping/") {
1622            let ip_str = &path[5..];
1623            if let Some(target) = parse_ipv4(ip_str) {
1624                let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1625                let seq = if data_len >= 2 {
1626                    u16::from_le_bytes([msg.payload[18], msg.payload[19]])
1627                } else {
1628                    0
1629                };
1630                if self.send_ping(target, seq) {
1631                    return reply_write(msg.sender, data_len);
1632                }
1633                return IpcMessage::error_reply(msg.sender, -11); // EAGAIN
1634            }
1635            return IpcMessage::error_reply(msg.sender, -22);
1636        }
1637
1638        if let Some(cidr_s) = path.strip_prefix("ip/set/") {
1639            let Some(cidr) = parse_ipv4_cidr(cidr_s) else {
1640                return IpcMessage::error_reply(msg.sender, -22);
1641            };
1642            self.dhcp_enabled = false;
1643            let (gateway, dns) = if let Some(ref cfg) = self.ip_config {
1644                (cfg.gateway, cfg.dns)
1645            } else {
1646                (None, [None; 3])
1647            };
1648            self.apply_ipv4_config(cidr, gateway, dns);
1649            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1650            return reply_write(msg.sender, data_len);
1651        }
1652
1653        if let Some(rest) = path.strip_prefix("dns/set/") {
1654            let mut parts = rest.split('/');
1655            let Some(idx_s) = parts.next() else {
1656                return IpcMessage::error_reply(msg.sender, -22);
1657            };
1658            let Some(ip_s) = parts.next() else {
1659                return IpcMessage::error_reply(msg.sender, -22);
1660            };
1661            if parts.next().is_some() {
1662                return IpcMessage::error_reply(msg.sender, -22);
1663            }
1664            let Some(idx) = idx_s.parse::<usize>().ok() else {
1665                return IpcMessage::error_reply(msg.sender, -22);
1666            };
1667            if idx >= 3 {
1668                return IpcMessage::error_reply(msg.sender, -22);
1669            }
1670            let Some(ip) = parse_ipv4(ip_s) else {
1671                return IpcMessage::error_reply(msg.sender, -22);
1672            };
1673            let Some(ref mut cfg) = self.ip_config else {
1674                return IpcMessage::error_reply(msg.sender, -11);
1675            };
1676            self.dhcp_enabled = false;
1677            cfg.dns[idx] = if ip == Ipv4Address::new(0, 0, 0, 0) {
1678                None
1679            } else {
1680                Some(ip)
1681            };
1682            self.refresh_dns_servers();
1683            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1684            return reply_write(msg.sender, data_len);
1685        }
1686
1687        if path == "dhcp/enable" {
1688            self.dhcp_enabled = true;
1689            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1690            return reply_write(msg.sender, data_len);
1691        }
1692
1693        if path == "dhcp/disable" {
1694            self.dhcp_enabled = false;
1695            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1696            return reply_write(msg.sender, data_len);
1697        }
1698
1699        if let Some(rest) = path.strip_prefix("route/add/") {
1700            let mut parts = rest.split('/');
1701            let Some(cidr_s) = parts.next() else {
1702                return IpcMessage::error_reply(msg.sender, -22);
1703            };
1704            let Some(gw_s) = parts.next() else {
1705                return IpcMessage::error_reply(msg.sender, -22);
1706            };
1707            if parts.next().is_some() {
1708                return IpcMessage::error_reply(msg.sender, -22);
1709            }
1710            let Some(cidr) = parse_ipv4_cidr(cidr_s) else {
1711                return IpcMessage::error_reply(msg.sender, -22);
1712            };
1713            let Some(gw) = parse_ipv4(gw_s) else {
1714                return IpcMessage::error_reply(msg.sender, -22);
1715            };
1716            let mut full = false;
1717            self.interface.routes_mut().update(|table| {
1718                if let Some((idx, _)) = table
1719                    .iter()
1720                    .enumerate()
1721                    .find(|(_, r)| r.cidr == IpCidr::Ipv4(cidr))
1722                {
1723                    let _ = table.remove(idx);
1724                }
1725                if table
1726                    .push(smoltcp::iface::Route {
1727                        cidr: IpCidr::Ipv4(cidr),
1728                        via_router: IpAddress::Ipv4(gw),
1729                        preferred_until: None,
1730                        expires_at: None,
1731                    })
1732                    .is_err()
1733                {
1734                    full = true;
1735                }
1736            });
1737            if full {
1738                return IpcMessage::error_reply(msg.sender, -28);
1739            }
1740            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1741            return reply_write(msg.sender, data_len);
1742        }
1743
1744        if let Some(rest) = path.strip_prefix("route/del/") {
1745            let Some(cidr) = parse_ipv4_cidr(rest) else {
1746                return IpcMessage::error_reply(msg.sender, -22);
1747            };
1748            let mut removed = false;
1749            self.interface.routes_mut().update(|table| {
1750                if let Some((idx, _)) = table
1751                    .iter()
1752                    .enumerate()
1753                    .find(|(_, r)| r.cidr == IpCidr::Ipv4(cidr))
1754                {
1755                    let _ = table.remove(idx);
1756                    removed = true;
1757                }
1758            });
1759            if !removed {
1760                return IpcMessage::error_reply(msg.sender, -2);
1761            }
1762            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1763            return reply_write(msg.sender, data_len);
1764        }
1765
1766        if let Some(gw_s) = path.strip_prefix("route/default/set/") {
1767            let Some(gw) = parse_ipv4(gw_s) else {
1768                return IpcMessage::error_reply(msg.sender, -22);
1769            };
1770            if self
1771                .interface
1772                .routes_mut()
1773                .add_default_ipv4_route(gw)
1774                .is_err()
1775            {
1776                return IpcMessage::error_reply(msg.sender, -28);
1777            }
1778            if let Some(ref mut cfg) = self.ip_config {
1779                cfg.gateway = Some(gw);
1780            }
1781            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1782            return reply_write(msg.sender, data_len);
1783        }
1784
1785        if path == "route/default/clear" {
1786            let _ = self.interface.routes_mut().remove_default_ipv4_route();
1787            if let Some(ref mut cfg) = self.ip_config {
1788                cfg.gateway = None;
1789            }
1790            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
1791            return reply_write(msg.sender, data_len);
1792        }
1793
1794        IpcMessage::error_reply(msg.sender, -1) // EPERM
1795    }
1796
1797    /// Implements handle close.
1798    fn handle_close(&mut self, msg: &IpcMessage) -> IpcMessage {
1799        let file_id = u64::from_le_bytes(msg.payload[0..8].try_into().unwrap_or([0u8; 8]));
1800        self.open_handles.remove(&file_id);
1801        if let Some(listener) = self.tcp_listeners.remove(&file_id) {
1802            let _ = self.sockets.remove(listener.socket);
1803        }
1804        if let Some(conn) = self.tcp_connections.remove(&file_id) {
1805            let sock = self.sockets.get_mut::<tcp::Socket>(conn.socket);
1806            sock.close();
1807            self.sockets.remove(conn.socket);
1808        }
1809        if let Some(state) = self.udp_bound.remove(&file_id) {
1810            let sock = self.sockets.get_mut::<udp::Socket>(state.socket);
1811            sock.close();
1812            self.sockets.remove(state.socket);
1813        }
1814        if let Some(conn) = self.udp_connections.remove(&file_id) {
1815            let sock = self.sockets.get_mut::<udp::Socket>(conn.socket);
1816            sock.close();
1817            self.sockets.remove(conn.socket);
1818        }
1819        reply_ok(msg.sender)
1820    }
1821
1822    /// Implements alloc fid.
1823    fn alloc_fid(&mut self) -> u64 {
1824        let id = self.next_fid;
1825        self.next_fid += 1;
1826        id
1827    }
1828
1829    // -----------------------------------------------------------------------
1830    // Main event loop
1831    // -----------------------------------------------------------------------
1832
1833    /// Implements serve.
1834    fn serve(&mut self, port: u64) -> ! {
1835        log("[strate-net] Starting DHCP...\n");
1836
1837        loop {
1838            // 1. Drive the smoltcp stack (transmits queued packets, processes received ones)
1839            let now = now_instant();
1840            let poll_result = self
1841                .interface
1842                .poll(now, &mut self.device, &mut self.sockets);
1843
1844            // 2. Check DHCP and ICMP state machines
1845            self.process_dhcp();
1846            self.process_icmp();
1847
1848            // 3. Handle IPC messages from other strates / VFS callers (non-blocking)
1849            let mut msg = IpcMessage::new(0);
1850            let mut got_ipc = false;
1851            if ipc_try_recv(port, &mut msg).is_ok() {
1852                got_ipc = true;
1853                let reply = match msg.msg_type {
1854                    OPCODE_OPEN => self.handle_open(&msg),
1855                    OPCODE_READ => self.handle_read(&msg),
1856                    OPCODE_WRITE => self.handle_write(&msg),
1857                    OPCODE_CLOSE => self.handle_close(&msg),
1858                    _ => IpcMessage::error_reply(msg.sender, -22), // EINVAL
1859                };
1860                let _ = call::ipc_reply(&reply);
1861            }
1862
1863            // 4. Brief sleep when idle — capped to stay responsive to IPC
1864            if !got_ipc && poll_result == smoltcp::iface::PollResult::None {
1865                const MAX_SLEEP_US: u64 = 10_000; // 10 ms
1866                if let Some(delay) = self.interface.poll_delay(now, &self.sockets) {
1867                    let micros = delay.total_micros().min(MAX_SLEEP_US);
1868                    if micros > 0 {
1869                        sleep_micros(micros);
1870                    } else {
1871                        let _ = proc_yield();
1872                    }
1873                } else {
1874                    sleep_micros(MAX_SLEEP_US);
1875                }
1876            }
1877        }
1878    }
1879}
1880
1881/// Implements log.
1882fn log(msg: &str) {
1883    let _ = call::debug_log(msg.as_bytes());
1884}
1885
1886#[unsafe(no_mangle)]
1887/// Implements start.
1888pub extern "C" fn _start() -> ! {
1889    log("[strate-net] Starting network silo\n");
1890
1891    let port = match call::ipc_create_port(0) {
1892        Ok(p) => p as u64,
1893        Err(e) => {
1894            log("[strate-net] Failed to create port: ");
1895            log_error_code(e);
1896            log("\n");
1897            exit(1);
1898        }
1899    };
1900
1901    if let Err(e) = call::ipc_bind_port(port as usize, b"/net") {
1902        log("[strate-net] Failed to bind to /net: ");
1903        log_error_code(e);
1904        log("\n");
1905        exit(2);
1906    }
1907
1908    log("[strate-net] Bound to /net\n");
1909
1910    let mut mac = [0u8; 6];
1911    if net_info(0, &mut mac).is_err() {
1912        log("[strate-net] No NIC found, using fallback MAC\n");
1913        mac = [0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
1914    } else {
1915        log("[strate-net] MAC acquired from kernel\n");
1916    }
1917
1918    let mut strate = NetworkStrate::new(mac);
1919    strate.serve(port);
1920}
1921
1922/// Implements log error code.
1923fn log_error_code(e: strate_net::syscalls::Error) {
1924    use core::fmt::Write;
1925    let mut buf = [0u8; 32];
1926    let n = {
1927        let mut w = BufWriter {
1928            buf: &mut buf,
1929            pos: 0,
1930        };
1931        let _ = write!(w, "{}", e.to_errno());
1932        w.pos
1933    };
1934    if let Ok(s) = core::str::from_utf8(&buf[..n]) {
1935        log(s);
1936    }
1937}