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 || 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 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}