use crate::commands::get_config_dir;
use crate::dns::aardvark::Aardvark;
use crate::error::{NetavarkError, NetavarkResult};
use crate::firewall;
use crate::network::driver::{get_network_driver, DriverInfo, NetworkDriver};
use crate::network::netlink::{self, LinkID};
use crate::network::{self};
use crate::network::{core_utils, types};
use clap::builder::NonEmptyStringValueParser;
use clap::Parser;
use log::{debug, error, info};
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::{self};
use std::os::fd::AsFd;
use std::path::Path;
#[derive(Parser, Debug)]
pub struct Setup {
#[clap(required = true, value_parser = NonEmptyStringValueParser::new())]
network_namespace_path: String,
}
impl Setup {
pub fn new(network_namespace_path: String) -> Self {
Self {
network_namespace_path,
}
}
pub fn exec(
&self,
input_file: Option<OsString>,
config_dir: Option<OsString>,
firewall_driver: Option<String>,
aardvark_bin: OsString,
plugin_directories: Option<Vec<OsString>>,
rootless: bool,
) -> NetavarkResult<()> {
match network::validation::ns_checks(&self.network_namespace_path) {
Ok(_) => (),
Err(e) => {
return Err(NetavarkError::wrap("invalid namespace path", e));
}
}
debug!("Setting up...");
let network_options = network::types::NetworkOptions::load(input_file)?;
let firewall_driver = firewall::get_supported_firewall_driver(firewall_driver)?;
let mut response: HashMap<String, types::StatusBlock> = HashMap::new();
let dns_port = core_utils::get_netavark_dns_port()?;
let (mut hostns, mut netns) =
core_utils::open_netlink_sockets(&self.network_namespace_path)?;
netns.netlink.set_up(LinkID::ID(1))?;
let config_dir = get_config_dir(config_dir, "setup")?;
let mut drivers = Vec::with_capacity(network_options.network_info.len());
for (net_name, network) in network_options.network_info.iter() {
let per_network_opts = network_options.networks.get(net_name).ok_or_else(|| {
NetavarkError::Message(format!("network options for network {net_name} not found"))
})?;
let mut driver = get_network_driver(
DriverInfo {
firewall: firewall_driver.as_ref(),
container_id: &network_options.container_id,
container_name: &network_options.container_name,
container_hostname: &network_options.container_hostname,
container_dns_servers: &network_options.dns_servers,
netns_host: hostns.file.as_fd(),
netns_container: netns.file.as_fd(),
netns_path: &self.network_namespace_path,
network,
per_network_opts,
port_mappings: &network_options.port_mappings,
dns_port,
config_dir: Path::new(&config_dir),
rootless,
},
&plugin_directories,
)?;
driver.validate()?;
drivers.push(driver);
}
let mut aardvark_entries = Vec::new();
for (i, driver) in drivers.iter().enumerate() {
let (status, aardvark_entry) =
match driver.setup((&mut hostns.netlink, &mut netns.netlink)) {
Ok((s, a)) => (s, a),
Err(e) => {
teardown_drivers(
drivers.iter().take(i),
&mut hostns.netlink,
&mut netns.netlink,
);
return Err(e);
}
};
let _ = response.insert(driver.network_name(), status);
if let Some(a) = aardvark_entry {
aardvark_entries.push(a);
}
}
if !aardvark_entries.is_empty() {
if Path::new(&aardvark_bin).exists() {
let path = Path::new(&config_dir).join("aardvark-dns");
match fs::create_dir(path.as_path()) {
Ok(_) => {}
Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
Err(e) => {
teardown_drivers(drivers.iter(), &mut hostns.netlink, &mut netns.netlink);
return Err(NetavarkError::wrap(
format!("failed to create aardvark-dns directory {}", path.display()),
NetavarkError::Io(e),
));
}
}
let aardvark_interface = Aardvark::new(path, rootless, aardvark_bin, dns_port);
if let Err(er) = aardvark_interface.commit_netavark_entries(aardvark_entries) {
teardown_drivers(drivers.iter(), &mut hostns.netlink, &mut netns.netlink);
return Err(NetavarkError::wrap("error while applying dns entries", er));
}
} else {
info!(
"dns disabled because aardvark-dns path {:?} does not exists",
&aardvark_bin
);
}
}
debug!("{response:#?}");
let response_json = serde_json::to_string(&response)?;
println!("{response_json}");
debug!("Setup complete");
Ok(())
}
}
fn teardown_drivers<'a, I>(drivers: I, host: &mut netlink::Socket, netns: &mut netlink::Socket)
where
I: Iterator<Item = &'a Box<dyn NetworkDriver + 'a>>,
{
for driver in drivers {
if let Err(e) = driver.teardown((host, netns)) {
error!(
"failed to cleanup network {} after setup failed: {}",
driver.network_name(),
e
);
};
}
}