Start to build NAT configuration mode
This commit is contained in:
		@@ -261,7 +261,7 @@ impl AppConfig {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn net_nat_path(&self, name: &NetworkName) -> PathBuf {
 | 
			
		||||
        self.nat_path().join(format!("nat-{}.json", name.0))
 | 
			
		||||
        self.nat_path().join(name.nat_file_name())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn net_filter_definition_path(&self, name: &NetworkFilterName) -> PathBuf {
 | 
			
		||||
 
 | 
			
		||||
@@ -83,3 +83,6 @@ pub const NETWORK_CHAINS: [&str; 8] = ["root", "mac", "stp", "vlan", "arp", "rar
 | 
			
		||||
 | 
			
		||||
/// Directory where nat rules are stored, inside storage directory
 | 
			
		||||
pub const STORAGE_NAT_DIR: &str = "nat";
 | 
			
		||||
 | 
			
		||||
/// Environment variable that is set to run VirtWeb in NAT configuration mode
 | 
			
		||||
pub const NAT_MODE_ENV_VAR_NAME: &str = "NAT_MODE";
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,9 @@ use crate::constants::{DISK_NAME_MAX_LEN, DISK_NAME_MIN_LEN, DISK_SIZE_MAX, DISK
 | 
			
		||||
use crate::controllers::{HttpResult, LibVirtReq};
 | 
			
		||||
use crate::extractors::local_auth_extractor::LocalAuthEnabled;
 | 
			
		||||
use crate::libvirt_rest_structures::hypervisor::HypervisorInfo;
 | 
			
		||||
use crate::utils::net_utils;
 | 
			
		||||
use actix_web::{HttpResponse, Responder};
 | 
			
		||||
use sysinfo::{NetworksExt, System, SystemExt};
 | 
			
		||||
use sysinfo::{System, SystemExt};
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
struct StaticConfig {
 | 
			
		||||
@@ -137,14 +138,5 @@ pub async fn number_vcpus() -> HttpResult {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn networks_list() -> HttpResult {
 | 
			
		||||
    let mut system = System::new();
 | 
			
		||||
    system.refresh_networks_list();
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(
 | 
			
		||||
        system
 | 
			
		||||
            .networks()
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|n| n.0.to_string())
 | 
			
		||||
            .collect::<Vec<_>>(),
 | 
			
		||||
    ))
 | 
			
		||||
    Ok(HttpResponse::Ok().json(net_utils::net_list()))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -61,6 +61,13 @@ pub struct IPV6Config {
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub struct NetworkName(pub String);
 | 
			
		||||
 | 
			
		||||
impl NetworkName {
 | 
			
		||||
    /// Get the name of the file that will store the NAT configuration of this network
 | 
			
		||||
    pub fn nat_file_name(&self) -> String {
 | 
			
		||||
        format!("nat-{}.json", self.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NetworkName {
 | 
			
		||||
    pub fn is_valid(&self) -> bool {
 | 
			
		||||
        regex!("^[a-zA-Z0-9]+$").is_match(&self.0)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,12 +27,19 @@ use virtweb_backend::controllers::{
 | 
			
		||||
};
 | 
			
		||||
use virtweb_backend::libvirt_client::LibVirtClient;
 | 
			
		||||
use virtweb_backend::middlewares::auth_middleware::AuthChecker;
 | 
			
		||||
use virtweb_backend::nat::nat_conf_mode;
 | 
			
		||||
use virtweb_backend::utils::files_utils;
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
 | 
			
		||||
 | 
			
		||||
    // Run in NAT configuration mode, if requested
 | 
			
		||||
    if std::env::var(constants::NAT_MODE_ENV_VAR_NAME).is_ok() {
 | 
			
		||||
        nat_conf_mode::sub_main().await.unwrap();
 | 
			
		||||
        return Ok(());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Load additional config from file, if requested
 | 
			
		||||
    AppConfig::parse_env_file().unwrap();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,3 @@
 | 
			
		||||
pub mod nat_conf_mode;
 | 
			
		||||
pub mod nat_definition;
 | 
			
		||||
pub mod nat_lib;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										79
									
								
								virtweb_backend/src/nat/nat_conf_mode.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								virtweb_backend/src/nat/nat_conf_mode.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
use crate::constants;
 | 
			
		||||
use crate::libvirt_rest_structures::net::NetworkName;
 | 
			
		||||
use crate::nat::nat_definition::NetNat;
 | 
			
		||||
use crate::utils::net_utils;
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::net::IpAddr;
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
 | 
			
		||||
/// VirtWeb NAT configuration mode. This executable should never be executed manually
 | 
			
		||||
#[derive(Parser, Debug, Clone)]
 | 
			
		||||
#[clap(author, version, about, long_about = None)]
 | 
			
		||||
struct NatArgs {
 | 
			
		||||
    /// Storage directory
 | 
			
		||||
    #[clap(short, long)]
 | 
			
		||||
    storage: String,
 | 
			
		||||
 | 
			
		||||
    /// Network name
 | 
			
		||||
    #[clap(short, long)]
 | 
			
		||||
    network_name: String,
 | 
			
		||||
 | 
			
		||||
    /// Operation
 | 
			
		||||
    #[clap(short, long)]
 | 
			
		||||
    operation: String,
 | 
			
		||||
 | 
			
		||||
    /// Sub operation
 | 
			
		||||
    #[clap(long)]
 | 
			
		||||
    sub_operation: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NatArgs {
 | 
			
		||||
    pub fn network_file(&self) -> PathBuf {
 | 
			
		||||
        let network_name = NetworkName(self.network_name.to_string());
 | 
			
		||||
        Path::new(&self.storage)
 | 
			
		||||
            .join(constants::STORAGE_NAT_DIR)
 | 
			
		||||
            .join(network_name.nat_file_name())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// NAT sub main function
 | 
			
		||||
pub async fn sub_main() -> anyhow::Result<()> {
 | 
			
		||||
    let args = NatArgs::parse();
 | 
			
		||||
 | 
			
		||||
    if !args.network_file().exists() {
 | 
			
		||||
        log::warn!("Cannot do anything for the network, because the NAT configuration file does not exixsts!");
 | 
			
		||||
        return Ok(());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let conf_json = std::fs::read_to_string(args.network_file())?;
 | 
			
		||||
    let conf: NetNat = serde_json::from_str(&conf_json)?;
 | 
			
		||||
 | 
			
		||||
    let ips = net_utils::net_list_and_ips()?;
 | 
			
		||||
 | 
			
		||||
    match (args.operation.as_str(), args.sub_operation.as_str()) {
 | 
			
		||||
        ("started", "begin") => network_started_begin(&conf, &ips).await?,
 | 
			
		||||
        ("stopped", "end") => network_stopped_end(&conf, &ips).await?,
 | 
			
		||||
        _ => log::debug!(
 | 
			
		||||
            "Operation {} - {} not supported",
 | 
			
		||||
            args.operation,
 | 
			
		||||
            args.sub_operation
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn network_started_begin(
 | 
			
		||||
    conf: &NetNat,
 | 
			
		||||
    ips: &HashMap<String, Vec<IpAddr>>,
 | 
			
		||||
) -> anyhow::Result<()> {
 | 
			
		||||
    todo!()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn network_stopped_end(
 | 
			
		||||
    conf: &NetNat,
 | 
			
		||||
    ips: &HashMap<String, Vec<IpAddr>>,
 | 
			
		||||
) -> anyhow::Result<()> {
 | 
			
		||||
    todo!()
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
use nix::sys::socket::{AddressFamily, SockaddrLike};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
use sysinfo::{NetworksExt, System, SystemExt};
 | 
			
		||||
 | 
			
		||||
pub fn extract_ipv4(ip: IpAddr) -> Ipv4Addr {
 | 
			
		||||
    match ip {
 | 
			
		||||
@@ -51,6 +54,88 @@ pub fn is_net_interface_name_valid<D: AsRef<str>>(int: D) -> bool {
 | 
			
		||||
    lazy_regex::regex!("^[a-zA-Z0-9]+$").is_match(int.as_ref())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the list of available network interfaces
 | 
			
		||||
pub fn net_list() -> Vec<String> {
 | 
			
		||||
    let mut system = System::new();
 | 
			
		||||
    system.refresh_networks_list();
 | 
			
		||||
 | 
			
		||||
    system
 | 
			
		||||
        .networks()
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|n| n.0.to_string())
 | 
			
		||||
        .collect::<Vec<_>>()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the list of available network interfaces associated with their IP address
 | 
			
		||||
pub fn net_list_and_ips() -> anyhow::Result<HashMap<String, Vec<IpAddr>>> {
 | 
			
		||||
    let addrs = nix::ifaddrs::getifaddrs().unwrap();
 | 
			
		||||
 | 
			
		||||
    let mut res = HashMap::new();
 | 
			
		||||
 | 
			
		||||
    for ifaddr in addrs {
 | 
			
		||||
        let address = match ifaddr.address {
 | 
			
		||||
            Some(address) => address,
 | 
			
		||||
            None => {
 | 
			
		||||
                log::debug!(
 | 
			
		||||
                    "Interface {} has an unsupported address family",
 | 
			
		||||
                    ifaddr.interface_name
 | 
			
		||||
                );
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let addr_str = match address.family() {
 | 
			
		||||
            Some(AddressFamily::Inet) => {
 | 
			
		||||
                let address = address.to_string();
 | 
			
		||||
                address
 | 
			
		||||
                    .split_once(':')
 | 
			
		||||
                    .map(|a| a.0)
 | 
			
		||||
                    .unwrap_or(&address)
 | 
			
		||||
                    .to_string()
 | 
			
		||||
            }
 | 
			
		||||
            Some(AddressFamily::Inet6) => {
 | 
			
		||||
                let address = address.to_string();
 | 
			
		||||
                let address = address
 | 
			
		||||
                    .split_once(']')
 | 
			
		||||
                    .map(|a| a.0)
 | 
			
		||||
                    .unwrap_or(&address)
 | 
			
		||||
                    .to_string();
 | 
			
		||||
 | 
			
		||||
                let address = address
 | 
			
		||||
                    .split_once('%')
 | 
			
		||||
                    .map(|a| a.0)
 | 
			
		||||
                    .unwrap_or(&address)
 | 
			
		||||
                    .to_string();
 | 
			
		||||
 | 
			
		||||
                address.strip_prefix('[').unwrap_or(&address).to_string()
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                log::debug!(
 | 
			
		||||
                    "Interface {} has an unsupported address family {:?}",
 | 
			
		||||
                    ifaddr.interface_name,
 | 
			
		||||
                    address.family()
 | 
			
		||||
                );
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        log::debug!(
 | 
			
		||||
            "Process ip {addr_str} for interface {}",
 | 
			
		||||
            ifaddr.interface_name
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let ip = IpAddr::from_str(&addr_str)?;
 | 
			
		||||
 | 
			
		||||
        if !res.contains_key(&ifaddr.interface_name) {
 | 
			
		||||
            res.insert(ifaddr.interface_name.to_string(), Vec::with_capacity(1));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        res.get_mut(&ifaddr.interface_name).unwrap().push(ip);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::utils::net_utils::{
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user