use super::{NetworkBackend, NetworkBackendConfig, NetworkBackendEndpoint};
use boxlite_shared::errors::{BoxliteError, BoxliteResult};
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::net::UnixStream;
use std::process::{Child, Command};
#[derive(Debug)]
pub struct LibslirpBackend {
#[allow(dead_code)]
port_mappings: Vec<(u16, u16)>,
#[allow(dead_code)]
socket_fd: RawFd,
#[allow(dead_code)]
helper_process: Option<Child>,
}
impl LibslirpBackend {
pub fn new(config: NetworkBackendConfig) -> BoxliteResult<Self> {
tracing::info!(
port_count = config.port_mappings.len(),
"Initializing libslirp backend"
);
let (host_socket, guest_socket) = UnixStream::pair().map_err(|e| {
BoxliteError::Engine(format!("Failed to create socket pair for libslirp: {}", e))
})?;
let host_fd = host_socket.as_raw_fd();
let guest_fd = guest_socket.as_raw_fd();
tracing::debug!(
host_fd = host_fd,
guest_fd = guest_fd,
"Created Unix socket pair for libslirp"
);
let mut helper_args = vec![];
helper_args.push(format!("--fd={}", guest_fd));
for (host_port, guest_port) in &config.port_mappings {
let forward_spec = format!("tcp:127.0.0.1:{}::{}:tcp", host_port, guest_port);
helper_args.push(format!("--forward={}", forward_spec));
tracing::info!(
host_port = host_port,
guest_port = guest_port,
"Configuring libslirp port forwarding"
);
}
tracing::debug!(args = ?helper_args, "Spawning libslirp-helper");
let helper_process = Command::new("libslirp-helper")
.args(&helper_args)
.spawn()
.map_err(|e| {
BoxliteError::Engine(format!(
"Failed to spawn libslirp-helper (is it installed and in PATH?): {}",
e
))
})?;
tracing::info!(pid = helper_process.id(), "libslirp-helper started");
std::mem::forget(guest_socket);
Ok(Self {
port_mappings: config.port_mappings,
socket_fd: host_fd,
helper_process: Some(helper_process),
})
}
}
impl NetworkBackend for LibslirpBackend {
fn endpoint(&self) -> BoxliteResult<NetworkBackendEndpoint> {
Err(BoxliteError::Engine(
"libslirp backend not yet updated for new NetworkBackend trait".to_string(),
))
}
fn name(&self) -> &'static str {
"libslirp"
}
}
impl Drop for LibslirpBackend {
fn drop(&mut self) {
if let Some(mut process) = self.helper_process.take() {
tracing::debug!(pid = process.id(), "Terminating libslirp-helper");
let _ = process.kill();
let _ = process.wait();
}
}
}