Use AF_UNIX for OpenVMM RPC connections

This commit is contained in:
Daman Mulye committed 2026-09-15 12:07:30 -07:00
1 parent 180e104a38
commit eb59238333
8 files changed
+77 -53

No files matched your search

+1 -1
View File
@@ -26,7 +26,7 @@ add_custom_command(
OUTPUT ${WSLOPENVMM_DLL}
COMMAND ${CMAKE_COMMAND} -E env "VM_SERVICE_PROTO=${VM_SERVICE_PROTO}" ${CARGO_EXECUTABLE} build --manifest-path ${CMAKE_CURRENT_LIST_DIR}/Cargo.toml --target ${RUST_TARGET} --target-dir ${CARGO_TARGET_DIR} ${CARGO_BUILD_ARGUMENTS}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CARGO_OUTPUT} ${WSLOPENVMM_DLL}
DEPENDS ${VM_SERVICE_PROTO} Cargo.lock Cargo.toml build.rs src/client.rs src/lib.rs src/named_pipe.rs wslopenvmm.h
DEPENDS ${VM_SERVICE_PROTO} Cargo.lock Cargo.toml build.rs src/client.rs src/lib.rs src/af_unix.rs wslopenvmm.h
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
VERBATIM)
+1
View File
@@ -1149,6 +1149,7 @@ dependencies = [
"prost",
"prost-types",
"protoc-bin-vendored",
"socket2",
"tokio",
"tokio-stream",
"tonic",
+1
View File
@@ -11,6 +11,7 @@ crate-type = ["cdylib"]
[dependencies]
hyper-util = { version = "0.1", features = ["tokio"] }
parking_lot = "0.12"
socket2 = "0.6"
prost = "0.14"
prost-types = "0.14"
tonic = "0.14"
+65
View File
@@ -0,0 +1,65 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
use std::io;
use std::path::{Path, PathBuf};
use std::time::Duration;
use hyper_util::rt::TokioIo;
use socket2::{Domain, SockAddr, Socket, Type};
use tokio::net::TcpStream;
use tonic::transport::{Channel, Endpoint};
use tower::service_fn;
/// Connects a Tonic channel to the OpenVMM AF_UNIX listener.
pub async fn connect_channel(
socket_path: PathBuf,
timeout: Duration,
) -> Result<Channel, tonic::transport::Error> {
Endpoint::from_static("http://[::]:50051")
.connect_timeout(timeout)
.connect_with_connector(service_fn(move |_| {
let socket_path = socket_path.clone();
async move { open_socket(&socket_path, timeout).await.map(TokioIo::new) }
}))
.await
}
async fn open_socket(path: &Path, timeout: Duration) -> io::Result<TcpStream> {
let address = SockAddr::unix(path)?;
tokio::time::timeout(timeout, async {
loop {
match connect_socket(&address).await {
Ok(stream) => return Ok(stream),
Err(error)
if matches!(
error.kind(),
io::ErrorKind::NotFound | io::ErrorKind::ConnectionRefused
) => {}
Err(error) => return Err(error),
}
tokio::time::sleep(Duration::from_millis(20)).await;
}
})
.await
.map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "timed out connecting to OpenVMM"))?
}
/// Registers the Winsock AF_UNIX socket with Tokio's Windows socket reactor.
/// The socket remains AF_UNIX; TCP-specific options and address queries must
/// not be used on the returned stream.
async fn connect_socket(address: &SockAddr) -> io::Result<TcpStream> {
let socket = Socket::new(Domain::UNIX, Type::STREAM, None)?;
socket.set_nonblocking(true)?;
match socket.connect(address) {
Ok(()) => {}
Err(error) if error.kind() == io::ErrorKind::WouldBlock => {}
Err(error) => return Err(error),
}
let stream = TcpStream::from_std(socket.into())?;
stream.writable().await?;
if let Some(error) = stream.take_error()? {
return Err(error);
}
Ok(stream)
}
+3 -3
View File
@@ -13,7 +13,7 @@ use windows::Win32::Foundation::{E_FAIL, E_INVALIDARG, ERROR_ALREADY_EXISTS, ERR
use windows::core::{GUID, HRESULT};
use windows_sys::Win32::Networking::WinSock::{AF_INET, AF_INET6};
use crate::{named_pipe, vmservice};
use crate::{af_unix, vmservice};
#[cfg(test)]
mod tests;
@@ -40,7 +40,7 @@ impl VmConfigBuilder {
}
}
pub fn create_vm(&self, pipe_name: String, timeout_ms: u32) -> Result<VmHandle, HRESULT> {
pub fn create_vm(&self, socket_path: String, timeout_ms: u32) -> Result<VmHandle, HRESULT> {
let runtime = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
@@ -49,7 +49,7 @@ impl VmConfigBuilder {
Err(_) => return Err(E_FAIL),
};
let timeout = Duration::from_millis(u64::from(timeout_ms));
let channel = match runtime.block_on(named_pipe::connect_channel(pipe_name, timeout)) {
let channel = match runtime.block_on(af_unix::connect_channel(socket_path.into(), timeout)) {
Ok(channel) => channel,
Err(error) if error.to_string().contains("timed out") => {
return Err(HRESULT::from_win32(WAIT_TIMEOUT.0));
+5 -5
View File
@@ -3,7 +3,7 @@
#![expect(clippy::missing_safety_doc)]
mod client;
pub mod named_pipe;
pub mod af_unix;
pub mod vmservice {
tonic::include_proto!("vmservice");
@@ -200,7 +200,7 @@ pub unsafe extern "C" fn WslOpenVmmConfigSetVirtioConsolePath(
#[unsafe(no_mangle)]
pub unsafe extern "C" fn WslOpenVmmCreateVm(
config: *mut *mut WslOpenVmmConfig,
pipe_name: *const u16,
socket_path: *const u16,
timeout_ms: u32,
vm: *mut *mut WslOpenVmmVm,
) -> i32 {
@@ -215,13 +215,13 @@ pub unsafe extern "C" fn WslOpenVmmCreateVm(
if config_handle.is_null() {
return E_POINTER.0;
}
let pipe_name = match unsafe { string_from_wide(pipe_name) } {
Ok(pipe_name) => pipe_name,
let socket_path = match unsafe { string_from_wide(socket_path) } {
Ok(socket_path) => socket_path,
Err(error) => return error.0,
};
let config_box = unsafe { Box::from_raw(config_handle) };
let result = config_box.0.lock().create_vm(pipe_name, timeout_ms);
let result = config_box.0.lock().create_vm(socket_path, timeout_ms);
let client = match result {
Ok(client) => client,
Err(error) => {
-43
View File
@@ -1,43 +0,0 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
use std::io;
use std::time::Duration;
use hyper_util::rt::TokioIo;
use tokio::net::windows::named_pipe::{ClientOptions, NamedPipeClient};
use tonic::transport::{Channel, Endpoint};
use tower::service_fn;
use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_PIPE_BUSY};
/// Connects a Tonic channel to the OpenVMM named-pipe listener.
pub async fn connect_channel(
pipe_name: String,
timeout: Duration,
) -> Result<Channel, tonic::transport::Error> {
let endpoint = Endpoint::from_static("http://[::]:50051").connect_timeout(timeout);
endpoint
.connect_with_connector(service_fn(move |_| {
let pipe_name = pipe_name.clone();
async move {
open_pipe(&pipe_name, timeout).await.map(TokioIo::new)
}
}))
.await
}
async fn open_pipe(pipe_name: &str, timeout: Duration) -> io::Result<NamedPipeClient> {
tokio::time::timeout(timeout, async {
loop {
match ClientOptions::new().open(pipe_name) {
Ok(pipe) => return Ok(pipe),
Err(error)
if matches!(error.raw_os_error(), Some(code)
if code == ERROR_FILE_NOT_FOUND.0 as i32 || code == ERROR_PIPE_BUSY.0 as i32) => {}
Err(error) => return Err(error),
}
tokio::time::sleep(Duration::from_millis(20)).await;
}
})
.await
.map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "timed out connecting to OpenVMM"))?
}
+1 -1
View File
@@ -26,7 +26,7 @@ __declspec(dllimport) HRESULT WslOpenVmmConfigSetConsommeNic(_Inout_ WslOpenVmmC
__declspec(dllimport) HRESULT WslOpenVmmConfigAddSerialPort(_Inout_ WslOpenVmmConfig* Config, _In_ UINT32 Port, _In_ LPCWSTR PipeName);
__declspec(dllimport) HRESULT WslOpenVmmConfigSetVirtioConsolePath(_Inout_ WslOpenVmmConfig* Config, _In_ LPCWSTR Path);
// On success, this consumes Config and sets it to nullptr. On failure, Config remains valid and Vm is nullptr.
__declspec(dllimport) HRESULT WslOpenVmmCreateVm(_Inout_ WslOpenVmmConfig** Config, _In_ LPCWSTR PipeName, _In_ UINT32 TimeoutMs, _Out_ WslOpenVmmVm** Vm);
__declspec(dllimport) HRESULT WslOpenVmmCreateVm(_Inout_ WslOpenVmmConfig** Config, _In_ LPCWSTR SocketPath, _In_ UINT32 TimeoutMs, _Out_ WslOpenVmmVm** Vm);
__declspec(dllimport) void WslOpenVmmDestroyVm(_In_opt_ WslOpenVmmVm* Vm);
__declspec(dllimport) HRESULT WslOpenVmmVmResume(_Inout_ WslOpenVmmVm* Vm);