From 06b87a29ab6e0c0b44667b897cf5333bad1ce352 Mon Sep 17 00:00:00 2001 From: phact Date: Tue, 11 Aug 2026 05:08:30 -0400 Subject: [PATCH] Add --vsock option for guest-to-host Unix socket IPC Allow mapping guest AF_VSOCK ports to Unix sockets on the host via libkrun's krun_add_vsock_port(). A guest connection to (CID 2, PORT) is proxied to the host socket at PATH, so host services can be reached without giving the guest any network access. The flag can be repeated to map multiple ports: krunvm start myvm --vsock 8765:/run/bridge.sock -- ... Only wired into 'krunvm start' for now; the mapping is per-invocation and intentionally not persisted in the VM config. Signed-off-by: phact --- src/bindings.rs | 1 + src/commands/start.rs | 46 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/bindings.rs b/src/bindings.rs index 12f30f0..6bcd071 100644 --- a/src/bindings.rs +++ b/src/bindings.rs @@ -13,6 +13,7 @@ extern "C" { pub fn krun_set_port_map(ctx: u32, port_map: *const *const c_char) -> i32; pub fn krun_set_workdir(ctx: u32, workdir_path: *const c_char) -> i32; pub fn krun_add_virtiofs(ctx: u32, tag: *const c_char, path: *const c_char) -> i32; + pub fn krun_add_vsock_port(ctx: u32, port: u32, filepath: *const c_char) -> i32; pub fn krun_set_exec( ctx: u32, exec_path: *const c_char, diff --git a/src/commands/start.rs b/src/commands/start.rs index 5c56c97..c70a415 100644 --- a/src/commands/start.rs +++ b/src/commands/start.rs @@ -44,6 +44,12 @@ pub struct StartCmd { /// env(s) in format "key=value" to be exposed to the VM #[arg(long = "env")] envs: Option>, + + /// vsock port to host Unix socket mapping(s) in format + /// "PORT:/host/path.sock". Guest connections to AF_VSOCK + /// (CID 2, PORT) are proxied to the Unix socket on the host. + #[arg(long = "vsock")] + vsocks: Option>, } impl StartCmd { @@ -76,11 +82,39 @@ impl StartCmd { Vec::new() }; + let vsock_ports: Vec<(u32, String)> = self + .vsocks + .unwrap_or_default() + .into_iter() + .map(|val| match val.split_once(':') { + Some((port, path)) => match port.parse::() { + Ok(port) => (port, path.to_string()), + Err(_) => { + println!("Invalid vsock port in mapping: {}", val); + std::process::exit(-1); + } + }, + None => { + println!("Invalid vsock mapping (expected PORT:PATH): {}", val); + std::process::exit(-1); + } + }) + .collect(); + set_rlimits(); let _file = set_lock(&rootfs); - unsafe { exec_vm(vmcfg, &rootfs, self.command.as_deref(), vm_args, env_pairs) }; + unsafe { + exec_vm( + vmcfg, + &rootfs, + self.command.as_deref(), + vm_args, + env_pairs, + vsock_ports, + ) + }; umount_container(cfg, vmcfg).expect("Error unmounting container"); } @@ -143,6 +177,7 @@ unsafe fn exec_vm( cmd: Option<&str>, args: Vec, env_pairs: Vec, + vsock_ports: Vec<(u32, String)>, ) { //bindings::krun_set_log_level(9); @@ -185,6 +220,15 @@ unsafe fn exec_vm( std::process::exit(-1); } + for (port, path) in vsock_ports.iter() { + let c_path = CString::new(path.as_str()).unwrap(); + let ret = bindings::krun_add_vsock_port(ctx, *port, c_path.as_ptr()); + if ret < 0 { + println!("Error adding vsock port {} -> {}", port, path); + std::process::exit(-1); + } + } + if !vmcfg.workdir.is_empty() { let c_workdir = CString::new(vmcfg.workdir.clone()).unwrap(); let ret = bindings::krun_set_workdir(ctx, c_workdir.as_ptr());