use crate::error::{VZError, VZResult};
use crate::shim_ffi;
use std::ffi::{CString, c_void};
use std::os::unix::io::RawFd;
use std::ptr;
pub const VZ_DEFAULT_MTU: usize = 1500;
pub const VZ_ENHANCED_MTU: u64 = 4000;
#[must_use]
pub fn desired_network_mtu() -> usize {
if std::env::var_os("ARCBOX_DIAG_NET_MTU_4000").is_some() {
VZ_ENHANCED_MTU as usize
} else {
VZ_DEFAULT_MTU
}
}
fn mtu_override() -> u64 {
let desired = desired_network_mtu();
if desired > VZ_DEFAULT_MTU {
tracing::warn!(
"VZ network MTU raised to {desired} (ARCBOX_DIAG_NET_MTU_4000; \
expect ABX-423 device stalls under sustained bulk transfer)"
);
desired as u64
} else {
tracing::info!("VZ network MTU at default {VZ_DEFAULT_MTU} (ABX-423 mitigation)");
0
}
}
pub struct NetworkDeviceConfiguration {
inner: *mut c_void,
}
unsafe impl Send for NetworkDeviceConfiguration {}
impl NetworkDeviceConfiguration {
pub fn nat() -> VZResult<Self> {
Self::nat_inner(None)
}
pub fn nat_with_mac(mac_address: &str) -> VZResult<Self> {
Self::nat_inner(Some(mac_address))
}
pub fn file_handle(fd: RawFd) -> VZResult<Self> {
Self::file_handle_inner(fd, None)
}
pub fn file_handle_with_mac(fd: RawFd, mac_address: &str) -> VZResult<Self> {
Self::file_handle_inner(fd, Some(mac_address))
}
fn nat_inner(mac_address: Option<&str>) -> VZResult<Self> {
let c_mac = mac_cstring(mac_address)?;
let mac_ptr = c_mac.as_ref().map_or(ptr::null(), |m| m.as_ptr());
unsafe {
let mut error: *mut std::ffi::c_char = ptr::null_mut();
let obj = shim_ffi::abx_network_nat_new(mac_ptr, mtu_override(), &raw mut error);
Self::from_shim(obj, error)
}
}
fn file_handle_inner(fd: RawFd, mac_address: Option<&str>) -> VZResult<Self> {
if unsafe { libc::fcntl(fd, libc::F_GETFD) } < 0 {
return Err(VZError::InvalidConfiguration(format!(
"invalid network fd {fd}: {}",
std::io::Error::last_os_error()
)));
}
let c_mac = mac_cstring(mac_address)?;
let mac_ptr = c_mac.as_ref().map_or(ptr::null(), |m| m.as_ptr());
unsafe {
let mut error: *mut std::ffi::c_char = ptr::null_mut();
let obj =
shim_ffi::abx_network_file_handle_new(fd, mac_ptr, mtu_override(), &raw mut error);
Self::from_shim(obj, error)
}
}
unsafe fn from_shim(
obj: *mut std::ffi::c_void,
error: *mut std::ffi::c_char,
) -> VZResult<Self> {
if obj.is_null() {
return Err(VZError::InvalidConfiguration(unsafe {
shim_ffi::take_error_string(error)
}));
}
Ok(Self { inner: obj })
}
#[must_use]
pub(crate) fn into_ptr(self) -> *mut c_void {
let ptr = self.inner;
std::mem::forget(self);
ptr
}
}
fn mac_cstring(mac: Option<&str>) -> VZResult<Option<CString>> {
mac.map(|m| {
CString::new(m).map_err(|_| VZError::InvalidConfiguration(format!("MAC contains NUL: {m}")))
})
.transpose()
}
impl Drop for NetworkDeviceConfiguration {
fn drop(&mut self) {
if !self.inner.is_null() {
unsafe { shim_ffi::abx_object_release(self.inner.cast()) };
}
}
}