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, sender: u64, path: &str) -> u64 {
26 let fid = self.new_fid(sender);
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(sender, 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(sender, 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(sender, 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(sender, 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(msg.sender, "");
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(msg.sender, 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 !self.is_owner(file_id, msg.sender) {
297 return IpcMessage::error_reply(msg.sender, -9); }
299
300 if let Some(listener) = self.tcp_listeners.get(&file_id).copied() {
301 return self.handle_tcp_read(msg.sender, listener, requested);
302 }
303 if let Some(conn) = self.tcp_connections.get(&file_id).copied() {
304 return self.handle_tcp_conn_read(msg.sender, conn, requested);
305 }
306 if self.udp_bound.contains_key(&file_id) {
307 return self.handle_udp_bound_read(msg.sender, file_id, requested);
308 }
309 if let Some(conn) = self.udp_connections.get(&file_id).copied() {
310 return self.handle_udp_conn_read(msg.sender, file_id, conn, requested);
311 }
312
313 let path = match self.open_handles.get(&file_id) {
314 Some(handle) => handle.path.clone(),
315 None => return IpcMessage::error_reply(msg.sender, -9),
316 };
317
318 let is_ping_path = path.starts_with("ping/") || path.starts_with("ping6/");
319
320 let needs_refresh = offset == 0
321 || self
322 .open_handles
323 .get(&file_id)
324 .and_then(|handle| handle.cached_content.as_ref())
325 .is_none()
326 || is_ping_path;
329
330 if needs_refresh {
331 match self.generate_content(file_id, &path) {
332 Ok(content) => {
333 if let Some(handle) = self.open_handles.get_mut(&file_id) {
334 handle.cached_content = Some(content);
335 }
336 }
337 Err(err) => return IpcMessage::error_reply(msg.sender, err),
338 }
339 }
340
341 let cached = match self
342 .open_handles
343 .get(&file_id)
344 .and_then(|handle| handle.cached_content.as_ref())
345 {
346 Some(content) => content,
347 None => return IpcMessage::error_reply(msg.sender, -9),
348 };
349
350 let start = if is_ping_path {
353 0
354 } else {
355 (offset as usize).min(cached.len())
356 };
357 reply_read(msg.sender, &cached[start..])
358 }
359
360 pub(crate) fn handle_write(&mut self, msg: &IpcMessage) -> IpcMessage {
361 let file_id = message_file_id(msg);
362
363 if !self.is_owner(file_id, msg.sender) {
365 return IpcMessage::error_reply(msg.sender, -9); }
367
368 if let Some(listener) = self.tcp_listeners.get(&file_id).copied() {
369 return self.handle_tcp_write(msg.sender, listener, msg);
370 }
371 if let Some(conn) = self.tcp_connections.get(&file_id).copied() {
372 return self.handle_tcp_conn_write(msg.sender, conn, msg);
373 }
374 if self.udp_bound.contains_key(&file_id) {
375 return self.handle_udp_bound_write(msg.sender, file_id, msg);
376 }
377 if let Some(conn) = self.udp_connections.get(&file_id).copied() {
378 return self.handle_udp_conn_write(msg.sender, conn, msg);
379 }
380
381 let path = match self.open_handles.get(&file_id) {
382 Some(handle) => handle.path.clone(),
383 None => return IpcMessage::error_reply(msg.sender, -9),
384 };
385
386 if path.starts_with("ping/") {
387 let Some(target) = parse_ipv4(&path[5..]) else {
388 return IpcMessage::error_reply(msg.sender, -22);
389 };
390 let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
391 let data = write_data(msg);
392 let seq = data
393 .get(0..2)
394 .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
395 .unwrap_or(0);
396 let _ = call::debug_log(b"[ping] write ping/ target=");
397 let _ = call::debug_log(&target.octets());
398 let _ = call::debug_log(b" seq=");
399 let _ = call::debug_log(&[(seq >> 8) as u8, seq as u8]);
400 let _ = call::debug_log(b"\n");
401 if self.send_ping(target, seq, file_id) {
402 return reply_write(msg.sender, data_len);
403 }
404 let _ = call::debug_log(b"[ping] write: send_ping failed\n");
405 return IpcMessage::error_reply(msg.sender, -11);
406 }
407
408 if path.starts_with("ping6/") {
409 let Some(target) = parse_ipv6(&path[6..]) else {
410 return IpcMessage::error_reply(msg.sender, -22);
411 };
412 let data_len = u16::from_le_bytes([msg.payload[16], msg.payload[17]]) as usize;
413 let data = write_data(msg);
414 let seq = data
415 .get(0..2)
416 .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
417 .unwrap_or(0);
418 let _ = call::debug_log(b"[ping] write ping6/ target=");
419 let _ = call::debug_log(&target.octets());
420 let _ = call::debug_log(b" seq=");
421 let _ = call::debug_log(&[(seq >> 8) as u8, seq as u8]);
422 let _ = call::debug_log(b"\n");
423 if self.send_ping6(target, seq, file_id) {
424 return reply_write(msg.sender, data_len);
425 }
426 let _ = call::debug_log(b"[ping] write: send_ping6 failed\n");
427 return IpcMessage::error_reply(msg.sender, -11);
428 }
429
430 if let Some(cidr) = path.strip_prefix("ip/set/").and_then(parse_ipv4_cidr) {
431 self.dhcp_enabled = false;
432 self.dns_from_dhcp = false;
433 let gateway = self.ip_config.as_ref().and_then(|cfg| cfg.gateway);
434 self.apply_ipv4_config(cidr, gateway);
435 return reply_write_data_len(msg.sender, msg);
436 } else if path.starts_with("ip/set/") {
437 return IpcMessage::error_reply(msg.sender, -22);
438 }
439
440 if let Some(cidr) = path.strip_prefix("ip6/set/").and_then(parse_ipv6_cidr) {
441 let gateway = self.ipv6_config.as_ref().and_then(|cfg| cfg.gateway);
442 self.apply_ipv6_config(cidr, gateway);
443 return reply_write_data_len(msg.sender, msg);
444 } else if path.starts_with("ip6/set/") {
445 return IpcMessage::error_reply(msg.sender, -22);
446 }
447
448 if let Some(rest) = path.strip_prefix("dns/set/") {
449 let mut parts = rest.split('/');
450 let Some(idx_s) = parts.next() else {
451 return IpcMessage::error_reply(msg.sender, -22);
452 };
453 let Some(ip_s) = parts.next() else {
454 return IpcMessage::error_reply(msg.sender, -22);
455 };
456 if parts.next().is_some() {
457 return IpcMessage::error_reply(msg.sender, -22);
458 }
459 let Some(idx) = idx_s.parse::<usize>().ok() else {
460 return IpcMessage::error_reply(msg.sender, -22);
461 };
462 if idx >= 3 {
463 return IpcMessage::error_reply(msg.sender, -22);
464 }
465 let Some(ip) = parse_ip(ip_s) else {
466 return IpcMessage::error_reply(msg.sender, -22);
467 };
468 self.dhcp_enabled = false;
469 self.dns_from_dhcp = false;
470 self.dns_servers[idx] = if matches!(ip, IpAddress::Ipv4(addr) if addr == Ipv4Address::new(0, 0, 0, 0))
471 {
472 None
473 } else {
474 Some(ip)
475 };
476 if let Some(ref mut cfg) = self.ip_config {
477 cfg.dns[idx] = match self.dns_servers[idx] {
478 Some(IpAddress::Ipv4(addr)) => Some(addr),
479 _ => None,
480 };
481 }
482 self.refresh_dns_servers();
483 return reply_write_data_len(msg.sender, msg);
484 }
485
486 if path == "dhcp/enable" {
487 self.enable_dhcp();
488 return reply_write_data_len(msg.sender, msg);
489 }
490
491 if path == "dhcp/disable" {
492 self.dhcp_enabled = false;
493 return reply_write_data_len(msg.sender, msg);
494 }
495
496 if let Some(rest) = path.strip_prefix("route/add/") {
497 let mut parts = rest.split('/');
498 let Some(cidr_s) = parts.next() else {
499 return IpcMessage::error_reply(msg.sender, -22);
500 };
501 let Some(gw_s) = parts.next() else {
502 return IpcMessage::error_reply(msg.sender, -22);
503 };
504 if parts.next().is_some() {
505 return IpcMessage::error_reply(msg.sender, -22);
506 }
507 let Some(cidr) = parse_ip_cidr(cidr_s) else {
508 return IpcMessage::error_reply(msg.sender, -22);
509 };
510 let Some(gw) = parse_ip(gw_s) else {
511 return IpcMessage::error_reply(msg.sender, -22);
512 };
513 let mut full = false;
514 self.interface.routes_mut().update(|table| {
515 if let Some((idx, _)) = table
516 .iter()
517 .enumerate()
518 .find(|(_, route)| route.cidr == cidr)
519 {
520 let _ = table.remove(idx);
521 }
522 match (cidr, gw) {
523 (smoltcp::wire::IpCidr::Ipv4(_), IpAddress::Ipv4(_))
524 | (smoltcp::wire::IpCidr::Ipv6(_), IpAddress::Ipv6(_)) => {
525 if table
526 .push(Route {
527 cidr,
528 via_router: gw,
529 preferred_until: None,
530 expires_at: None,
531 })
532 .is_err()
533 {
534 full = true;
535 }
536 }
537 _ => {
538 full = true;
539 }
540 }
541 });
542 if full {
543 return IpcMessage::error_reply(msg.sender, -28);
544 }
545 return reply_write_data_len(msg.sender, msg);
546 }
547
548 if let Some(rest) = path.strip_prefix("route/del/") {
549 let Some(cidr) = parse_ip_cidr(rest) else {
550 return IpcMessage::error_reply(msg.sender, -22);
551 };
552 let mut removed = false;
553 self.interface.routes_mut().update(|table| {
554 if let Some((idx, _)) = table
555 .iter()
556 .enumerate()
557 .find(|(_, route)| route.cidr == cidr)
558 {
559 let _ = table.remove(idx);
560 removed = true;
561 }
562 });
563 if !removed {
564 return IpcMessage::error_reply(msg.sender, -2);
565 }
566 return reply_write_data_len(msg.sender, msg);
567 }
568
569 if let Some(gw_s) = path.strip_prefix("route/default/set/") {
570 let Some(gw) = parse_ip(gw_s) else {
571 return IpcMessage::error_reply(msg.sender, -22);
572 };
573 match gw {
574 IpAddress::Ipv4(gw4) => {
575 let _ = self.interface.routes_mut().remove_default_ipv4_route();
576 if self
577 .interface
578 .routes_mut()
579 .add_default_ipv4_route(gw4)
580 .is_err()
581 {
582 return IpcMessage::error_reply(msg.sender, -28);
583 }
584 if let Some(ref mut cfg) = self.ip_config {
585 cfg.gateway = Some(gw4);
586 }
587 }
588 IpAddress::Ipv6(gw6) => {
589 let _ = self.interface.routes_mut().remove_default_ipv6_route();
590 if self
591 .interface
592 .routes_mut()
593 .add_default_ipv6_route(gw6)
594 .is_err()
595 {
596 return IpcMessage::error_reply(msg.sender, -28);
597 }
598 if let Some(ref mut cfg) = self.ipv6_config {
599 cfg.gateway = Some(gw6);
600 }
601 }
602 }
603 self.refresh_dns_servers();
604 return reply_write_data_len(msg.sender, msg);
605 }
606
607 if path == "route/default/clear" {
608 let _ = self.interface.routes_mut().remove_default_ipv4_route();
609 let _ = self.interface.routes_mut().remove_default_ipv6_route();
610 if let Some(ref mut cfg) = self.ip_config {
611 cfg.gateway = None;
612 }
613 if let Some(ref mut cfg) = self.ipv6_config {
614 cfg.gateway = None;
615 }
616 self.refresh_dns_servers();
617 return reply_write_data_len(msg.sender, msg);
618 }
619
620 IpcMessage::error_reply(msg.sender, -1)
621 }
622
623 pub(crate) fn handle_close(&mut self, msg: &IpcMessage) -> IpcMessage {
624 let file_id = message_file_id(msg);
625
626 if !self.is_owner(file_id, msg.sender) {
629 return IpcMessage::error_reply(msg.sender, -9); }
631 self.forget_handle(file_id);
632 self.open_handles.remove(&file_id);
633 if let Some(listener) = self.tcp_listeners.remove(&file_id) {
634 let _ = self.sockets.remove(listener.socket);
635 }
636 if let Some(conn) = self.tcp_connections.remove(&file_id) {
637 let socket = self.sockets.get_mut::<tcp::Socket>(conn.socket);
638 socket.close();
639 self.lingering_sockets.push(conn.socket);
640 }
641 if let Some(state) = self.udp_bound.remove(&file_id) {
642 let socket = self.sockets.get_mut::<udp::Socket>(state.socket);
643 socket.close();
644 self.sockets.remove(state.socket);
645 }
646 if let Some(conn) = self.udp_connections.remove(&file_id) {
647 let socket = self.sockets.get_mut::<udp::Socket>(conn.socket);
648 socket.close();
649 self.sockets.remove(conn.socket);
650 }
651 reply_ok(msg.sender)
652 }
653}