diff --git a/Cargo.lock b/Cargo.lock index 72ee70505c..996997dd95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1260,6 +1260,7 @@ dependencies = [ name = "net_util" version = "0.1.0" dependencies = [ + "anyhow", "base", "cfg-if", "cros_async", @@ -1269,6 +1270,7 @@ dependencies = [ "metrics", "net_sys", "pcap-file", + "prebuilts", "remain", "serde", "smallvec", diff --git a/Cargo.toml b/Cargo.toml index a7f590fd9f..13fdd76b47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,7 +130,7 @@ all-linux = [ "wl-dmabuf", "x", ] -win64 = [ "balloon", "crash_report", "haxm", "stats" ] +win64 = [ "balloon", "crash_report", "haxm", "slirp", "stats" ] arc_quota = ["devices/arc_quota"] audio = ["devices/audio"] audio_cras = ["devices/audio_cras"] @@ -169,7 +169,7 @@ plugin = ["protos/plugin", "crosvm_plugin", "kvm", "kvm_sys", "protobuf"] plugin-render-server = [] power-monitor-powerd = ["arch/power-monitor-powerd"] qcow = ["disk/qcow"] -slirp = ["devices/slirp"] +slirp = ["devices/slirp", "net_util/slirp"] stats = ["devices/stats"] tpm = ["devices/tpm"] usb = ["devices/usb"] diff --git a/devices/src/virtio/mod.rs b/devices/src/virtio/mod.rs index da9dd16f6b..f6c7ddcb19 100644 --- a/devices/src/virtio/mod.rs +++ b/devices/src/virtio/mod.rs @@ -15,7 +15,6 @@ mod interrupt; mod iommu; mod queue; mod rng; -#[cfg(unix)] mod sys; #[cfg(any(feature = "tpm", feature = "vtpm"))] mod tpm; @@ -80,6 +79,8 @@ cfg_if::cfg_if! { #[cfg(feature = "slirp")] pub use self::net::*; + #[cfg(feature = "slirp")] + pub use self::sys::windows::NetExt; pub use self::vsock::*; } else { compile_error!("Unsupported platform"); diff --git a/devices/src/virtio/vhost/user/device/net/sys/windows.rs b/devices/src/virtio/vhost/user/device/net/sys/windows.rs index bc60143de1..628f9e9561 100644 --- a/devices/src/virtio/vhost/user/device/net/sys/windows.rs +++ b/devices/src/virtio/vhost/user/device/net/sys/windows.rs @@ -163,13 +163,11 @@ pub(in crate::virtio::vhost::user::device::net) fn start_queue() + .unwrap(); + // TODO(b:242204245) build libslirp locally on windows from build.rs. + prebuilts::download_prebuilts( + "libslirp", + version, + &[ + SLIRP_DLL, + SLIRP_LIB, + #[cfg(unix)] + // When compiling with mingw64 to run under wine64, we need glib as slirp links + // against it. + GLIB_FILENAME, + ], + ) + .unwrap(); + } // For unix, libslirp-sys's build script will make the appropriate linking calls to pkg_config. } diff --git a/net_util/prebuilts_version b/net_util/prebuilts_version new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/net_util/prebuilts_version @@ -0,0 +1 @@ +1 diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs index 737ca50be8..1cff1a35e4 100644 --- a/net_util/src/lib.rs +++ b/net_util/src/lib.rs @@ -33,6 +33,11 @@ use serde::Serialize; pub use sys::TapT; use thiserror::Error as ThisError; +#[cfg(all(feature = "slirp"))] +pub mod slirp; +#[cfg(all(feature = "slirp", windows))] +pub use slirp::Slirp; + #[sorted] #[derive(ThisError, Debug)] pub enum Error { @@ -51,18 +56,23 @@ pub enum Error { /// Couldn't open /dev/net/tun. #[error("failed to open /dev/net/tun: {0}")] OpenTun(SysError), + #[cfg(all(feature = "slirp", windows))] + #[error("slirp related error")] + Slirp(slirp::SlirpError), } pub type Result = std::result::Result; impl Error { pub fn sys_error(&self) -> SysError { - match *self { - Error::CreateSocket(e) => e, - Error::OpenTun(e) => e, - Error::CreateTap(e) => e, - Error::CloneTap(e) => e, - Error::IoctlError(e) => e, + match &*self { + Error::CreateSocket(e) => *e, + Error::OpenTun(e) => *e, + Error::CreateTap(e) => *e, + Error::CloneTap(e) => *e, + Error::IoctlError(e) => *e, + #[cfg(all(feature = "slirp", windows))] + Error::Slirp(e) => e.sys_error(), } } } diff --git a/net_util/src/slirp.rs b/net_util/src/slirp.rs index fee082a519..da1fd25362 100644 --- a/net_util/src/slirp.rs +++ b/net_util/src/slirp.rs @@ -6,6 +6,8 @@ //! level interfaces to libslirp that are used to implement that loop, and //! diagnostic tools. +#![cfg(windows)] + #[path = "../../third_party/libslirp-rs/src/context.rs"] pub mod context; @@ -15,6 +17,44 @@ pub mod packet_ring_buffer; pub mod sys; pub use sys::Slirp; +use base::Error as SysError; +use remain::sorted; +use thiserror::Error as ThisError; + /// Length includes space for an ethernet frame & the vnet header. See the virtio spec for details: /// pub const ETHERNET_FRAME_SIZE: usize = 1526; + +#[cfg(windows)] +#[sorted] +#[derive(ThisError, Debug)] +pub enum SlirpError { + #[error("pipe was closed: {0}")] + BrokenPipe(std::io::Error), + #[error("failed to clone object: {0}")] + CloneFailed(std::io::Error), + #[error("overlapped operation failed: {0}")] + OverlappedError(std::io::Error), + /// Error encountered while in a Slirp related poll operation. + #[error("slirp poll failed: {0}")] + SlirpIOPollError(std::io::Error), + /// Error encountered while in a Slirp related poll operation. + #[error("slirp poll failed: {0}")] + SlirpPollError(SysError), + #[error("WSAStartup failed with code: {0}")] + WSAStartupError(SysError), +} + +#[cfg(windows)] +impl SlirpError { + pub fn sys_error(&self) -> SysError { + match &*self { + SlirpError::BrokenPipe(e) => SysError::new(e.raw_os_error().unwrap_or_default()), + SlirpError::CloneFailed(e) => SysError::new(e.raw_os_error().unwrap_or_default()), + SlirpError::OverlappedError(e) => SysError::new(e.raw_os_error().unwrap_or_default()), + SlirpError::SlirpIOPollError(e) => SysError::new(e.raw_os_error().unwrap_or_default()), + SlirpError::SlirpPollError(e) => *e, + SlirpError::WSAStartupError(e) => *e, + } + } +} diff --git a/net_util/src/slirp/sys/windows.rs b/net_util/src/slirp/sys/windows.rs index 651df6d1a3..71001ea909 100644 --- a/net_util/src/slirp/sys/windows.rs +++ b/net_util/src/slirp/sys/windows.rs @@ -30,6 +30,7 @@ use cros_async::IntoAsync; use serde::Deserialize; use serde::Serialize; +use crate::slirp::SlirpError; use crate::slirp::ETHERNET_FRAME_SIZE; use crate::Error; use crate::MacAddress; @@ -69,7 +70,7 @@ impl Slirp { #[cfg(feature = "slirp-ring-capture")] let slirp_capture_file_clone = slirp_capture_file.clone(); slirp_thread = thread::spawn(move || { - let disable_access_to_host = !cfg!("guest-to-host-net-loopback"); + let disable_access_to_host = !cfg!(feature = "guest-to-host-net-loopback"); handler::start_slirp( slirp_pipe, @@ -100,7 +101,10 @@ impl Slirp { fn try_clone(&self) -> Result { Ok(Slirp { - guest_pipe: self.guest_pipe.try_clone().map_err(Error::CloneFailed)?, + guest_pipe: self + .guest_pipe + .try_clone() + .map_err(|e| Error::Slirp(SlirpError::CloneFailed(e)))?, overlapped_wrapper: OverlappedWrapper::new(true).unwrap(), slirp_thread: None, }) @@ -122,7 +126,7 @@ impl Slirp { // because libslirp logs *everything* as a debug entry. std::env::set_var("G_MESSAGES_DEBUG", "all"); - let disable_access_to_host = !cfg!("guest-to-host-net-loopback"); + let disable_access_to_host = !cfg!(feature = "guest-to-host-net-loopback"); info!("starting slirp loop..."); match handler::start_slirp( @@ -132,7 +136,9 @@ impl Slirp { #[cfg(feature = "slirp-ring-capture")] slirp_capture_file.take(), ) { - Err(Error::BrokenPipe(e)) => warn!("exited slirp listening loop: {}", e), + Err(Error::Slirp(SlirpError::BrokenPipe(e))) => { + warn!("exited slirp listening loop: {}", e) + } Err(e) => panic!("error while running slirp listening loop: {}", e), _ => {} } @@ -226,7 +232,7 @@ impl TapTCommon for Slirp { unimplemented!("not used by Slirp"); } - fn if_flags(&self) -> i32 { + fn if_flags(&self) -> u32 { // This function is unused by the Slirp code paths. unimplemented!("not used by Slirp"); } diff --git a/net_util/src/slirp/sys/windows/handler.rs b/net_util/src/slirp/sys/windows/handler.rs index 8cf8b543e8..d316a58865 100644 --- a/net_util/src/slirp/sys/windows/handler.rs +++ b/net_util/src/slirp/sys/windows/handler.rs @@ -21,20 +21,20 @@ use base::AsRawDescriptor; use base::Descriptor; use base::Error as SysError; use base::Event; +use base::EventExt; use base::EventToken; -use base::EventWindows; use base::RawDescriptor; use base::Timer; use base::WaitContext; +use base::WaitContextExt; use data_model::DataInit; -use data_model::Le16; use metrics::MetricEventType; use metrics::PeriodicLogger; #[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))] use pcap_file::pcap::PcapWriter; use smallvec::SmallVec; -use virtio_sys::virtio_net_hdr; -use virtio_sys::virtio_net_hdr_mrg_rxbuf; +use virtio_sys::virtio_net::virtio_net_hdr; +use virtio_sys::virtio_net::virtio_net_hdr_mrg_rxbuf; use winapi::shared::minwindef::MAKEWORD; use winapi::um::winnt::LONG; use winapi::um::winnt::SHORT; @@ -61,6 +61,7 @@ use crate::slirp::context::Context; use crate::slirp::context::PollEvents; #[cfg(feature = "slirp-ring-capture")] use crate::slirp::packet_ring_buffer::PacketRingBuffer; +use crate::slirp::SlirpError; use crate::slirp::ETHERNET_FRAME_SIZE; use crate::Error; use crate::Result; @@ -113,7 +114,7 @@ impl CallbackHandler for Handler { hdr_len: 0, csum_start: 0, csum_offset: 0, - gso_type: virtio_sys::VIRTIO_NET_HDR_GSO_NONE as u8, + gso_type: virtio_sys::virtio_net::VIRTIO_NET_HDR_GSO_NONE as u8, }, num_buffers: 1, }; @@ -359,7 +360,7 @@ impl<'a> EventSelectedSocket<'a> { ) }; if res == SOCKET_ERROR { - return Err(Error::SlirpIOPollError(last_wsa_error())); + return Err(Error::Slirp(SlirpError::SlirpIOPollError(last_wsa_error()))); } Ok(EventSelectedSocket { socket, event }) } @@ -402,28 +403,32 @@ fn poll<'a>( selected_sockets.push(EventSelectedSocket::new(*socket, socket_event_handle)?); } - wait_ctx.clear().map_err(Error::SlirpPollError)?; + wait_ctx + .clear() + .map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?; for (i, handle) in handles.iter().enumerate() { match wait_ctx.add(*handle, Token::EventHandleReady(i)) { Ok(v) => v, Err(e) => { - return Err(Error::SlirpPollError(e)); + return Err(Error::Slirp(SlirpError::SlirpPollError(e))); } } } match wait_ctx.add(socket_event_handle, Token::SocketReady) { Ok(v) => v, Err(e) => { - return Err(Error::SlirpPollError(e)); + return Err(Error::Slirp(SlirpError::SlirpPollError(e))); } } let events = if let Some(timeout) = timeout { wait_ctx .wait_timeout(timeout) - .map_err(Error::SlirpPollError)? + .map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))? } else { - wait_ctx.wait().map_err(Error::SlirpPollError)? + wait_ctx + .wait() + .map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))? }; let tokens: Vec = events @@ -446,7 +451,7 @@ fn poll<'a>( let socket_results = if sockets.is_empty() { Vec::new() } else { - poll_sockets(sockets).map_err(Error::SlirpIOPollError)? + poll_sockets(sockets).map_err(|e| Error::Slirp(SlirpError::SlirpIOPollError(e)))? }; Ok((handle_results, socket_results)) @@ -466,7 +471,9 @@ impl WSAContext { // Safe because ctx.data is guaranteed to exist, and we check the return code. let err = unsafe { WSAStartup(MAKEWORD(2, 0), &mut ctx.data) }; if err != 0 { - Err(Error::WSAStartupError(SysError::new(err))) + Err(Error::Slirp(SlirpError::WSAStartupError(SysError::new( + err, + )))) } else { Ok(ctx) } @@ -506,8 +513,10 @@ pub fn start_slirp( let shutdown_event_handle = shutdown_event.as_raw_descriptor(); // Stack data for the poll function. - let wait_ctx: WaitContext = WaitContext::new().map_err(Error::SlirpPollError)?; - let socket_event_handle = Event::new_auto_reset().map_err(Error::SlirpPollError)?; + let wait_ctx: WaitContext = + WaitContext::new().map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?; + let socket_event_handle = + Event::new_auto_reset().map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?; 'slirp: loop { // Request the FDs that we should poll from Slirp. Slirp provides them to us by way of a diff --git a/src/sys/windows/main.rs b/src/sys/windows/main.rs index a9586454e9..0df0480eb6 100644 --- a/src/sys/windows/main.rs +++ b/src/sys/windows/main.rs @@ -74,6 +74,7 @@ pub(crate) fn run_slirp(args: RunSlirpCommand) -> Result<()> { let slirp_config = bootstrap_tube.recv::().unwrap(); + #[cfg(feature = "sandbox")] if let Some(mut target) = sandbox::TargetServices::get() .exit_context(Exit::SandboxError, "sandbox operation failed")? { diff --git a/third_party/libslirp-rs/src/context.rs b/third_party/libslirp-rs/src/context.rs index 1908179657..d17dd2df8d 100644 --- a/third_party/libslirp-rs/src/context.rs +++ b/third_party/libslirp-rs/src/context.rs @@ -52,6 +52,7 @@ use base::error; use base::RawDescriptor; use libslirp_sys::*; +use crate::slirp::SlirpError; use crate::Error; use crate::Result; @@ -512,10 +513,10 @@ impl Context { assert!(!slirp.is_null()); match ret.callback_handler.begin_read_from_guest() { Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { - return Err(Error::BrokenPipe(e)); + return Err(Error::Slirp(SlirpError::BrokenPipe(e))); } Err(e) => { - return Err(Error::OverlappedError(e)); + return Err(Error::Slirp(SlirpError::OverlappedError(e))); } _ => {} } @@ -545,15 +546,15 @@ impl Context { break; } Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { - return Err(Error::BrokenPipe(e)); + 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::BrokenPipe(e)); + return Err(Error::Slirp(SlirpError::BrokenPipe(e))); } Err(e) => { - return Err(Error::OverlappedError(e)); + return Err(Error::Slirp(SlirpError::OverlappedError(e))); } _ => {} } @@ -562,10 +563,10 @@ impl Context { } match self.callback_handler.begin_read_from_guest() { Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { - return Err(Error::BrokenPipe(e)); + return Err(Error::Slirp(SlirpError::BrokenPipe(e))); } Err(e) => { - return Err(Error::OverlappedError(e)); + return Err(Error::Slirp(SlirpError::OverlappedError(e))); } _ => {} } diff --git a/tools/install-deps b/tools/install-deps index d79bac03f6..bf9371942b 100755 --- a/tools/install-deps +++ b/tools/install-deps @@ -22,7 +22,9 @@ sudo apt-get install --yes --no-install-recommends \ libdbus-1-dev \ libdrm-dev \ libepoxy-dev \ + libglib2.0-dev \ libguestfs-tools \ + libslirp-dev \ libssl-dev \ libswscale-dev \ libudev-dev \