use nix::sched::{CloneFlags, setns};
use nix::sys::socket::{AddressFamily, SockFlag, SockType, socket};
use nix::unistd::{self, ForkResult, Pid};
use sendfd::{RecvWithFd, SendWithFd};
use std::fs::File;
use std::net::Ipv4Addr;
use std::os::fd::{AsRawFd, RawFd};
use std::os::unix::net::UnixDatagram;
use tun_rs::{DeviceBuilder, Layer, SyncDevice};
use super::SetupStatus;
use crate::error::*;
nix::ioctl_readwrite_bad!(get_iface_flags, libc::SIOCGIFFLAGS, libc::ifreq);
nix::ioctl_readwrite_bad!(set_iface_flags, libc::SIOCSIFFLAGS, libc::ifreq);
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug)]
pub enum RustSlirpMode {
TUN,
TAP,
}
#[derive(Clone, Debug)]
pub struct RustSlirp {
mode: RustSlirpMode,
address: Ipv4Addr,
netmask: Ipv4Addr,
destination: Option<Ipv4Addr>,
mtu: u16,
}
impl RustSlirp {
pub fn mode(&mut self, mode: RustSlirpMode) -> &mut Self {
self.mode = mode;
self
}
pub fn address(&mut self, address: Ipv4Addr) -> &mut Self {
self.address = address;
self
}
pub fn netmask(&mut self, netmask: Ipv4Addr) -> &mut Self {
self.netmask = netmask;
self
}
pub fn destination(&mut self, destination: Ipv4Addr) -> &mut Self {
self.destination = Some(destination);
self
}
pub fn mtu(&mut self, mtu: u16) -> &mut Self {
self.mtu = mtu;
self
}
}
impl Default for RustSlirp {
fn default() -> Self {
Self {
mode: RustSlirpMode::TUN,
address: Ipv4Addr::new(10, 0, 0, 1),
netmask: Ipv4Addr::new(255, 255, 255, 0),
destination: None,
mtu: 1500,
}
}
}
impl RustSlirp {
pub(crate) fn mainp_setup(rustslirp: &Self, target: Pid) -> Result<SetupStatus> {
let (sock_a, sock_z) = UnixDatagram::pair().map_err(ProcessErrorKind::StdIoError)?;
match unsafe { unistd::fork() } {
Ok(ForkResult::Parent { .. }) => {
drop(sock_z);
let fd = Self::mainp_setup_parent(sock_a).map_err(|err| {
let errmsg = format!("{err}");
ProcessErrorKind::SetupNetworkFailed(errmsg)
})?;
Ok(SetupStatus::RustSlirpTapFd(fd))
}
Ok(ForkResult::Child) => {
drop(sock_a);
Self::mainp_setup_child(&sock_z, rustslirp, target).map_err(|err| {
let errmsg = format!("rustslirp: {err}");
_ = sock_z.send_with_fd(errmsg.as_bytes(), &[]);
unsafe { libc::_exit(1) }
});
unsafe { libc::_exit(0) }
}
Err(err) => {
let errmsg = format!("{err}");
Err(ProcessErrorKind::SetupNetworkFailed(errmsg))?
}
}
}
fn mainp_setup_parent(sock: UnixDatagram) -> Result<RawFd> {
let mut buf = [0u8; 1024];
let mut fds = [0 as RawFd];
let (bufsize, _) = sock
.recv_with_fd(&mut buf, &mut fds)
.map_err(ProcessErrorKind::StdIoError)?;
if bufsize != 0 {
let errmsg = String::from_utf8_lossy(&buf[..bufsize]);
Err(Error::UnError(errmsg.to_string()))
} else {
Ok(fds[0])
}
}
fn mainp_setup_child(sock: &UnixDatagram, rustslirp: &RustSlirp, target: Pid) -> Result<()> {
let fd1 =
File::open(format!("/proc/{}/ns/net", target)).map_err(ProcessErrorKind::StdIoError)?;
let fd2 = File::open(format!("/proc/{}/ns/user", target))
.map_err(ProcessErrorKind::StdIoError)?;
setns(fd2, CloneFlags::CLONE_NEWUSER).map_err(ProcessErrorKind::NixError)?;
setns(fd1, CloneFlags::CLONE_NEWNET).map_err(ProcessErrorKind::NixError)?;
Self::bring_up_loopback_interface()?;
let dev = Self::create_tun_device(rustslirp)?;
let destination = "0.0.0.0".parse().expect("failed to parse ip address");
let route = route_manager::Route::new(destination, 0).with_if_index(2);
let mut mgr = route_manager::RouteManager::new().map_err(ProcessErrorKind::StdIoError)?;
mgr.add(&route).map_err(ProcessErrorKind::StdIoError)?;
_ = sock.send_with_fd(b"", &[dev.as_raw_fd()]);
Ok(())
}
fn bring_up_loopback_interface() -> Result<()> {
let fd = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.map_err(ProcessErrorKind::NixError)?;
let if_name = "lo";
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
for (i, byte) in if_name.as_bytes().iter().enumerate() {
ifr.ifr_name[i] = *byte as libc::c_char;
}
unsafe {
get_iface_flags(fd.as_raw_fd(), &mut ifr).map_err(ProcessErrorKind::NixError)?;
ifr.ifr_ifru.ifru_flags |= (libc::IFF_UP | libc::IFF_RUNNING) as libc::c_short;
set_iface_flags(fd.as_raw_fd(), &mut ifr).map_err(ProcessErrorKind::NixError)?;
}
Ok(())
}
fn create_tun_device(rustslirp: &RustSlirp) -> Result<SyncDevice> {
let mut builder = DeviceBuilder::new();
match rustslirp.mode {
RustSlirpMode::TUN => builder = builder.layer(Layer::L3),
RustSlirpMode::TAP => builder = builder.layer(Layer::L2),
};
let dev = builder
.mtu(rustslirp.mtu)
.ipv4(rustslirp.address, rustslirp.netmask, rustslirp.destination)
.build_sync()
.map_err(ProcessErrorKind::StdIoError)?;
Ok(dev)
}
}