| // Documentation for code maintainers: |
| // |
| // Closure pointers as *c_void |
| // ----------------- |
| // This file interfaces with Slirp's callbacks extensively. As part of that, it passes Rust closures |
| // to callbacks as opaque data, and those closures are executed during a call into Slirp; in other |
| // words, the sequence of events looks like this: |
| // 1. Rust creates a closure C. |
| // 2. Rust calls Slirp and gives it callback CB (a Rust func.), and opaque data that contains C. |
| // 3. (0...n times) CB runs and unpacks C from the opaque data. a. CB calls C. |
| // 4. The call from #2 completes. |
| // Closures are represented as trait objects, and trait object references are wide/fat pointers |
| // (2x machine pointer size) that contain pointers to the closure's struct & its vtable. Since wide |
| // pointers are obviously too large to fit into a *mut c_void (this is the opaque data), we cannot |
| // pass them directly. Luckily, wide pointers themselves are simple sized data structures, so we can |
| // pass a reference to the wide pointer as a *mut c_void; in other words, the way to pass a closure |
| // is to cast &mut &mut some_closure into a *mut c_void. We can then unpack this easily and call |
| // the closure. |
| // |
| // Why is CallbackHandler involved in the outbound (guest -> host) packet path? |
| // ---------------------------------------------------------------------------- |
| // In short, ownership. Since the CallbackHandler is responsible for writing to the guest, it must |
| // own the connection/stream that is attached to the guest. Because CallbackHandler owns the |
| // connection, it would be significantly complicated to have any other entity read from the |
| // connection. |
| // |
| // Safety assumptions |
| // ------------------ |
| // Most statements explaining the safety of unsafe code depend on libslirp behaving in a safe |
| // & expected manner. Given that libslirp is has experienced CVEs related to safety problems, |
| // these statements should be taken with a grain of salt. Consumers of this library are STRONGLY |
| // RECOMMENDED to run this code in a separate process with strong sandboxing. |
| |
| // Some bindings of the libslirp API are used, but we want to keep them for completeness |
| #![allow(dead_code)] |
| |
| use std::ffi::CStr; |
| use std::ffi::CString; |
| use std::fmt; |
| use std::io; |
| use std::net::Ipv4Addr; |
| use std::net::Ipv6Addr; |
| use std::ops; |
| use std::os::raw::c_char; |
| use std::os::raw::c_int; |
| use std::os::raw::c_void; |
| use std::slice; |
| use std::str; |
| |
| use base::error; |
| use base::RawDescriptor; |
| use libslirp_sys::*; |
| |
| use crate::slirp::SlirpError; |
| use crate::Error; |
| use crate::Result; |
| |
| /// An instance of libslirp is represented by a Context, which provides methods that consumers use |
| /// to interact with Slirp. The Context is also where consumers register their CallbackHandler, |
| /// which provides required functions to libslirp, and allows libslirp to communicate back to the |
| /// consumer. The Context is intended to be used from an event loop which handles polling for IO |
| /// from the guest and host (see `Context.pollfds_fill`, `Context.pollfds_poll`, and |
| /// `Context.handle_guest_input`). |
| /// |
| /// Example data flow for an outbound packet: |
| /// 1. Context `ctx` created with CallbackHandler `h`. An event loop (henceforth the slirp loop) is |
| /// started, which polls for packets from the guest, and from the outside world (sockets). |
| /// 2. Guest emits an ethernet frame. The consumer makes this frame available through their |
| /// implementation of `h.read_from_guest`. |
| /// 3. The slirp loop is notified there is a packet from the guest, and invokes |
| /// `ctx.handle_guest_input`. `ctx.handle_guest_input` reads all currently available packets |
| /// using `h.read_from_guest` & returns back to the slirp loop. |
| /// |
| /// Example data flow for an inbound packet: |
| /// 1. Same as above. |
| /// 2. An ethernet frame arrives to the host, and gets demuxed by the host kernel into one of the |
| /// host sockets opened by libslirp. |
| /// 3. The slirp loop receives a notification that data has arrived on one of the libslirp sockets, |
| /// and invokes `ctx.pollfds_poll` to notify libslirp. a. libslirp calls into `h.send_packet` to |
| /// deliver the packet to the consumer. |
| pub struct Context<H> { |
| slirp: *mut Slirp, |
| callbacks: SlirpCb, |
| callback_handler: H, |
| } |
| |
| impl<H> Drop for Context<H> { |
| fn drop(&mut self) { |
| if !self.slirp.is_null() { |
| // SAFETY: |
| // Safe because self.context is guaranteed to be valid or null upon construction. |
| unsafe { |
| slirp_cleanup(self.slirp); |
| } |
| } |
| } |
| } |
| |
| /// `CallbackHandler` is the safe Rust interface for the Slirp callbacks. Consumers of Slirp MUST |
| /// implement this interface to handle the required callbacks from Slirp. |
| /// |
| /// ## Notes about timers |
| /// To send NDP router advertisements on IPv6, libslirp uses a timer. If IPv6 support is not |
| /// needed, the timer callbacks can be left unimplemented. |
| /// |
| /// Example data flow for timer creation/modification (`timer_new`/`timer_mod`/`timer_free`): |
| /// 1. libslirp calls into `timer_new` to request a new timer. `timer_new` creates some entity to |
| /// represent the timer and boxes it. A pointer to that boxed entity is returned to libslirp, and |
| /// is how libslirp will refer to the timer in the future. |
| /// 2. The timer's expire time can be changed when timer_mod is called by libslirp. A pointer to the |
| /// boxed timer is passed in by libslirp. |
| /// 3. The implementor of `CallbackHandler` is responsible for ensuring that the timer's callback as |
| /// provided in `timer_new` is invoked at/after the `expire_time`. |
| /// 4. libslirp will free timers using `timer_free` when `slirp_cleanup` runs. |
| /// |
| /// libslirp never does anything with the timer pointer beyond passing it to/from the the functions |
| /// in `CallbackHandler`. |
| pub trait CallbackHandler { |
| type Timer; |
| |
| /// Returns a timestamp in nanoseconds relative to the moment that this instance of libslirp |
| /// started running. |
| fn clock_get_ns(&mut self) -> i64; |
| |
| fn send_packet(&mut self, buf: &[u8]) -> io::Result<usize>; |
| |
| /// Gets an iterator of timers (as raw descriptors) so they can be awaited with a suitable |
| /// polling function as part of libslirp's main consumer loop. |
| fn get_timers(&self) -> Box<dyn Iterator<Item = &RawDescriptor> + '_>; |
| |
| /// Runs the handler function for a specific timer. |
| fn execute_timer(&mut self, timer: RawDescriptor); |
| |
| // Normally in crosvm we refer to FDs as descriptors, because FDs are platform specific; |
| // however, this interface is very close to the libslirp FFI, and libslirp follows the Linux |
| // philosophy of everything is an FD. Since even Windows refers to FDs in WSAPoll, keeping FD |
| // as a concept here helps keep terminology consistent between crosvm code interfacing with |
| // libslirp, and libslirp itself. |
| fn register_poll_fd(&mut self, fd: i32); |
| fn unregister_poll_fd(&mut self, fd: i32); |
| |
| fn guest_error(&mut self, msg: &str); |
| |
| fn notify(&mut self); |
| |
| fn timer_new(&mut self, callback: Box<dyn FnMut()>) -> Box<Self::Timer>; |
| |
| /// Sets a timer to expire in expire_time_ms - (clock_get_ns() (as ms). |
| fn timer_mod(&mut self, timer: &mut Self::Timer, expire_time: i64); |
| |
| fn timer_free(&mut self, timer: Box<Self::Timer>); |
| |
| fn begin_read_from_guest(&mut self) -> io::Result<()>; |
| |
| fn end_read_from_guest(&mut self) -> io::Result<&[u8]>; |
| } |
| |
| extern "C" fn write_handler_callback(buf: *const c_void, len: usize, opaque: *mut c_void) -> isize { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type. |
| let closure = unsafe { &mut *(opaque as *mut &mut dyn FnMut(&[u8]) -> isize) }; |
| |
| // SAFETY: |
| // Safe because libslirp provides us with a valid buffer & that buffer's length. |
| let slice = unsafe { slice::from_raw_parts(buf as *const u8, len) }; |
| |
| closure(slice) |
| } |
| |
| extern "C" fn read_handler_callback(buf: *mut c_void, len: usize, opaque: *mut c_void) -> isize { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type. |
| let closure = unsafe { &mut *(opaque as *mut &mut dyn FnMut(&mut [u8]) -> isize) }; |
| |
| // SAFETY: |
| // Safe because libslirp provides us with a valid buffer & that buffer's length. |
| let slice = unsafe { slice::from_raw_parts_mut(buf as *mut u8, len) }; |
| |
| closure(slice) |
| } |
| |
| /// Represents poll events in libslirp's format (e.g. `struct pollfd.[r]events`). |
| #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)] |
| pub struct PollEvents(usize); |
| |
| impl PollEvents { |
| pub fn empty() -> Self { |
| PollEvents(0) |
| } |
| pub fn poll_in() -> Self { |
| PollEvents(SLIRP_POLL_IN as usize) |
| } |
| pub fn poll_out() -> Self { |
| PollEvents(SLIRP_POLL_OUT as usize) |
| } |
| pub fn poll_pri() -> Self { |
| PollEvents(SLIRP_POLL_PRI as usize) |
| } |
| pub fn poll_err() -> Self { |
| PollEvents(SLIRP_POLL_ERR as usize) |
| } |
| pub fn poll_hup() -> Self { |
| PollEvents(SLIRP_POLL_HUP as usize) |
| } |
| pub fn contains<T: Into<Self>>(&self, other: T) -> bool { |
| let other = other.into(); |
| (*self & other) == other |
| } |
| pub fn is_empty(&self) -> bool { |
| self.0 == 0 |
| } |
| pub fn has_in(&self) -> bool { |
| self.contains(PollEvents::poll_in()) |
| } |
| pub fn has_out(&self) -> bool { |
| self.contains(PollEvents::poll_out()) |
| } |
| pub fn has_pri(&self) -> bool { |
| self.contains(PollEvents::poll_pri()) |
| } |
| pub fn has_err(&self) -> bool { |
| self.contains(PollEvents::poll_err()) |
| } |
| pub fn has_hup(&self) -> bool { |
| self.contains(PollEvents::poll_hup()) |
| } |
| } |
| |
| impl<T: Into<PollEvents>> ops::BitAnd<T> for PollEvents { |
| type Output = PollEvents; |
| |
| fn bitand(self, other: T) -> PollEvents { |
| PollEvents(self.0 & other.into().0) |
| } |
| } |
| |
| impl<T: Into<PollEvents>> ops::BitOr<T> for PollEvents { |
| type Output = PollEvents; |
| |
| fn bitor(self, other: T) -> PollEvents { |
| PollEvents(self.0 | other.into().0) |
| } |
| } |
| |
| impl<T: Into<PollEvents>> ops::BitOrAssign<T> for PollEvents { |
| fn bitor_assign(&mut self, other: T) { |
| self.0 |= other.into().0; |
| } |
| } |
| |
| impl fmt::Debug for PollEvents { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| let mut one = false; |
| let flags = [ |
| (PollEvents(SLIRP_POLL_IN as usize), "IN"), |
| (PollEvents(SLIRP_POLL_OUT as usize), "OUT"), |
| (PollEvents(SLIRP_POLL_PRI as usize), "PRI"), |
| (PollEvents(SLIRP_POLL_ERR as usize), "ERR"), |
| (PollEvents(SLIRP_POLL_HUP as usize), "HUP"), |
| ]; |
| |
| for &(flag, msg) in &flags { |
| if self.contains(flag) { |
| if one { |
| write!(fmt, " | ")? |
| } |
| write!(fmt, "{msg}")?; |
| |
| one = true |
| } |
| } |
| |
| if !one { |
| fmt.write_str("(empty)")?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| extern "C" fn add_poll_handler_callback(fd: c_int, events: c_int, opaque: *mut c_void) -> c_int { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type. |
| let closure = unsafe { &mut *(opaque as *mut &mut dyn FnMut(i32, PollEvents) -> i32) }; |
| |
| closure(fd, PollEvents(events as usize)) |
| } |
| |
| extern "C" fn get_revents_handler_callback(idx: c_int, opaque: *mut c_void) -> c_int { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type. |
| let closure = unsafe { &mut *(opaque as *mut &mut dyn FnMut(i32) -> PollEvents) }; |
| |
| closure(idx).0 as c_int |
| } |
| |
| /// Inbound packets from libslirp are delivered to this handler, which passes them on to the |
| /// Context's CallbackHandler for forwarding to the guest. |
| extern "C" fn send_packet_handler<H: CallbackHandler>( |
| buf: *const c_void, |
| len: usize, |
| opaque: *mut c_void, |
| ) -> isize { |
| // SAFETY: |
| // Safe because libslirp gives us a valid buffer & that buffer's length. |
| let slice = unsafe { slice::from_raw_parts(buf as *const u8, len) }; |
| |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| let res = unsafe { |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .send_packet(slice) |
| }; |
| |
| match res { |
| Ok(res) => res as isize, |
| Err(e) => { |
| error!("send_packet error: {}", e); |
| -1 |
| } |
| } |
| } |
| |
| extern "C" fn guest_error_handler<H: CallbackHandler>(msg: *const c_char, opaque: *mut c_void) { |
| // SAFETY: |
| // Safe because libslirp gives us a valid C string representing the error message. |
| let msg = str::from_utf8(unsafe { CStr::from_ptr(msg) }.to_bytes()).unwrap_or(""); |
| |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| unsafe { |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .guest_error(msg) |
| } |
| } |
| |
| extern "C" fn clock_get_ns_handler<H: CallbackHandler>(opaque: *mut c_void) -> i64 { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| unsafe { |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .clock_get_ns() |
| } |
| } |
| |
| extern "C" fn timer_new_handler<H: CallbackHandler>( |
| cb: SlirpTimerCb, |
| cb_opaque: *mut c_void, |
| opaque: *mut c_void, |
| ) -> *mut c_void { |
| let callback = Box::new(move || { |
| if let Some(cb) = cb { |
| // SAFETY: |
| // Safe because libslirp gives us a valid callback function to call. |
| unsafe { |
| cb(cb_opaque); |
| } |
| } |
| }); |
| |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| let timer = unsafe { |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .timer_new(callback) |
| }; |
| |
| Box::into_raw(timer) as *mut c_void |
| } |
| |
| extern "C" fn timer_free_handler<H: CallbackHandler>(timer: *mut c_void, opaque: *mut c_void) { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| // Also, timer was created by us as exactly the type we unpack into. |
| unsafe { |
| let timer = Box::from_raw(timer as *mut H::Timer); |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .timer_free(timer); |
| } |
| } |
| |
| extern "C" fn timer_mod_handler<H: CallbackHandler>( |
| timer: *mut c_void, |
| expire_time: i64, |
| opaque: *mut c_void, |
| ) { |
| // SAFETY: |
| // Safe because: |
| // 1. We pass in opaque as exactly this type when constructing the Slirp object. |
| // 2. timer was created by us as exactly the type we unpack into |
| // 3. libslirp is responsible for freeing timer, so forgetting about it is safe. |
| unsafe { |
| let mut timer = Box::from_raw(timer as *mut H::Timer); |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .timer_mod(&mut timer, expire_time); |
| let _ = Box::into_raw(timer); |
| } |
| } |
| |
| extern "C" fn register_poll_fd_handler<H: CallbackHandler>(fd: c_int, opaque: *mut c_void) { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| unsafe { |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .register_poll_fd(fd) |
| } |
| } |
| |
| extern "C" fn unregister_poll_fd_handler<H: CallbackHandler>(fd: c_int, opaque: *mut c_void) { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| unsafe { |
| (*(opaque as *mut Context<H>)) |
| .callback_handler |
| .unregister_poll_fd(fd) |
| } |
| } |
| |
| extern "C" fn notify_handler<H: CallbackHandler>(opaque: *mut c_void) { |
| // SAFETY: |
| // Safe because we pass in opaque as exactly this type when constructing the Slirp object. |
| unsafe { (*(opaque as *mut Context<H>)).callback_handler.notify() } |
| } |
| |
| impl<H: CallbackHandler> Context<H> { |
| /// Create a new instance of the libslirp context. |
| /// |
| /// The parameters which are prefixed by "host" refer to the system on which libslirp runs; |
| /// for example, host_v4_address is the IP address of the host system that the guest will be |
| /// able to connect to. |
| /// |
| /// `host_v[4|6]_address` maps to the host's local loopback interface. |
| /// `dns_server_` options configure the DNS server provided on the virtual network by libslirp. |
| pub fn new( |
| disable_access_to_host: bool, |
| ipv4_enabled: bool, |
| virtual_network_v4_address: Ipv4Addr, |
| virtual_network_v4_mask: Ipv4Addr, |
| host_v4_address: Ipv4Addr, |
| ipv6_enabled: bool, |
| virtual_network_v6_address: Ipv6Addr, |
| virtual_network_v6_prefix_len: u8, |
| host_v6_address: Ipv6Addr, |
| host_hostname: Option<String>, |
| dhcp_start_addr: Ipv4Addr, |
| dns_server_v4_addr: Ipv4Addr, |
| dns_server_v6_addr: Ipv6Addr, |
| virtual_network_dns_search_domains: Vec<String>, |
| dns_server_domain_name: Option<String>, |
| callback_handler: H, |
| ) -> Result<Box<Context<H>>> { |
| let mut ret = Box::new(Context { |
| slirp: std::ptr::null_mut(), |
| callbacks: SlirpCb { |
| send_packet: Some(send_packet_handler::<H>), |
| guest_error: Some(guest_error_handler::<H>), |
| clock_get_ns: Some(clock_get_ns_handler::<H>), |
| timer_new: Some(timer_new_handler::<H>), |
| timer_free: Some(timer_free_handler::<H>), |
| timer_mod: Some(timer_mod_handler::<H>), |
| register_poll_fd: Some(register_poll_fd_handler::<H>), |
| unregister_poll_fd: Some(unregister_poll_fd_handler::<H>), |
| notify: Some(notify_handler::<H>), |
| }, |
| callback_handler, |
| }); |
| |
| let cstr_dns_search: Vec<_> = virtual_network_dns_search_domains |
| .iter() |
| .map(|arg| CString::new(arg.clone().into_bytes()).unwrap()) |
| .collect(); |
| let mut p_dns_search: Vec<_> = cstr_dns_search.iter().map(|arg| arg.as_ptr()).collect(); |
| p_dns_search.push(std::ptr::null()); |
| |
| let host_hostname = host_hostname.and_then(|s| CString::new(s).ok()); |
| let dns_server_domain_name = dns_server_domain_name.and_then(|s| CString::new(s).ok()); |
| let rust_context_ptr = &*ret as *const _ as *mut _; |
| |
| let as_ptr = |p: &Option<CString>| p.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()); |
| |
| let slirp_config = SlirpConfig { |
| version: 1, |
| restricted: 0, |
| disable_dhcp: false, |
| in_enabled: ipv4_enabled, |
| vnetwork: virtual_network_v4_address.into(), |
| vnetmask: virtual_network_v4_mask.into(), |
| vhost: host_v4_address.into(), |
| in6_enabled: ipv6_enabled, |
| vprefix_addr6: virtual_network_v6_address.into(), |
| vprefix_len: virtual_network_v6_prefix_len, |
| vhost6: host_v6_address.into(), |
| vhostname: as_ptr(&host_hostname), |
| tftp_server_name: std::ptr::null(), |
| tftp_path: std::ptr::null(), |
| bootfile: std::ptr::null(), |
| vdhcp_start: dhcp_start_addr.into(), |
| vnameserver: dns_server_v4_addr.into(), |
| vnameserver6: dns_server_v6_addr.into(), |
| vdnssearch: p_dns_search.as_ptr() as *mut *const _, |
| vdomainname: as_ptr(&dns_server_domain_name), |
| if_mtu: 0, |
| if_mru: 0, |
| disable_host_loopback: disable_access_to_host, |
| enable_emu: false, |
| outbound_addr: std::ptr::null(), |
| outbound_addr6: std::ptr::null(), |
| disable_dns: false, |
| }; |
| |
| // SAFETY: |
| // Safe because we pass valid pointers (or null as appropriate) as parameters and we check |
| // that the return value is valid. |
| let slirp = unsafe { |
| slirp_new( |
| &slirp_config, |
| &ret.callbacks, |
| // This value is passed to callbacks as opaque data, which allows those callbacks |
| // to get access to the Context struct. It allows them to invoke the appropriate |
| // methods on the CallbackHandler to notify it about new packets, get data about |
| // sockets that are ready for reading, etc. |
| rust_context_ptr, |
| ) |
| }; |
| assert!(!slirp.is_null()); |
| match ret.callback_handler.begin_read_from_guest() { |
| Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { |
| return Err(Error::Slirp(SlirpError::BrokenPipe(e))); |
| } |
| Err(e) => { |
| return Err(Error::Slirp(SlirpError::OverlappedError(e))); |
| } |
| _ => {} |
| } |
| ret.slirp = slirp; |
| Ok(ret) |
| } |
| |
| /// Reads from the guest & injects into Slirp. This method reads until an error is encountered |
| /// or io::ErrorKind::WouldBlock is returned by the callback_handler's read_from_guest. |
| pub fn handle_guest_input(&mut self) -> Result<()> { |
| loop { |
| match self.callback_handler.end_read_from_guest() { |
| Ok(ethernet_frame) => { |
| // SAFETY: |
| // Safe because the buffer (ethernet_frame) is valid & libslirp is provided |
| // with the data's underlying length. |
| unsafe { |
| slirp_input( |
| self.slirp, |
| ethernet_frame.as_ptr(), |
| ethernet_frame.len() as i32, |
| ); |
| } |
| } |
| Err(e) if e.kind() == std::io::ErrorKind::InvalidData => { |
| error!("error reading packet from guest: {}", e); |
| } |
| Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { |
| // No packets are available. Yield back to the Slirp loop. |
| break; |
| } |
| Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { |
| return Err(Error::Slirp(SlirpError::BrokenPipe(e))); |
| } |
| Err(_) => { |
| match self.callback_handler.begin_read_from_guest() { |
| Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { |
| return Err(Error::Slirp(SlirpError::BrokenPipe(e))); |
| } |
| Err(e) => { |
| return Err(Error::Slirp(SlirpError::OverlappedError(e))); |
| } |
| _ => {} |
| } |
| break; |
| } |
| } |
| match self.callback_handler.begin_read_from_guest() { |
| Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { |
| return Err(Error::Slirp(SlirpError::BrokenPipe(e))); |
| } |
| Err(e) => { |
| return Err(Error::Slirp(SlirpError::OverlappedError(e))); |
| } |
| _ => {} |
| } |
| } |
| Ok(()) |
| } |
| |
| pub fn connection_info(&mut self) -> &str { |
| str::from_utf8( |
| // TODO(b/315998194): Add safety comment |
| #[allow(clippy::undocumented_unsafe_blocks)] |
| unsafe { CStr::from_ptr(slirp_connection_info(self.slirp)) }.to_bytes(), |
| ) |
| .unwrap_or("") |
| } |
| |
| /// Requests libslirp provide the set of sockets & events that should be polled for. These |
| /// sockets are provided to you by 0..n calls to `add_poll_cb`. `add_poll_cb` must return an |
| /// integer (henceforth the socket reference) which libslirp can use to later request the |
| /// revents that came from polling on that socket. |
| /// |
| /// The `timeout` value expresses how long (in ms) the consumer intends to wait (at most) when |
| /// it invokes the polling function. libslirp will overwrite this with the time that the |
| /// consumer should wait. |
| pub fn pollfds_fill<F>(&mut self, timeout: &mut u32, mut add_poll_cb: F) |
| where |
| F: FnMut(i32, PollEvents) -> i32, |
| { |
| let cb = &mut (&mut add_poll_cb as &mut dyn FnMut(i32, PollEvents) -> i32); |
| // SAFETY: |
| // Safe because cb is only used while slirp_pollfds_fill is running, and self.slirp is |
| // guaranteed to be valid. |
| unsafe { |
| slirp_pollfds_fill( |
| self.slirp, |
| timeout, |
| Some(add_poll_handler_callback), |
| cb as *mut _ as *mut c_void, |
| ); |
| } |
| } |
| |
| /// Informs libslirp that polling has returned with some events on sockets that libslirp said |
| /// should be polled for when you called `pollfds_fill`. You provide the results of polling by |
| /// supplying `get_revents_cb`, which returns the `PollEvents` for each provided socket |
| /// reference. libslirp will call that function 0..n times to gather results from the polling |
| /// operation. |
| pub fn pollfds_poll<F>(&mut self, error: bool, mut get_revents_cb: F) |
| where |
| F: FnMut(i32) -> PollEvents, |
| { |
| let cb = &mut (&mut get_revents_cb as &mut dyn FnMut(i32) -> PollEvents); |
| |
| // SAFETY: |
| // Safe because cb is only used while slirp_pollfds_poll is running, and self.slirp is |
| // guaranteed to be valid. |
| unsafe { |
| slirp_pollfds_poll( |
| self.slirp, |
| error as i32, |
| Some(get_revents_handler_callback), |
| cb as *mut _ as *mut c_void, |
| ); |
| } |
| } |
| |
| pub fn state_save<F>(&mut self, mut write_cb: F) |
| where |
| F: FnMut(&[u8]) -> isize, |
| { |
| let cb = &mut (&mut write_cb as &mut dyn FnMut(&[u8]) -> isize); |
| // SAFETY: |
| // Safe because cb is only used while state_save is running, and self.slirp is |
| // guaranteed to be valid. |
| unsafe { |
| slirp_state_save( |
| self.slirp, |
| Some(write_handler_callback), |
| cb as *mut _ as *mut c_void, |
| ); |
| } |
| } |
| |
| pub fn state_load<F>(&mut self, version_id: i32, mut read_cb: F) -> i32 |
| where |
| F: FnMut(&mut [u8]) -> isize, |
| { |
| let cb = &mut (&mut read_cb as &mut dyn FnMut(&mut [u8]) -> isize); |
| // SAFETY: |
| // Safe because cb is only used while state_load is running, and self.slirp is |
| // guaranteed to be valid. While this function may fail, interpretation of the error code |
| // is the responsibility of the caller. |
| // |
| // TODO(nkgold): if state_load becomes used by crosvm, interpretation of the error code |
| // should occur here. |
| unsafe { |
| slirp_state_load( |
| self.slirp, |
| version_id, |
| Some(read_handler_callback), |
| cb as *mut _ as *mut c_void, |
| ) |
| } |
| } |
| |
| pub fn get_timers(&self) -> Box<dyn Iterator<Item = &RawDescriptor> + '_> { |
| self.callback_handler.get_timers() |
| } |
| |
| pub fn execute_timer(&mut self, timer: RawDescriptor) { |
| self.callback_handler.execute_timer(timer) |
| } |
| } |