netjaileRS/src/namespace/bind_interface.rs

173 lines
5.5 KiB
Rust

use nix::sched::{CloneFlags, clone, unshare, setns};
use nix::sys::signal::Signal;
use crate::namespace::consts::{
NET_NS_DIR,
STACK_SIZE
};
use futures::TryStreamExt;
use nix::fcntl::{open, OFlag};
use nix::mount::{mount, MsFlags};
use nix::unistd::{fork, ForkResult, Pid};
use nix::sys::wait::{waitpid, WaitStatus};
use nix::sys::stat::Mode;
use nix::sys::statvfs::{statvfs, FsFlags};
use rtnetlink::{new_connection, Error, Handle, NetworkNamespace};
use std::env;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::os::unix::io::RawFd;
use std::os::fd::FromRawFd;
pub fn run_in_namespace(ns_name: &String) -> Result<(), ()> {
// Configure networking in the child namespace:
// Fork a process that is set to the newly created namespace
// Here set the veth ip addr, routing tables etc.
// Unfortunately the NetworkNamespace interface of rtnetlink does
// not offer these functionalities
let mut tmp_stack: [u8; STACK_SIZE] = [0; STACK_SIZE];
let mut flags = CloneFlags::empty();
flags.insert(CloneFlags::CLONE_VM);
flags.insert(CloneFlags::CLONE_VFORK);
unsafe {
match clone(
Box::new(|| run_child(&ns_name.clone())),
&mut tmp_stack,
flags,
Some(Signal::SIGCHLD as i32)) {
Ok(pid) => Ok(()),
Err(e) => {
return Err(());
}
}
}
}
fn run_child(ns_name: &String) -> isize {
let res = split_namespace(ns_name);
match res {
Err(_) => {
log::error!("Child process crashed");
return -1;
}
Ok(()) => {
log::debug!("Child exited normally");
return 0;
}
}
}
fn split_namespace(ns_name: &String) -> Result<(), ()> {
// First create the network namespace
// NetworkNamespace::add(ns_name.to_string()).await.map_err(|e| {
// log::error!("Can not create namespace {}", e);
// }).unwrap();
// Open NS path
let ns_path = format!("{}/{}", NET_NS_DIR, ns_name);
log::debug!("ns_path:{}", ns_path);
let mut open_flags = OFlag::empty();
open_flags.insert(OFlag::O_RDONLY);
open_flags.insert(OFlag::O_CLOEXEC);
let fd = match open(Path::new(&ns_path), open_flags, Mode::empty()) {
Ok(raw_fd) => unsafe {
File::from_raw_fd(raw_fd)
}
Err(e) => {
log::error!("Can not open network namespace: {}", e);
return Err(());
}
};
// Switch to network namespace with CLONE_NEWNET
if let Err(e) = setns(fd, CloneFlags::CLONE_NEWNET) {
log::error!("Can not set namespace to target {}: {}", ns_name, e);
return Err(());
}
// unshare with CLONE_NEWNS
if let Err(e) = unshare(CloneFlags::CLONE_NEWNS) {
log::error!("Can not unshare: {}", e);
return Err(());
}
// mount blind the fs
// let's avoid that any mount propagates to the parent process
// mount_directory(None, &PathBuf::from("/"), vec![MsFlags::MS_REC, MsFlags::MS_PRIVATE])?;
let mut mount_flags = MsFlags::empty();
mount_flags.insert(MsFlags::MS_REC);
mount_flags.insert(MsFlags::MS_PRIVATE);
if let Err(e) = mount::<PathBuf, PathBuf, str, PathBuf>(None, &PathBuf::from("/"), None, mount_flags, None) {
log::error!("Can not remount root directory");
()
}
// Now unmount /sys
let sys_path = PathBuf::from("/sys");
mount_flags = MsFlags::empty();
// Needed to respect the trait for NixPath
let ns_name_path = PathBuf::from(ns_name);
// TODO do not exit for EINVAL error
// unmount_path(&sys_path)?;
// consider the case that a sysfs is not present
let stat_sys = statvfs(&sys_path)
.map_err(|e| {
log::error!("Can not stat sys: {}", e);
}).unwrap();
if stat_sys.flags().contains(FsFlags::ST_RDONLY) {
mount_flags.insert(MsFlags::MS_RDONLY);
}
// and remount a version of /sys that describes the network namespace
if let Err(e) = mount::<PathBuf, PathBuf, str, PathBuf>(Some(&ns_name_path), &sys_path, Some("sysfs"), mount_flags, None) {
log::error!("Can not remount /sys to namespace: {}", e);
()
}
set_lo_up().unwrap();
Ok(())
}
pub fn set_lo_up() -> Result<(), Error> {
tokio::runtime::Runtime::new().unwrap().handle().block_on( async {
let (connection, handle, _) = new_connection().unwrap();
tokio::spawn(connection);
log::debug!("ARE WE STOPPING YET???");
let mut links = handle.link().get().match_name("lo".to_string()).execute();
if let Some(link) = links.try_next().await.unwrap() {
let index = link.header.index;
log::debug!("index:{}", index);
handle
.link()
.set(index)
.up()
.execute()
.await.unwrap()
} else {
println!("no link link lo found");
}
});
Ok(())
}
pub fn get_inferfaces() -> Result<(), Error> {
tokio::runtime::Runtime::new().unwrap().handle().block_on( async {
let (connection, handle, _) = new_connection().unwrap();
tokio::spawn(connection);
log::debug!("ARE WE STOPPING YET???");
let mut links = handle.link().get().match_name("lo".to_string()).execute();
if let Some(link) = links.try_next().await.unwrap() {
let index = link.header.index;
log::debug!("index:{}", index);
} else {
println!("no link link lo found");
}
});
Ok(())
}