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