xref: /aosp_15_r20/tools/netsim/rust/libslirp-rs/src/libslirp.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! # This module provides a safe Rust wrapper for the libslirp library.
16 
17 //! It allows to embed a virtual network stack within your Rust applications.
18 //!
19 //! ## Features
20 //!
21 //! * **Safe API:**  Wraps the libslirp C API in a safe and idiomatic Rust interface.
22 //! * **Networking:**  Provides functionality for virtual networking, including TCP/IP, UDP, and ICMP.
23 //! * **Proxy Support:**  Allows integration with proxy managers for handling external connections.
24 //! * **Threading:**  Handles communication between the Rust application and the libslirp event loop.
25 //!
26 //! ## Usage
27 //!
28 //! ```
29 //! use bytes::Bytes;
30 //! use libslirp_rs::libslirp_config::SlirpConfig;
31 //! use libslirp_rs::libslirp::LibSlirp;
32 //! use std::net::Ipv4Addr;
33 //! use std::sync::mpsc;
34 //!
35 //! let (tx_cmds, _) = mpsc::channel();
36 //! // Create a LibSlirp instance with default configuration
37 //! let libslirp = LibSlirp::new(
38 //!     SlirpConfig::default(),
39 //!     tx_cmds,
40 //!     None
41 //! );
42 //!
43 //! let data = vec![0x01, 0x02, 0x03];
44 //! // Input network data into libslirp
45 //! libslirp.input(Bytes::from(data));
46 //!
47 //! // ... other operations ...
48 //!
49 //! // Shutdown libslirp
50 //! libslirp.shutdown();
51 //! ```
52 //!
53 //! ## Example with Proxy
54 //!
55 //! ```
56 //! use libslirp_rs::libslirp::LibSlirp;
57 //! use libslirp_rs::libslirp_config::SlirpConfig;
58 //! use libslirp_rs::libslirp::{ProxyManager, ProxyConnect};
59 //! use std::sync::mpsc;
60 //! use std::net::SocketAddr;
61 //! // Implement the ProxyManager trait for your proxy logic
62 //! struct MyProxyManager;
63 //!
64 //! impl ProxyManager for MyProxyManager {
65 //!     // ... implementation ...
66 //!     fn try_connect(
67 //!         &self,
68 //!         sockaddr: SocketAddr,
69 //!         connect_id: usize,
70 //!         connect_func: Box<dyn ProxyConnect + Send>,
71 //!     ) -> bool {
72 //!         todo!()
73 //!     }
74 //!     fn remove(&self, connect_id: usize) {
75 //!         todo!()
76 //!     }
77 //! }
78 //! let (tx_cmds, _) = mpsc::channel();
79 //! // Create a LibSlirp instance with a proxy manager
80 //! let libslirp = LibSlirp::new(
81 //!     SlirpConfig::default(),
82 //!     tx_cmds,
83 //!     Some(Box::new(MyProxyManager)),
84 //! );
85 //!
86 //! // ...
87 //! ```
88 //!
89 //! This module abstracts away the complexities of interacting with the libslirp C library,
90 //! providing a more convenient and reliable way to use it in your Rust projects.
91 
92 use crate::libslirp_config;
93 use crate::libslirp_config::SlirpConfigs;
94 use crate::libslirp_sys;
95 
96 use bytes::Bytes;
97 use core::sync::atomic::{AtomicUsize, Ordering};
98 use log::{debug, info, warn};
99 use std::cell::RefCell;
100 use std::collections::HashMap;
101 use std::ffi::{c_char, c_int, c_void, CStr};
102 use std::mem::ManuallyDrop;
103 use std::net::SocketAddr;
104 use std::rc::Rc;
105 use std::sync::mpsc;
106 use std::thread;
107 use std::time::Duration;
108 use std::time::Instant;
109 
110 type TimerOpaque = usize;
111 
112 const TIMEOUT_SECS: u64 = 1;
113 
114 struct TimerManager {
115     clock: RefCell<Instant>,
116     map: RefCell<HashMap<TimerOpaque, Timer>>,
117     timers: AtomicUsize,
118 }
119 
120 #[derive(Clone)]
121 struct Timer {
122     id: libslirp_sys::SlirpTimerId,
123     cb_opaque: usize,
124     expire_time: u64,
125 }
126 
127 // The operations performed on the slirp thread
128 #[derive(Debug)]
129 enum SlirpCmd {
130     Input(Bytes),
131     PollResult(Vec<PollFd>, c_int),
132     TimerModified,
133     Shutdown,
134     ProxyConnect(libslirp_sys::SlirpProxyConnectFunc, usize, i32, i32),
135 }
136 
137 /// Alias for io::fd::RawFd on Unix or RawSocket on Windows (converted to i32)
138 pub type RawFd = i32;
139 
140 /// HTTP Proxy callback trait
141 pub trait ProxyManager: Send {
try_connect( &self, sockaddr: SocketAddr, connect_id: usize, connect_func: Box<dyn ProxyConnect + Send>, ) -> bool142     fn try_connect(
143         &self,
144         sockaddr: SocketAddr,
145         connect_id: usize,
146         connect_func: Box<dyn ProxyConnect + Send>,
147     ) -> bool;
remove(&self, connect_id: usize)148     fn remove(&self, connect_id: usize);
149 }
150 
151 struct CallbackContext {
152     tx_bytes: mpsc::Sender<Bytes>,
153     tx_cmds: mpsc::Sender<SlirpCmd>,
154     poll_fds: Rc<RefCell<Vec<PollFd>>>,
155     proxy_manager: Option<Box<dyn ProxyManager>>,
156     timer_manager: Rc<TimerManager>,
157 }
158 
159 // A poll thread request has a poll_fds and a timeout
160 type PollRequest = (Vec<PollFd>, u32);
161 
162 /// API to LibSlirp
163 
164 pub struct LibSlirp {
165     tx_cmds: mpsc::Sender<SlirpCmd>,
166 }
167 
168 impl TimerManager {
next_timer(&self) -> TimerOpaque169     fn next_timer(&self) -> TimerOpaque {
170         self.timers.fetch_add(1, Ordering::SeqCst) as TimerOpaque
171     }
172 
173     // Finds expired Timers, clears then clones them
collect_expired(&self) -> Vec<Timer>174     fn collect_expired(&self) -> Vec<Timer> {
175         let now_ms = self.get_elapsed().as_millis() as u64;
176         self.map
177             .borrow_mut()
178             .iter_mut()
179             .filter(|(_, timer)| timer.expire_time < now_ms)
180             .map(|(_, &mut ref mut timer)| {
181                 timer.expire_time = u64::MAX;
182                 timer.clone()
183             })
184             .collect()
185     }
186 
187     // Return the minimum duration until the next timer
min_duration(&self) -> Duration188     fn min_duration(&self) -> Duration {
189         match self.map.borrow().iter().min_by_key(|(_, timer)| timer.expire_time) {
190             Some((_, timer)) => {
191                 let now_ms = self.get_elapsed().as_millis() as u64;
192                 // Duration is >= 0
193                 Duration::from_millis(timer.expire_time.saturating_sub(now_ms))
194             }
195             None => Duration::from_millis(u64::MAX),
196         }
197     }
198 
get_elapsed(&self) -> Duration199     fn get_elapsed(&self) -> Duration {
200         self.clock.borrow().elapsed()
201     }
202 
remove(&self, timer_key: &TimerOpaque) -> Option<Timer>203     fn remove(&self, timer_key: &TimerOpaque) -> Option<Timer> {
204         self.map.borrow_mut().remove(timer_key)
205     }
206 
insert(&self, timer_key: TimerOpaque, value: Timer)207     fn insert(&self, timer_key: TimerOpaque, value: Timer) {
208         self.map.borrow_mut().insert(timer_key, value);
209     }
210 
timer_mod(&self, timer_key: &TimerOpaque, expire_time: u64)211     fn timer_mod(&self, timer_key: &TimerOpaque, expire_time: u64) {
212         if let Some(&mut ref mut timer) = self.map.borrow_mut().get_mut(&timer_key) {
213             // expire_time is >= 0
214             timer.expire_time = expire_time;
215         } else {
216             warn!("Unknown timer {timer_key}");
217         }
218     }
219 }
220 
221 impl LibSlirp {
new( config: libslirp_config::SlirpConfig, tx_bytes: mpsc::Sender<Bytes>, proxy_manager: Option<Box<dyn ProxyManager>>, ) -> LibSlirp222     pub fn new(
223         config: libslirp_config::SlirpConfig,
224         tx_bytes: mpsc::Sender<Bytes>,
225         proxy_manager: Option<Box<dyn ProxyManager>>,
226     ) -> LibSlirp {
227         let (tx_cmds, rx_cmds) = mpsc::channel::<SlirpCmd>();
228         let (tx_poll, rx_poll) = mpsc::channel::<PollRequest>();
229 
230         // Create channels for polling thread and launch
231         let tx_cmds_poll = tx_cmds.clone();
232         if let Err(e) = thread::Builder::new()
233             .name("slirp_poll".to_string())
234             .spawn(move || slirp_poll_thread(rx_poll, tx_cmds_poll))
235         {
236             warn!("Failed to start slirp poll thread: {}", e);
237         }
238 
239         let tx_cmds_slirp = tx_cmds.clone();
240         // Create channels for command processor thread and launch
241         if let Err(e) = thread::Builder::new().name("slirp".to_string()).spawn(move || {
242             slirp_thread(config, tx_bytes, tx_cmds_slirp, rx_cmds, tx_poll, proxy_manager)
243         }) {
244             warn!("Failed to start slirp thread: {}", e);
245         }
246 
247         LibSlirp { tx_cmds }
248     }
249 
shutdown(self)250     pub fn shutdown(self) {
251         if let Err(e) = self.tx_cmds.send(SlirpCmd::Shutdown) {
252             warn!("Failed to send Shutdown cmd: {}", e);
253         }
254     }
255 
input(&self, bytes: Bytes)256     pub fn input(&self, bytes: Bytes) {
257         if let Err(e) = self.tx_cmds.send(SlirpCmd::Input(bytes)) {
258             warn!("Failed to send Input cmd: {}", e);
259         }
260     }
261 }
262 
263 struct ConnectRequest {
264     tx_cmds: mpsc::Sender<SlirpCmd>,
265     connect_func: libslirp_sys::SlirpProxyConnectFunc,
266     connect_id: usize,
267     af: i32,
268     start: Instant,
269 }
270 
271 pub trait ProxyConnect: Send {
proxy_connect(&self, fd: i32, addr: SocketAddr)272     fn proxy_connect(&self, fd: i32, addr: SocketAddr);
273 }
274 
275 impl ProxyConnect for ConnectRequest {
proxy_connect(&self, fd: i32, addr: SocketAddr)276     fn proxy_connect(&self, fd: i32, addr: SocketAddr) {
277         // Send it to Slirp after try_connect() completed
278         let duration = self.start.elapsed().as_secs();
279         if duration > TIMEOUT_SECS {
280             warn!(
281                 "ConnectRequest for connection ID {} to {} took too long: {:?}",
282                 self.connect_id, addr, duration
283             );
284         }
285         let _ = self.tx_cmds.send(SlirpCmd::ProxyConnect(
286             self.connect_func,
287             self.connect_id,
288             fd,
289             self.af,
290         ));
291     }
292 }
293 
294 // Converts a libslirp callback's `opaque` handle into a
295 // `CallbackContext.`
296 //
297 // Wrapped in a `ManuallyDrop` because we do not want to release the
298 // storage when the callback returns.
299 //
300 // SAFETY:
301 //
302 // * opaque is a CallbackContext passed to the slirp API
callback_context_from_raw(opaque: *mut c_void) -> ManuallyDrop<Box<CallbackContext>>303 unsafe fn callback_context_from_raw(opaque: *mut c_void) -> ManuallyDrop<Box<CallbackContext>> {
304     ManuallyDrop::new(unsafe { Box::from_raw(opaque as *mut CallbackContext) })
305 }
306 
307 // A Rust struct for the fields held by `slirp` C library through it's
308 // lifetime.
309 //
310 // All libslirp C calls are impl on this struct.
311 struct Slirp {
312     slirp: *mut libslirp_sys::Slirp,
313     // These fields are held by slirp C library
314     #[allow(dead_code)]
315     configs: Box<SlirpConfigs>,
316     #[allow(dead_code)]
317     callbacks: Box<libslirp_sys::SlirpCb>,
318     // Passed to API calls and then to callbacks
319     callback_context: Box<CallbackContext>,
320 }
321 
322 impl Slirp {
new(config: libslirp_config::SlirpConfig, callback_context: Box<CallbackContext>) -> Slirp323     fn new(config: libslirp_config::SlirpConfig, callback_context: Box<CallbackContext>) -> Slirp {
324         let callbacks = Box::new(libslirp_sys::SlirpCb {
325             send_packet: Some(send_packet_cb),
326             guest_error: Some(guest_error_cb),
327             clock_get_ns: Some(clock_get_ns_cb),
328             timer_new: None,
329             timer_free: Some(timer_free_cb),
330             timer_mod: Some(timer_mod_cb),
331             register_poll_fd: Some(register_poll_fd_cb),
332             unregister_poll_fd: Some(unregister_poll_fd_cb),
333             notify: Some(notify_cb),
334             init_completed: Some(init_completed_cb),
335             timer_new_opaque: Some(timer_new_opaque_cb),
336             try_connect: Some(try_connect_cb),
337             remove: Some(remove_cb),
338         });
339         let configs = Box::new(SlirpConfigs::new(&config));
340 
341         // Call libslrip "C" library to create a new instance of a slirp
342         // protocol stack.
343         //
344         // SAFETY: We ensure that:
345         //
346         // * config is a valid pointer to the "C" config struct. It is
347         // held by the "C" slirp library for lifetime of the slirp
348         // instance.
349         //
350         // * callbacks is a valid pointer to an array of callback
351         // functions. It is held by the "C" slirp library for the lifetime
352         // of the slirp instance.
353         //
354         // * callback_context is an arbitrary opaque type passed back
355         //  to callback functions by libslirp.
356         let slirp = unsafe {
357             libslirp_sys::slirp_new(
358                 &configs.c_slirp_config,
359                 &*callbacks,
360                 &*callback_context as *const CallbackContext as *mut c_void,
361             )
362         };
363 
364         Slirp { slirp, configs, callbacks, callback_context }
365     }
366 
handle_timer(&self, timer: Timer)367     fn handle_timer(&self, timer: Timer) {
368         unsafe {
369             //
370             // SAFETY: We ensure that:
371             //
372             // *self.slirp is a valid state returned by `slirp_new()`
373             //
374             // * timer.id is a valid c_uint from "C" slirp library calling `timer_new_opaque_cb()`
375             //
376             // * timer.cb_opaque is an usize representing a pointer to callback function from
377             // "C" slirp library calling `timer_new_opaque_cb()`
378             libslirp_sys::slirp_handle_timer(self.slirp, timer.id, timer.cb_opaque as *mut c_void);
379         };
380     }
381 }
382 
383 impl Drop for Slirp {
drop(&mut self)384     fn drop(&mut self) {
385         // SAFETY:
386         //
387         // * self.slirp is a slirp pointer initialized by slirp_new;
388         // it's private to the struct and is only constructed that
389         // way.
390         unsafe { libslirp_sys::slirp_cleanup(self.slirp) };
391     }
392 }
393 
slirp_thread( config: libslirp_config::SlirpConfig, tx_bytes: mpsc::Sender<Bytes>, tx_cmds: mpsc::Sender<SlirpCmd>, rx: mpsc::Receiver<SlirpCmd>, tx_poll: mpsc::Sender<PollRequest>, proxy_manager: Option<Box<dyn ProxyManager>>, )394 fn slirp_thread(
395     config: libslirp_config::SlirpConfig,
396     tx_bytes: mpsc::Sender<Bytes>,
397     tx_cmds: mpsc::Sender<SlirpCmd>,
398     rx: mpsc::Receiver<SlirpCmd>,
399     tx_poll: mpsc::Sender<PollRequest>,
400     proxy_manager: Option<Box<dyn ProxyManager>>,
401 ) {
402     // Data structures wrapped in an RC are referenced through the
403     // libslirp callbacks and this code (both in the same thread).
404 
405     let timer_manager = Rc::new(TimerManager {
406         clock: RefCell::new(Instant::now()),
407         map: RefCell::new(HashMap::new()),
408         timers: AtomicUsize::new(1),
409     });
410 
411     let poll_fds = Rc::new(RefCell::new(Vec::new()));
412 
413     let callback_context = Box::new(CallbackContext {
414         tx_bytes,
415         tx_cmds,
416         poll_fds: poll_fds.clone(),
417         proxy_manager,
418         timer_manager: timer_manager.clone(),
419     });
420 
421     let slirp = Slirp::new(config, callback_context);
422 
423     slirp.pollfds_fill_and_send(&poll_fds, &tx_poll);
424 
425     let min_duration = timer_manager.min_duration();
426     loop {
427         let command = rx.recv_timeout(min_duration);
428         let start = Instant::now();
429 
430         let cmd_str = format!("{:?}", command);
431         match command {
432             // The dance to tell libslirp which FDs have IO ready
433             // starts with a response from a worker thread sending a
434             // PollResult, followed by pollfds_poll forwarding the FDs
435             // to libslirp, followed by giving the worker thread
436             // another set of fds to poll (and block).
437             Ok(SlirpCmd::PollResult(poll_fds_result, select_error)) => {
438                 poll_fds.borrow_mut().clone_from_slice(&poll_fds_result);
439                 slirp.pollfds_poll(select_error);
440                 slirp.pollfds_fill_and_send(&poll_fds, &tx_poll);
441             }
442             Ok(SlirpCmd::Input(bytes)) => slirp.input(&bytes),
443 
444             // A timer has been modified, new expired_time value
445             Ok(SlirpCmd::TimerModified) => continue,
446 
447             // Exit the while loop and shutdown
448             Ok(SlirpCmd::Shutdown) => break,
449 
450             // SAFETY: we ensure that func (`SlirpProxyConnectFunc`)
451             // and `connect_opaque` are valid because they originated
452             // from the libslirp call to `try_connect_cb.`
453             //
454             // Parameter `fd` will be >= 0 and the descriptor for the
455             // active socket to use, `af` will be either AF_INET or
456             // AF_INET6. On failure `fd` will be negative.
457             Ok(SlirpCmd::ProxyConnect(func, connect_id, fd, af)) => match func {
458                 Some(func) => unsafe { func(connect_id as *mut c_void, fd as c_int, af as c_int) },
459                 None => warn!("Proxy connect function not found"),
460             },
461 
462             // Timeout... process any timers
463             Err(mpsc::RecvTimeoutError::Timeout) => continue,
464 
465             // Error
466             _ => break,
467         }
468 
469         // Explicitly store expired timers to release lock
470         let timers = timer_manager.collect_expired();
471         // Handle any expired timers' callback in the slirp thread
472         for timer in timers {
473             slirp.handle_timer(timer);
474         }
475         let duration = start.elapsed().as_secs();
476         if duration > TIMEOUT_SECS {
477             warn!("libslirp command '{cmd_str}' took too long to complete: {duration:?}");
478         }
479     }
480     // Shuts down the instance of a slirp stack and release slirp storage. No callbacks
481     // occur after this since it calls slirp_cleanup.
482     drop(slirp);
483 
484     // Shutdown slirp_poll_thread -- worst case it sends a PollResult that is ignored
485     // since this thread is no longer processing Slirp commands.
486     drop(tx_poll);
487 }
488 
489 #[derive(Clone, Debug)]
490 struct PollFd {
491     fd: c_int,
492     events: libslirp_sys::SlirpPollType,
493     revents: libslirp_sys::SlirpPollType,
494 }
495 
496 // Fill the pollfds from libslirp and pass the request to the polling thread.
497 //
498 // This is called by the application when it is about to sleep through
499 // poll().  *timeout is set to the amount of virtual time (in ms) that
500 // the application intends to wait (UINT32_MAX if
501 // infinite). slirp_pollfds_fill updates it according to e.g. TCP
502 // timers, so the application knows it should sleep a smaller amount
503 // of time. slirp_pollfds_fill calls add_poll for each file descriptor
504 // that should be monitored along the sleep. The opaque pointer is
505 // passed as such to add_poll, and add_poll returns an index.
506 //
507 // # Safety
508 //
509 // `slirp` must be a valid Slirp state returned by `slirp_new()`
510 impl Slirp {
pollfds_fill_and_send( &self, poll_fds: &RefCell<Vec<PollFd>>, tx: &mpsc::Sender<PollRequest>, )511     fn pollfds_fill_and_send(
512         &self,
513         poll_fds: &RefCell<Vec<PollFd>>,
514         tx: &mpsc::Sender<PollRequest>,
515     ) {
516         let mut timeout: u32 = u32::MAX;
517         poll_fds.borrow_mut().clear();
518 
519         // Call libslrip "C" library to fill poll information using
520         // slirp_add_poll_cb callback function.
521         //
522         // SAFETY: we ensure that:
523         //
524         // * self.slirp has a slirp pointer initialized by slirp_new,
525         // as it's private to the struct is only constructed that way
526         //
527         // * timeout is a valid ptr to a mutable u32.  The "C" slirp
528         // library stores into timeout.
529         //
530         // * slirp_add_poll_cb is a valid `SlirpAddPollCb` function.
531         //
532         // * self.callback_context is a CallbackContext
533         unsafe {
534             libslirp_sys::slirp_pollfds_fill(
535                 self.slirp,
536                 &mut timeout,
537                 Some(slirp_add_poll_cb),
538                 &*self.callback_context as *const CallbackContext as *mut c_void,
539             );
540         }
541         if let Err(e) = tx.send((poll_fds.borrow().to_vec(), timeout)) {
542             warn!("Failed to send poll fds: {}", e);
543         }
544     }
545 }
546 
547 // "C" library callback that is called for each file descriptor that
548 // should be monitored.
549 //
550 // SAFETY:
551 //
552 // * opaque is a CallbackContext
slirp_add_poll_cb(fd: c_int, events: c_int, opaque: *mut c_void) -> c_int553 unsafe extern "C" fn slirp_add_poll_cb(fd: c_int, events: c_int, opaque: *mut c_void) -> c_int {
554     unsafe { callback_context_from_raw(opaque) }.add_poll(fd, events)
555 }
556 
557 impl CallbackContext {
add_poll(&mut self, fd: c_int, events: c_int) -> c_int558     fn add_poll(&mut self, fd: c_int, events: c_int) -> c_int {
559         let idx = self.poll_fds.borrow().len();
560         self.poll_fds.borrow_mut().push(PollFd {
561             fd,
562             events: events as libslirp_sys::SlirpPollType,
563             revents: 0,
564         });
565         idx as i32
566     }
567 }
568 
569 // Pass the result from the polling thread back to libslirp
570 
571 // This is called by the application when it is about to sleep through
572 // poll().  *timeout is set to the amount of virtual time (in ms) that
573 // the application intends to wait (UINT32_MAX if
574 // infinite). slirp_pollfds_fill updates it according to e.g. TCP
575 // timers, so the application knows it should sleep a smaller amount
576 // of time. slirp_pollfds_fill calls add_poll for each file descriptor
577 // that should be monitored along the sleep. The opaque pointer is
578 // passed as such to add_poll, and add_poll returns an index.
579 //
580 // * select_error should be 1 if poll() returned an error, else 0.
581 
582 impl Slirp {
pollfds_poll(&self, select_error: c_int)583     fn pollfds_poll(&self, select_error: c_int) {
584         // Call libslrip "C" library to fill poll return event information
585         // using slirp_get_revents_cb callback function.
586         //
587         // SAFETY: we ensure that:
588         //
589         // * self.slirp has a slirp pointer initialized by slirp_new,
590         // as it's private to the struct is only constructed that way
591         //
592         // * slirp_get_revents_cb is a valid `SlirpGetREventsCb` callback
593         // function.
594         //
595         // * select_error should be 1 if poll() returned an error, else 0.
596         //
597         // * self.callback_context is a CallbackContext
598         unsafe {
599             libslirp_sys::slirp_pollfds_poll(
600                 self.slirp,
601                 select_error,
602                 Some(slirp_get_revents_cb),
603                 &*self.callback_context as *const CallbackContext as *mut c_void,
604             );
605         }
606     }
607 }
608 
609 // "C" library callback that is called on each file descriptor, giving
610 // it the index that add_poll returned.
611 //
612 // SAFETY:
613 //
614 // * opaque is a CallbackContext
slirp_get_revents_cb(idx: c_int, opaque: *mut c_void) -> c_int615 unsafe extern "C" fn slirp_get_revents_cb(idx: c_int, opaque: *mut c_void) -> c_int {
616     unsafe { callback_context_from_raw(opaque) }.get_events(idx)
617 }
618 
619 impl CallbackContext {
get_events(&self, idx: c_int) -> c_int620     fn get_events(&self, idx: c_int) -> c_int {
621         if let Some(poll_fd) = self.poll_fds.borrow().get(idx as usize) {
622             poll_fd.revents as c_int
623         } else {
624             0
625         }
626     }
627 }
628 
629 macro_rules! ternary {
630     ($cond:expr, $true_expr:expr) => {
631         if $cond != 0 {
632             $true_expr
633         } else {
634             0
635         }
636     };
637 }
638 
639 // Worker thread loops issuing blocking poll requests, sending the
640 // results into the slirp thread
641 
slirp_poll_thread(rx: mpsc::Receiver<PollRequest>, tx: mpsc::Sender<SlirpCmd>)642 fn slirp_poll_thread(rx: mpsc::Receiver<PollRequest>, tx: mpsc::Sender<SlirpCmd>) {
643     #[cfg(any(target_os = "linux", target_os = "macos"))]
644     use libc::{
645         nfds_t as OsPollFdsLenType, poll, pollfd, POLLERR, POLLHUP, POLLIN, POLLOUT, POLLPRI,
646     };
647     #[cfg(target_os = "windows")]
648     use winapi::{
649         shared::minwindef::ULONG as OsPollFdsLenType,
650         um::winsock2::{
651             WSAPoll as poll, POLLERR, POLLHUP, POLLOUT, POLLPRI, POLLRDBAND, POLLRDNORM,
652             SOCKET as FdType, WSAPOLLFD as pollfd,
653         },
654     };
655     #[cfg(any(target_os = "linux", target_os = "macos"))]
656     type FdType = c_int;
657 
658     #[cfg(any(target_os = "linux", target_os = "macos"))]
659     fn to_os_events(events: libslirp_sys::SlirpPollType) -> i16 {
660         ternary!(events & libslirp_sys::SLIRP_POLL_IN, POLLIN)
661             | ternary!(events & libslirp_sys::SLIRP_POLL_OUT, POLLOUT)
662             | ternary!(events & libslirp_sys::SLIRP_POLL_PRI, POLLPRI)
663             | ternary!(events & libslirp_sys::SLIRP_POLL_ERR, POLLERR)
664             | ternary!(events & libslirp_sys::SLIRP_POLL_HUP, POLLHUP)
665     }
666 
667     #[cfg(any(target_os = "linux", target_os = "macos"))]
668     fn to_slirp_events(events: i16) -> libslirp_sys::SlirpPollType {
669         ternary!(events & POLLIN, libslirp_sys::SLIRP_POLL_IN)
670             | ternary!(events & POLLOUT, libslirp_sys::SLIRP_POLL_OUT)
671             | ternary!(events & POLLPRI, libslirp_sys::SLIRP_POLL_PRI)
672             | ternary!(events & POLLOUT, libslirp_sys::SLIRP_POLL_ERR)
673             | ternary!(events & POLLHUP, libslirp_sys::SLIRP_POLL_HUP)
674     }
675 
676     #[cfg(target_os = "windows")]
677     fn to_os_events(events: libslirp_sys::SlirpPollType) -> i16 {
678         ternary!(events & libslirp_sys::SLIRP_POLL_IN, POLLRDNORM)
679             | ternary!(events & libslirp_sys::SLIRP_POLL_OUT, POLLOUT)
680             | ternary!(events & libslirp_sys::SLIRP_POLL_PRI, POLLRDBAND)
681     }
682 
683     #[cfg(target_os = "windows")]
684     fn to_slirp_events(events: i16) -> libslirp_sys::SlirpPollType {
685         ternary!(events & POLLRDNORM, libslirp_sys::SLIRP_POLL_IN)
686             | ternary!(events & POLLERR, libslirp_sys::SLIRP_POLL_IN)
687             | ternary!(events & POLLHUP, libslirp_sys::SLIRP_POLL_IN)
688             | ternary!(events & POLLOUT, libslirp_sys::SLIRP_POLL_OUT)
689             | ternary!(events & POLLERR, libslirp_sys::SLIRP_POLL_PRI)
690             | ternary!(events & POLLHUP, libslirp_sys::SLIRP_POLL_PRI)
691             | ternary!(events & POLLPRI, libslirp_sys::SLIRP_POLL_PRI)
692             | ternary!(events & POLLRDBAND, libslirp_sys::SLIRP_POLL_PRI)
693     }
694 
695     let mut prev_poll_fds_len = 0;
696     while let Ok((poll_fds, timeout)) = rx.recv() {
697         if poll_fds.len() != prev_poll_fds_len {
698             prev_poll_fds_len = poll_fds.len();
699             debug!("slirp_poll_thread recv poll_fds.len(): {:?}", prev_poll_fds_len);
700         }
701         // Create a c format array with the same size as poll
702         let mut os_poll_fds: Vec<pollfd> = Vec::with_capacity(poll_fds.len());
703         for fd in &poll_fds {
704             os_poll_fds.push(pollfd {
705                 fd: fd.fd as FdType,
706                 events: to_os_events(fd.events),
707                 revents: 0,
708             });
709         }
710 
711         let mut poll_result = 0;
712         #[cfg(any(target_os = "linux", target_os = "macos"))]
713         {
714             // SAFETY: we ensure that:
715             //
716             // `os_poll_fds` is a valid ptr to a vector of pollfd which
717             // the `poll` system call can write into. Note `os_poll_fds`
718             // is created and allocated above.
719             poll_result = unsafe {
720                 poll(
721                     os_poll_fds.as_mut_ptr(),
722                     os_poll_fds.len() as OsPollFdsLenType,
723                     timeout as i32,
724                 )
725             };
726         }
727         // WSAPoll requires an array of one or more POLLFD structures.
728         // When nfds == 0, WSAPoll returns immediately with result -1, ignoring the timeout.
729         // This is different from poll on Linux/macOS, which will wait for the timeout.
730         // Therefore, on Windows, we don't call WSAPoll when nfds == 0, and instead explicitly sleep for the timeout.
731         #[cfg(target_os = "windows")]
732         if os_poll_fds.is_empty() {
733             // If there are no FDs to poll, sleep for the specified timeout.
734             thread::sleep(Duration::from_millis(timeout as u64));
735         } else {
736             // SAFETY: we ensure that:
737             //
738             // `os_poll_fds` is a valid ptr to a vector of pollfd which
739             // the `poll` system call can write into. Note `os_poll_fds`
740             // is created and allocated above.
741             poll_result = unsafe {
742                 poll(
743                     os_poll_fds.as_mut_ptr(),
744                     os_poll_fds.len() as OsPollFdsLenType,
745                     timeout as i32,
746                 )
747             };
748         }
749 
750         let mut slirp_poll_fds: Vec<PollFd> = Vec::with_capacity(poll_fds.len());
751         #[cfg(any(target_os = "linux", target_os = "macos"))]
752         for &fd in &os_poll_fds {
753             slirp_poll_fds.push(PollFd {
754                 fd: fd.fd as c_int,
755                 events: to_slirp_events(fd.events),
756                 revents: to_slirp_events(fd.revents) & to_slirp_events(fd.events),
757             });
758         }
759         #[cfg(target_os = "windows")]
760         for (fd, poll_fd) in os_poll_fds.iter().zip(poll_fds.iter()) {
761             slirp_poll_fds.push(PollFd {
762                 fd: fd.fd as c_int,
763                 events: poll_fd.events,
764                 revents: to_slirp_events(fd.revents) & poll_fd.events,
765             });
766         }
767 
768         // 'select_error' should be 1 if poll() returned an error, else 0.
769         if let Err(e) = tx.send(SlirpCmd::PollResult(slirp_poll_fds, (poll_result < 0) as i32)) {
770             warn!("Failed to send slirp PollResult cmd: {}", e);
771         }
772     }
773 }
774 
775 // Call libslrip "C" library to send input.
776 //
777 // This is called by the application when the guest emits a packet on
778 // the guest network, to be interpreted by slirp.
779 impl Slirp {
input(&self, bytes: &[u8])780     fn input(&self, bytes: &[u8]) {
781         // SAFETY: The "C" library ensure that the memory is not
782         // referenced after the call and `bytes` does not need to remain
783         // valid after the function returns.
784         unsafe { libslirp_sys::slirp_input(self.slirp, bytes.as_ptr(), bytes.len() as i32) };
785     }
786 }
787 
788 // "C" library callback that is called to send an ethernet frame to
789 // the guest network. If the guest is not ready to receive a frame,
790 // the function can just drop the data. TCP will then handle
791 // retransmissions at a lower pace.  A return of < 0 reports an IO
792 // error.
793 //
794 // # Safety:
795 //
796 // * buf must be a valid pointer to `len` bytes of memory. The
797 // contents of buf must be valid for the duration of this call.
798 //
799 // * len is > 0
800 //
801 // * opaque is a CallbackContext
send_packet_cb( buf: *const c_void, len: usize, opaque: *mut c_void, ) -> libslirp_sys::slirp_ssize_t802 unsafe extern "C" fn send_packet_cb(
803     buf: *const c_void,
804     len: usize,
805     opaque: *mut c_void,
806 ) -> libslirp_sys::slirp_ssize_t {
807     unsafe { callback_context_from_raw(opaque) }.send_packet(buf, len)
808 }
809 
810 impl CallbackContext {
send_packet(&self, buf: *const c_void, len: usize) -> libslirp_sys::slirp_ssize_t811     fn send_packet(&self, buf: *const c_void, len: usize) -> libslirp_sys::slirp_ssize_t {
812         // SAFETY: The caller ensures that `buf` is contains `len` bytes of data.
813         let c_slice = unsafe { std::slice::from_raw_parts(buf as *const u8, len) };
814         // Bytes::from(slice: &'static [u8]) creates a Bytes object without copying the data.
815         // To own its data, copy &'static [u8] to Vec<u8> before converting to Bytes.
816         let _ = self.tx_bytes.send(Bytes::from(c_slice.to_vec()));
817         len as libslirp_sys::slirp_ssize_t
818     }
819 }
820 
821 // "C" library callback to print a message for an error due to guest
822 // misbehavior.
823 //
824 // # Safety:
825 //
826 // * msg must be a valid nul-terminated utf8 string.
827 //
828 // * opaque is a CallbackContext
guest_error_cb(msg: *const c_char, opaque: *mut c_void)829 unsafe extern "C" fn guest_error_cb(msg: *const c_char, opaque: *mut c_void) {
830     // SAFETY: The caller ensures that `msg` is a nul-terminated string.
831     let msg = String::from_utf8_lossy(unsafe { CStr::from_ptr(msg) }.to_bytes());
832     unsafe { callback_context_from_raw(opaque) }.guest_error(msg.to_string());
833 }
834 
835 impl CallbackContext {
guest_error(&self, msg: String)836     fn guest_error(&self, msg: String) {
837         warn!("libslirp: {msg}");
838     }
839 }
840 
841 // SAFETY:
842 //
843 // * opaque is a CallbackContext
clock_get_ns_cb(opaque: *mut c_void) -> i64844 unsafe extern "C" fn clock_get_ns_cb(opaque: *mut c_void) -> i64 {
845     unsafe { callback_context_from_raw(opaque) }.clock_get_ns()
846 }
847 
848 impl CallbackContext {
clock_get_ns(&self) -> i64849     fn clock_get_ns(&self) -> i64 {
850         self.timer_manager.get_elapsed().as_nanos() as i64
851     }
852 }
853 
854 // SAFETY:
855 //
856 // * opaque is a CallbackContext
init_completed_cb(_slirp: *mut libslirp_sys::Slirp, opaque: *mut c_void)857 unsafe extern "C" fn init_completed_cb(_slirp: *mut libslirp_sys::Slirp, opaque: *mut c_void) {
858     unsafe { callback_context_from_raw(opaque) }.init_completed();
859 }
860 
861 impl CallbackContext {
init_completed(&self)862     fn init_completed(&self) {
863         info!("libslirp: initialization completed.");
864     }
865 }
866 
867 // Create a new timer
868 //
869 // SAFETY:
870 //
871 // * opaque is a CallbackContext
timer_new_opaque_cb( id: libslirp_sys::SlirpTimerId, cb_opaque: *mut c_void, opaque: *mut c_void, ) -> *mut c_void872 unsafe extern "C" fn timer_new_opaque_cb(
873     id: libslirp_sys::SlirpTimerId,
874     cb_opaque: *mut c_void,
875     opaque: *mut c_void,
876 ) -> *mut c_void {
877     unsafe { callback_context_from_raw(opaque) }.timer_new_opaque(id, cb_opaque)
878 }
879 
880 impl CallbackContext {
881     // SAFETY:
882     //
883     // * cb_opaque is only passed back to libslirp
timer_new_opaque( &self, id: libslirp_sys::SlirpTimerId, cb_opaque: *mut c_void, ) -> *mut c_void884     unsafe fn timer_new_opaque(
885         &self,
886         id: libslirp_sys::SlirpTimerId,
887         cb_opaque: *mut c_void,
888     ) -> *mut c_void {
889         let timer = self.timer_manager.next_timer();
890         self.timer_manager
891             .insert(timer, Timer { expire_time: u64::MAX, id, cb_opaque: cb_opaque as usize });
892         timer as *mut c_void
893     }
894 }
895 
896 // SAFETY:
897 //
898 // * timer is a TimerOpaque key for timer manager
899 //
900 // * opaque is a CallbackContext
timer_free_cb(timer: *mut c_void, opaque: *mut c_void)901 unsafe extern "C" fn timer_free_cb(timer: *mut c_void, opaque: *mut c_void) {
902     unsafe { callback_context_from_raw(opaque) }.timer_free(timer);
903 }
904 
905 impl CallbackContext {
timer_free(&self, timer: *mut c_void)906     fn timer_free(&self, timer: *mut c_void) {
907         let timer = timer as TimerOpaque;
908         if self.timer_manager.remove(&timer).is_none() {
909             warn!("Unknown timer {timer}");
910         }
911     }
912 }
913 
914 // SAFETY:
915 //
916 // * timer is a TimerOpaque key for timer manager
917 //
918 // * opaque is a CallbackContext
timer_mod_cb(timer: *mut c_void, expire_time: i64, opaque: *mut c_void)919 unsafe extern "C" fn timer_mod_cb(timer: *mut c_void, expire_time: i64, opaque: *mut c_void) {
920     unsafe { callback_context_from_raw(opaque) }.timer_mod(timer, expire_time);
921 }
922 
923 impl CallbackContext {
timer_mod(&self, timer: *mut c_void, expire_time: i64)924     fn timer_mod(&self, timer: *mut c_void, expire_time: i64) {
925         let timer_key = timer as TimerOpaque;
926         let expire_time = std::cmp::max(expire_time, 0) as u64;
927         self.timer_manager.timer_mod(&timer_key, expire_time);
928         // Wake up slirp command thread to reset sleep duration
929         let _ = self.tx_cmds.send(SlirpCmd::TimerModified);
930     }
931 }
932 
register_poll_fd_cb(_fd: c_int, _opaque: *mut c_void)933 extern "C" fn register_poll_fd_cb(_fd: c_int, _opaque: *mut c_void) {
934     //TODO: Need implementation for Windows
935 }
936 
unregister_poll_fd_cb(_fd: c_int, _opaque: *mut c_void)937 extern "C" fn unregister_poll_fd_cb(_fd: c_int, _opaque: *mut c_void) {
938     //TODO: Need implementation for Windows
939 }
940 
notify_cb(_opaque: *mut c_void)941 extern "C" fn notify_cb(_opaque: *mut c_void) {
942     //TODO: Un-implemented
943 }
944 
945 // Called by libslirp to initiate a proxy connection to address
946 // `addr.` Eventually this will notify libslirp with a result by
947 // calling the passed `connect_func.`
948 //
949 // SAFETY:
950 //
951 // * opaque is a CallbackContext
try_connect_cb( addr: *const libslirp_sys::sockaddr_storage, connect_func: libslirp_sys::SlirpProxyConnectFunc, connect_opaque: *mut c_void, opaque: *mut c_void, ) -> bool952 unsafe extern "C" fn try_connect_cb(
953     addr: *const libslirp_sys::sockaddr_storage,
954     connect_func: libslirp_sys::SlirpProxyConnectFunc,
955     connect_opaque: *mut c_void,
956     opaque: *mut c_void,
957 ) -> bool {
958     unsafe { callback_context_from_raw(opaque) }.try_connect(
959         addr,
960         connect_func,
961         connect_opaque as usize,
962     )
963 }
964 
965 impl CallbackContext {
try_connect( &self, addr: *const libslirp_sys::sockaddr_storage, connect_func: libslirp_sys::SlirpProxyConnectFunc, connect_id: usize, ) -> bool966     fn try_connect(
967         &self,
968         addr: *const libslirp_sys::sockaddr_storage,
969         connect_func: libslirp_sys::SlirpProxyConnectFunc,
970         connect_id: usize,
971     ) -> bool {
972         if let Some(proxy_manager) = &self.proxy_manager {
973             // SAFETY: We ensure that addr is valid when `try_connect` is called from libslirp
974             let storage = unsafe { *addr };
975             let af = storage.ss_family as i32;
976             let socket_addr: SocketAddr = storage.into();
977             proxy_manager.try_connect(
978                 socket_addr,
979                 connect_id,
980                 Box::new(ConnectRequest {
981                     tx_cmds: self.tx_cmds.clone(),
982                     connect_func,
983                     connect_id,
984                     af,
985                     start: Instant::now(),
986                 }),
987             )
988         } else {
989             false
990         }
991     }
992 }
993 
remove_cb(connect_opaque: *mut c_void, opaque: *mut c_void)994 unsafe extern "C" fn remove_cb(connect_opaque: *mut c_void, opaque: *mut c_void) {
995     unsafe { callback_context_from_raw(opaque) }.remove(connect_opaque as usize);
996 }
997 
998 impl CallbackContext {
remove(&self, connect_id: usize)999     fn remove(&self, connect_id: usize) {
1000         if let Some(proxy_connector) = &self.proxy_manager {
1001             proxy_connector.remove(connect_id);
1002         }
1003     }
1004 }
1005 
1006 #[cfg(test)]
1007 mod tests {
1008     use super::*;
1009 
1010     #[test]
test_version_string()1011     fn test_version_string() {
1012         // Safety
1013         // Function returns a constant c_str
1014         let c_version_str = unsafe { CStr::from_ptr(crate::libslirp_sys::slirp_version_string()) };
1015         assert_eq!("4.7.0", c_version_str.to_str().unwrap());
1016     }
1017 }
1018