start to implementig clone to create namespaces
This commit is contained in:
58
src/main.rs
58
src/main.rs
@@ -1,47 +1,15 @@
|
||||
use std::fs::create_dir;
|
||||
use std::io::{Result, ErrorKind};
|
||||
use std::fs::File;
|
||||
use nix::mount::{
|
||||
mount,
|
||||
MsFlags
|
||||
};
|
||||
mod namespace;
|
||||
use std::io::Result;
|
||||
use rtnetlink::NetworkNamespace;
|
||||
use futures::executor::block_on;
|
||||
|
||||
const PATH_MAX:u32 = 4096;
|
||||
const NET_NS_DIR:&str = "/var/run/netns";
|
||||
const PROC_NS_DIR:&str = "/proc/self/ns/net";
|
||||
|
||||
fn main() -> Result<()> {
|
||||
create_ns_dir()?;
|
||||
let nsdir = format!("{}/{}",NET_NS_DIR,"fake-net");
|
||||
File::create(nsdir.clone())?;
|
||||
bind_ns_file(nsdir)?;
|
||||
println!("end");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_ns_dir() -> Result<()> {
|
||||
match create_dir(NET_NS_DIR) {
|
||||
Err(e) if e.kind() != ErrorKind::AlreadyExists => {
|
||||
println!("entra 2");
|
||||
Err(e)
|
||||
},
|
||||
_ => {
|
||||
println!("entra 1");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_ns_file(ns_file: String) -> Result<()> {
|
||||
match create_mount(ns_file) {
|
||||
Ok(_mount) => {
|
||||
println!("mount succes");
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_mount(ns_file: String) -> Result<()> {
|
||||
Ok(mount::<str, str, str, str>(Some(PROC_NS_DIR), ns_file.as_str(), None, MsFlags::MS_BIND, None)?)
|
||||
fn main() {
|
||||
env_logger::Builder::from_default_env()
|
||||
.format_timestamp_secs()
|
||||
.filter(None, log::LevelFilter::Debug)
|
||||
.init();
|
||||
//namespace::create_ns::create_ns();
|
||||
let ns_name = "test-newns".to_string();
|
||||
block_on(NetworkNamespace::add(ns_name.clone()));
|
||||
namespace::bind_interface::run_in_namespace(&ns_name);
|
||||
}
|
||||
|
||||
172
src/namespace/bind_interface.rs
Normal file
172
src/namespace/bind_interface.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
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(())
|
||||
}
|
||||
3
src/namespace/consts.rs
Normal file
3
src/namespace/consts.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub const NET_NS_DIR:&str = "/var/run/netns";
|
||||
pub const PROC_NS_DIR:&str = "/proc/self/ns/net";
|
||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
||||
43
src/namespace/create_ns.rs
Normal file
43
src/namespace/create_ns.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::fs::create_dir;
|
||||
use std::io::{Result, ErrorKind};
|
||||
use std::fs::File;
|
||||
use crate::namespace::consts::{
|
||||
NET_NS_DIR,
|
||||
PROC_NS_DIR
|
||||
};
|
||||
use nix::mount::{
|
||||
mount,
|
||||
MsFlags
|
||||
};
|
||||
|
||||
pub fn create_ns() -> Result<()> {
|
||||
create_ns_dir()?;
|
||||
let nsdir = format!("{}/{}",NET_NS_DIR,"fake-net");
|
||||
File::create(nsdir.clone())?;
|
||||
bind_ns_file(nsdir)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_ns_dir() -> Result<()> {
|
||||
match create_dir(NET_NS_DIR) {
|
||||
Err(e) if e.kind() != ErrorKind::AlreadyExists => {
|
||||
Err(e)
|
||||
},
|
||||
_ => {
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_ns_file(ns_file: String) -> Result<()> {
|
||||
match create_mount(ns_file) {
|
||||
Ok(_mount) => {
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_mount(ns_file: String) -> Result<()> {
|
||||
Ok(mount::<str, str, str, str>(Some(PROC_NS_DIR), ns_file.as_str(), None, MsFlags::MS_BIND, None)?)
|
||||
}
|
||||
3
src/namespace/mod.rs
Normal file
3
src/namespace/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod create_ns;
|
||||
pub mod bind_interface;
|
||||
mod consts;
|
||||
Reference in New Issue
Block a user