Skip to main content

strate_net_silo/
vfs.rs

1use alloc::vec::Vec;
2use strate_net::syscalls::call;
3
4use smoltcp::{
5    iface::Route,
6    socket::{tcp, udp},
7    wire::{IpAddress, IpEndpoint, Ipv4Address},
8};
9use strat9_syscall::data::IPC_FILE_FLAG_DIRECTORY;
10use strate_net::IpcMessage;
11
12use crate::{
13    ip::{parse_ip, parse_ip_cidr, parse_ipv4, parse_ipv4_cidr, parse_ipv6, parse_ipv6_cidr},
14    ipc::{
15        message_file_id, open_path, read_request, reply_ok, reply_open, reply_read, reply_write,
16        reply_write_data_len, write_data, HANDLE_FLAG_CONTROL, HANDLE_FLAG_DATAGRAM,
17        HANDLE_FLAG_INFO, HANDLE_FLAG_STREAM,
18    },
19    state::{
20        NetworkStrate, OpenedFile, TcpConnState, TcpListenerState, UdpBoundState, UdpConnState,
21    },
22};
23
24impl NetworkStrate {
25    fn insert_open_handle(&mut self, path: &str) -> u64 {
26        let fid = self.alloc_fid();
27        self.open_handles.insert(fid, OpenedFile::new(path));
28        fid
29    }
30
31    fn reply_open_path(&mut self, sender: u64, path: &str, flags: u32) -> IpcMessage {
32        let fid = self.insert_open_handle(path);
33        reply_open(sender, fid, u64::MAX, flags)
34    }
35
36    fn open_tcp_listener_handle(
37        &mut self,
38        sender: u64,
39        path: &str,
40        port: u16,
41        auto_relisten: bool,
42    ) -> IpcMessage {
43        let rx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 16384]);
44        let tx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 16384]);
45        let mut socket = tcp::Socket::new(rx_buf, tx_buf);
46        if socket.listen(port).is_err() {
47            return IpcMessage::error_reply(sender, -98);
48        }
49        let socket = self.sockets.add(socket);
50        let fid = self.insert_open_handle(path);
51        self.tcp_listeners.insert(
52            fid,
53            TcpListenerState {
54                socket,
55                port,
56                auto_relisten,
57            },
58        );
59        reply_open(sender, fid, u64::MAX, HANDLE_FLAG_STREAM)
60    }
61
62    fn open_tcp_connection_handle(
63        &mut self,
64        sender: u64,
65        path: &str,
66        remote_ip: IpAddress,
67        remote_port: u16,
68        local_port: u16,
69    ) -> IpcMessage {
70        let rx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 16384]);
71        let tx_buf = tcp::SocketBuffer::new(alloc::vec![0u8; 16384]);
72        let socket = tcp::Socket::new(rx_buf, tx_buf);
73        let handle = self.sockets.add(socket);
74        if self
75            .sockets
76            .get_mut::<tcp::Socket>(handle)
77            .connect(
78                self.interface.context(),
79                (remote_ip, remote_port),
80                local_port,
81            )
82            .is_err()
83        {
84            self.sockets.remove(handle);
85            return IpcMessage::error_reply(sender, -111);
86        }
87
88        let fid = self.insert_open_handle(path);
89        self.tcp_connections.insert(
90            fid,
91            TcpConnState {
92                socket: handle,
93                local_port,
94                remote: IpEndpoint::new(remote_ip, remote_port),
95            },
96        );
97        reply_open(sender, fid, u64::MAX, HANDLE_FLAG_STREAM)
98    }
99
100    fn open_udp_connection_handle(
101        &mut self,
102        sender: u64,
103        path: &str,
104        remote: IpEndpoint,
105    ) -> IpcMessage {
106        let Some(local_port) = self.alloc_udp_ephemeral_port() else {
107            return IpcMessage::error_reply(sender, -28);
108        };
109        let Ok(socket) = self.create_udp_socket(local_port) else {
110            return IpcMessage::error_reply(sender, -98);
111        };
112
113        let fid = self.insert_open_handle(path);
114        self.udp_connections.insert(
115            fid,
116            UdpConnState {
117                socket,
118                local_port,
119                remote,
120            },
121        );
122        reply_open(sender, fid, u64::MAX, HANDLE_FLAG_DATAGRAM)
123    }
124
125    pub(crate) fn handle_open(&mut self, msg: &IpcMessage) -> IpcMessage {
126        let path = match open_path(msg) {
127            Ok(path) => path,
128            Err(err) => return IpcMessage::error_reply(msg.sender, err),
129        };
130
131        match path {
132            "" => {
133                let fid = self.insert_open_handle("");
134                reply_open(msg.sender, fid, u64::MAX, IPC_FILE_FLAG_DIRECTORY)
135            }
136            "ip" | "address" | "prefix" | "netmask" | "broadcast" | "gateway" | "route"
137            | "routes" | "dns" | "resolve" | "ping" | "tcp" | "tcp/listeners"
138            | "tcp/connections" | "tcp/stats" | "udp" | "dhcp" | "ip6" | "ip6/address"
139            | "ip6/gateway" => self.reply_open_path(msg.sender, path, HANDLE_FLAG_INFO),
140            path if path.starts_with("resolve/") => {
141                if path.len() <= 8 {
142                    return IpcMessage::error_reply(msg.sender, -22);
143                }
144                self.reply_open_path(msg.sender, path, HANDLE_FLAG_INFO)
145            }
146            path if path.starts_with("ping/") => {
147                if parse_ipv4(&path[5..]).is_none() {
148                    return IpcMessage::error_reply(msg.sender, -22);
149                }
150                self.reply_open_path(msg.sender, path, HANDLE_FLAG_CONTROL)
151            }
152            path if path.starts_with("ping6/") => {
153                if parse_ipv6(&path[6..]).is_none() {
154                    return IpcMessage::error_reply(msg.sender, -22);
155                }
156                self.reply_open_path(msg.sender, path, HANDLE_FLAG_CONTROL)
157            }
158            path if path.starts_with("tcp/connect/") => {
159                let rest = &path[12..];
160                let parts: Vec<&str> = rest.split('/').collect();
161                if parts.len() < 2 || parts.len() > 3 {
162                    return IpcMessage::error_reply(msg.sender, -22);
163                }
164                let Some(remote_ip) = parse_ip(parts[0]) else {
165                    return IpcMessage::error_reply(msg.sender, -22);
166                };
167                let Some(remote_port) = parts[1].parse::<u16>().ok() else {
168                    return IpcMessage::error_reply(msg.sender, -22);
169                };
170                if remote_port == 0 {
171                    return IpcMessage::error_reply(msg.sender, -22);
172                }
173                let local_port = if parts.len() == 3 {
174                    let Some(local_port) = parts[2].parse::<u16>().ok() else {
175                        return IpcMessage::error_reply(msg.sender, -22);
176                    };
177                    if local_port == 0 {
178                        return IpcMessage::error_reply(msg.sender, -22);
179                    }
180                    local_port
181                } else {
182                    49152 + (self.next_fid as u16 % 16384)
183                };
184                self.open_tcp_connection_handle(
185                    msg.sender,
186                    path,
187                    remote_ip,
188                    remote_port,
189                    local_port,
190                )
191            }
192            path if path.starts_with("tcp/listen-once/") => {
193                let Some(port) = path[16..].parse::<u16>().ok() else {
194                    return IpcMessage::error_reply(msg.sender, -22);
195                };
196                if port == 0 {
197                    return IpcMessage::error_reply(msg.sender, -22);
198                }
199                self.open_tcp_listener_handle(msg.sender, path, port, false)
200            }
201            path if path.starts_with("tcp/listen/") => {
202                let Some(port) = path[11..].parse::<u16>().ok() else {
203                    return IpcMessage::error_reply(msg.sender, -22);
204                };
205                if port == 0 {
206                    return IpcMessage::error_reply(msg.sender, -22);
207                }
208                self.open_tcp_listener_handle(msg.sender, path, port, true)
209            }
210            path if path.starts_with("udp/bind/") => {
211                let Some(port) = path[9..].parse::<u16>().ok() else {
212                    return IpcMessage::error_reply(msg.sender, -22);
213                };
214                if port == 0 || self.udp_port_in_use(port) {
215                    return IpcMessage::error_reply(msg.sender, -98);
216                }
217                let Ok(socket) = self.create_udp_socket(port) else {
218                    return IpcMessage::error_reply(msg.sender, -98);
219                };
220                let fid = self.insert_open_handle(path);
221                self.udp_bound.insert(
222                    fid,
223                    UdpBoundState {
224                        socket,
225                        local_port: port,
226                        last_peer: None,
227                    },
228                );
229                reply_open(msg.sender, fid, u64::MAX, HANDLE_FLAG_DATAGRAM)
230            }
231            path if path.starts_with("udp/connect/") => {
232                let rest = &path[12..];
233                let parts: Vec<&str> = rest.splitn(2, '/').collect();
234                if parts.len() != 2 {
235                    return IpcMessage::error_reply(msg.sender, -22);
236                }
237                let Some(remote_ip) = parse_ip(parts[0]) else {
238                    return IpcMessage::error_reply(msg.sender, -22);
239                };
240                let Some(remote_port) = parts[1].parse::<u16>().ok() else {
241                    return IpcMessage::error_reply(msg.sender, -22);
242                };
243                if remote_port == 0 {
244                    return IpcMessage::error_reply(msg.sender, -22);
245                }
246                self.open_udp_connection_handle(
247                    msg.sender,
248                    path,
249                    IpEndpoint::new(remote_ip, remote_port),
250                )
251            }
252            path if path.starts_with("udp/send/") => {
253                let rest = &path[9..];
254                let parts: Vec<&str> = rest.splitn(2, '/').collect();
255                if parts.len() != 2 {
256                    return IpcMessage::error_reply(msg.sender, -22);
257                }
258                let Some(remote_ip) = parse_ip(parts[0]) else {
259                    return IpcMessage::error_reply(msg.sender, -22);
260                };
261                let Some(remote_port) = parts[1].parse::<u16>().ok() else {
262                    return IpcMessage::error_reply(msg.sender, -22);
263                };
264                if remote_port == 0 {
265                    return IpcMessage::error_reply(msg.sender, -22);
266                }
267                self.open_udp_connection_handle(
268                    msg.sender,
269                    path,
270                    IpEndpoint::new(remote_ip, remote_port),
271                )
272            }
273            path if path.starts_with("route/add/")
274                || path.starts_with("route/del/")
275                || path.starts_with("route/default/set/")
276                || path == "route/default/clear" =>
277            {
278                self.reply_open_path(msg.sender, path, HANDLE_FLAG_CONTROL)
279            }
280            path if path.starts_with("ip/set/")
281                || path.starts_with("ip6/set/")
282                || path.starts_with("dns/set/")
283                || path == "dhcp/enable"
284                || path == "dhcp/disable" =>
285            {
286                self.reply_open_path(msg.sender, path, HANDLE_FLAG_CONTROL)
287            }
288            _ => IpcMessage::error_reply(msg.sender, -2),
289        }
290    }
291
292    pub(crate) fn handle_read(&mut self, msg: &IpcMessage) -> IpcMessage {
293        let (file_id, offset, requested) = read_request(msg);
294
295        if let Some(listener) = self.tcp_listeners.get(&file_id).copied() {
296            return self.handle_tcp_read(msg.sender, listener, requested);
297        }
298        if let Some(conn) = self.tcp_connections.get(&file_id).copied() {
299            return self.handle_tcp_conn_read(msg.sender, conn, requested);
300        }
301        if self.udp_bound.contains_key(&file_id) {
302            return self.handle_udp_bound_read(msg.sender, file_id, requested);
303        }
304        if let Some(conn) = self.udp_connections.get(&file_id).copied() {
305            return self.handle_udp_conn_read(msg.sender, file_id, conn, requested);
306        }
307
308        let path = match self.open_handles.get(&file_id) {
309            Some(handle) => handle.path.clone(),
310            None => return IpcMessage::error_reply(msg.sender, -9),
311        };
312
313        let is_ping_path = path.starts_with("ping/") || path.starts_with("ping6/");
314
315        let needs_refresh = offset == 0
316            || self
317                .open_handles
318                .get(&file_id)
319                .and_then(|handle| handle.cached_content.as_ref())
320                .is_none()
321            // Ping paths return dynamic content : one reply per read.
322            // Never serve stale cached data.
323            || is_ping_path;
324
325        if needs_refresh {
326            match self.generate_content(file_id, &path) {
327                Ok(content) => {
328                    if let Some(handle) = self.open_handles.get_mut(&file_id) {
329                        handle.cached_content = Some(content);
330                    }
331                }
332                Err(err) => return IpcMessage::error_reply(msg.sender, err),
333            }
334        }
335
336        let cached = match self
337            .open_handles
338            .get(&file_id)
339            .and_then(|handle| handle.cached_content.as_ref())
340        {
341            Some(content) => content,
342            None => return IpcMessage::error_reply(msg.sender, -9),
343        };
344
345        // Ping endpoints behave like a dequeue, not a seekable file. Reads must
346        // ignore the shared file offset because the preceding write advances it.
347        let start = if is_ping_path {
348            0
349        } else {
350            (offset as usize).min(cached.len())
351        };
352        reply_read(msg.sender, &cached[start..])
353    }
354
355    pub(crate) fn handle_write(&mut self, msg: &IpcMessage) -> IpcMessage {
356        let file_id = message_file_id(msg);
357
358        if let Some(listener) = self.tcp_listeners.get(&file_id).copied() {
359            return self.handle_tcp_write(msg.sender, listener, msg);
360        }
361        if let Some(conn) = self.tcp_connections.get(&file_id).copied() {
362            return self.handle_tcp_conn_write(msg.sender, conn, msg);
363        }
364        if self.udp_bound.contains_key(&file_id) {
365            return self.handle_udp_bound_write(msg.sender, file_id, msg);
366        }
367        if let Some(conn) = self.udp_connections.get(&file_id).copied() {
368            return self.handle_udp_conn_write(msg.sender, conn, msg);
369        }
370
371        let path = match self.open_handles.get(&file_id) {
372            Some(handle) => handle.path.clone(),
373            None => return IpcMessage::error_reply(msg.sender, -9),
374        };
375
376        if path.starts_with("ping/") {
377            let Some(target) = parse_ipv4(&path[5..]) else {
378                return IpcMessage::error_reply(msg.sender, -22);
379            };
380            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
381            let data = write_data(msg);
382            let seq = data
383                .get(0..2)
384                .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
385                .unwrap_or(0);
386            let _ = call::debug_log(b"[ping] write ping/ target=");
387            let _ = call::debug_log(&target.octets());
388            let _ = call::debug_log(b" seq=");
389            let _ = call::debug_log(&[(seq >> 8) as u8, seq as u8]);
390            let _ = call::debug_log(b"\n");
391            if self.send_ping(target, seq, file_id) {
392                return reply_write(msg.sender, data_len);
393            }
394            let _ = call::debug_log(b"[ping] write: send_ping failed\n");
395            return IpcMessage::error_reply(msg.sender, -11);
396        }
397
398        if path.starts_with("ping6/") {
399            let Some(target) = parse_ipv6(&path[6..]) else {
400                return IpcMessage::error_reply(msg.sender, -22);
401            };
402            let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
403            let data = write_data(msg);
404            let seq = data
405                .get(0..2)
406                .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
407                .unwrap_or(0);
408            let _ = call::debug_log(b"[ping] write ping6/ target=");
409            let _ = call::debug_log(&target.octets());
410            let _ = call::debug_log(b" seq=");
411            let _ = call::debug_log(&[(seq >> 8) as u8, seq as u8]);
412            let _ = call::debug_log(b"\n");
413            if self.send_ping6(target, seq, file_id) {
414                return reply_write(msg.sender, data_len);
415            }
416            let _ = call::debug_log(b"[ping] write: send_ping6 failed\n");
417            return IpcMessage::error_reply(msg.sender, -11);
418        }
419
420        if let Some(cidr) = path.strip_prefix("ip/set/").and_then(parse_ipv4_cidr) {
421            self.dhcp_enabled = false;
422            self.dns_from_dhcp = false;
423            let gateway = self.ip_config.as_ref().and_then(|cfg| cfg.gateway);
424            self.apply_ipv4_config(cidr, gateway);
425            return reply_write_data_len(msg.sender, msg);
426        } else if path.starts_with("ip/set/") {
427            return IpcMessage::error_reply(msg.sender, -22);
428        }
429
430        if let Some(cidr) = path.strip_prefix("ip6/set/").and_then(parse_ipv6_cidr) {
431            let gateway = self.ipv6_config.as_ref().and_then(|cfg| cfg.gateway);
432            self.apply_ipv6_config(cidr, gateway);
433            return reply_write_data_len(msg.sender, msg);
434        } else if path.starts_with("ip6/set/") {
435            return IpcMessage::error_reply(msg.sender, -22);
436        }
437
438        if let Some(rest) = path.strip_prefix("dns/set/") {
439            let mut parts = rest.split('/');
440            let Some(idx_s) = parts.next() else {
441                return IpcMessage::error_reply(msg.sender, -22);
442            };
443            let Some(ip_s) = parts.next() else {
444                return IpcMessage::error_reply(msg.sender, -22);
445            };
446            if parts.next().is_some() {
447                return IpcMessage::error_reply(msg.sender, -22);
448            }
449            let Some(idx) = idx_s.parse::<usize>().ok() else {
450                return IpcMessage::error_reply(msg.sender, -22);
451            };
452            if idx >= 3 {
453                return IpcMessage::error_reply(msg.sender, -22);
454            }
455            let Some(ip) = parse_ip(ip_s) else {
456                return IpcMessage::error_reply(msg.sender, -22);
457            };
458            self.dhcp_enabled = false;
459            self.dns_from_dhcp = false;
460            self.dns_servers[idx] = if matches!(ip, IpAddress::Ipv4(addr) if addr == Ipv4Address::new(0, 0, 0, 0))
461            {
462                None
463            } else {
464                Some(ip)
465            };
466            if let Some(ref mut cfg) = self.ip_config {
467                cfg.dns[idx] = match self.dns_servers[idx] {
468                    Some(IpAddress::Ipv4(addr)) => Some(addr),
469                    _ => None,
470                };
471            }
472            self.refresh_dns_servers();
473            return reply_write_data_len(msg.sender, msg);
474        }
475
476        if path == "dhcp/enable" {
477            self.enable_dhcp();
478            return reply_write_data_len(msg.sender, msg);
479        }
480
481        if path == "dhcp/disable" {
482            self.dhcp_enabled = false;
483            return reply_write_data_len(msg.sender, msg);
484        }
485
486        if let Some(rest) = path.strip_prefix("route/add/") {
487            let mut parts = rest.split('/');
488            let Some(cidr_s) = parts.next() else {
489                return IpcMessage::error_reply(msg.sender, -22);
490            };
491            let Some(gw_s) = parts.next() else {
492                return IpcMessage::error_reply(msg.sender, -22);
493            };
494            if parts.next().is_some() {
495                return IpcMessage::error_reply(msg.sender, -22);
496            }
497            let Some(cidr) = parse_ip_cidr(cidr_s) else {
498                return IpcMessage::error_reply(msg.sender, -22);
499            };
500            let Some(gw) = parse_ip(gw_s) else {
501                return IpcMessage::error_reply(msg.sender, -22);
502            };
503            let mut full = false;
504            self.interface.routes_mut().update(|table| {
505                if let Some((idx, _)) = table
506                    .iter()
507                    .enumerate()
508                    .find(|(_, route)| route.cidr == cidr)
509                {
510                    let _ = table.remove(idx);
511                }
512                match (cidr, gw) {
513                    (smoltcp::wire::IpCidr::Ipv4(_), IpAddress::Ipv4(_))
514                    | (smoltcp::wire::IpCidr::Ipv6(_), IpAddress::Ipv6(_)) => {
515                        if table
516                            .push(Route {
517                                cidr,
518                                via_router: gw,
519                                preferred_until: None,
520                                expires_at: None,
521                            })
522                            .is_err()
523                        {
524                            full = true;
525                        }
526                    }
527                    _ => {
528                        full = true;
529                    }
530                }
531            });
532            if full {
533                return IpcMessage::error_reply(msg.sender, -28);
534            }
535            return reply_write_data_len(msg.sender, msg);
536        }
537
538        if let Some(rest) = path.strip_prefix("route/del/") {
539            let Some(cidr) = parse_ip_cidr(rest) else {
540                return IpcMessage::error_reply(msg.sender, -22);
541            };
542            let mut removed = false;
543            self.interface.routes_mut().update(|table| {
544                if let Some((idx, _)) = table
545                    .iter()
546                    .enumerate()
547                    .find(|(_, route)| route.cidr == cidr)
548                {
549                    let _ = table.remove(idx);
550                    removed = true;
551                }
552            });
553            if !removed {
554                return IpcMessage::error_reply(msg.sender, -2);
555            }
556            return reply_write_data_len(msg.sender, msg);
557        }
558
559        if let Some(gw_s) = path.strip_prefix("route/default/set/") {
560            let Some(gw) = parse_ip(gw_s) else {
561                return IpcMessage::error_reply(msg.sender, -22);
562            };
563            match gw {
564                IpAddress::Ipv4(gw4) => {
565                    let _ = self.interface.routes_mut().remove_default_ipv4_route();
566                    if self
567                        .interface
568                        .routes_mut()
569                        .add_default_ipv4_route(gw4)
570                        .is_err()
571                    {
572                        return IpcMessage::error_reply(msg.sender, -28);
573                    }
574                    if let Some(ref mut cfg) = self.ip_config {
575                        cfg.gateway = Some(gw4);
576                    }
577                }
578                IpAddress::Ipv6(gw6) => {
579                    let _ = self.interface.routes_mut().remove_default_ipv6_route();
580                    if self
581                        .interface
582                        .routes_mut()
583                        .add_default_ipv6_route(gw6)
584                        .is_err()
585                    {
586                        return IpcMessage::error_reply(msg.sender, -28);
587                    }
588                    if let Some(ref mut cfg) = self.ipv6_config {
589                        cfg.gateway = Some(gw6);
590                    }
591                }
592            }
593            self.refresh_dns_servers();
594            return reply_write_data_len(msg.sender, msg);
595        }
596
597        if path == "route/default/clear" {
598            let _ = self.interface.routes_mut().remove_default_ipv4_route();
599            let _ = self.interface.routes_mut().remove_default_ipv6_route();
600            if let Some(ref mut cfg) = self.ip_config {
601                cfg.gateway = None;
602            }
603            if let Some(ref mut cfg) = self.ipv6_config {
604                cfg.gateway = None;
605            }
606            self.refresh_dns_servers();
607            return reply_write_data_len(msg.sender, msg);
608        }
609
610        IpcMessage::error_reply(msg.sender, -1)
611    }
612
613    pub(crate) fn handle_close(&mut self, msg: &IpcMessage) -> IpcMessage {
614        let file_id = message_file_id(msg);
615        self.open_handles.remove(&file_id);
616        if let Some(listener) = self.tcp_listeners.remove(&file_id) {
617            let _ = self.sockets.remove(listener.socket);
618        }
619        if let Some(conn) = self.tcp_connections.remove(&file_id) {
620            let socket = self.sockets.get_mut::<tcp::Socket>(conn.socket);
621            socket.close();
622            self.lingering_sockets.push(conn.socket);
623        }
624        if let Some(state) = self.udp_bound.remove(&file_id) {
625            let socket = self.sockets.get_mut::<udp::Socket>(state.socket);
626            socket.close();
627            self.sockets.remove(state.socket);
628        }
629        if let Some(conn) = self.udp_connections.remove(&file_id) {
630            let socket = self.sockets.get_mut::<udp::Socket>(conn.socket);
631            socket.close();
632            self.sockets.remove(conn.socket);
633        }
634        reply_ok(msg.sender)
635    }
636}