Add basic NAT structures
This commit is contained in:
parent
80ecb3c5d2
commit
672e866897
@ -7,6 +7,7 @@ use crate::libvirt_rest_structures::hypervisor::*;
|
|||||||
use crate::libvirt_rest_structures::net::*;
|
use crate::libvirt_rest_structures::net::*;
|
||||||
use crate::libvirt_rest_structures::nw_filter::{NetworkFilter, NetworkFilterName};
|
use crate::libvirt_rest_structures::nw_filter::{NetworkFilter, NetworkFilterName};
|
||||||
use crate::libvirt_rest_structures::vm::*;
|
use crate::libvirt_rest_structures::vm::*;
|
||||||
|
use crate::nat::nat_lib;
|
||||||
use actix::{Actor, Context, Handler, Message};
|
use actix::{Actor, Context, Handler, Message};
|
||||||
use image::ImageOutputFormat;
|
use image::ImageOutputFormat;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
@ -395,6 +396,9 @@ impl Handler<DefineNetwork> for LibVirtActor {
|
|||||||
let network = Network::define_xml(&self.m, &network_xml)?;
|
let network = Network::define_xml(&self.m, &network_xml)?;
|
||||||
let uuid = XMLUuid::parse_from_str(&network.get_uuid_string()?)?;
|
let uuid = XMLUuid::parse_from_str(&network.get_uuid_string()?)?;
|
||||||
|
|
||||||
|
// Save NAT definition
|
||||||
|
nat_lib::save_nat_def(&msg.0)?;
|
||||||
|
|
||||||
// Save a copy of the source definition
|
// Save a copy of the source definition
|
||||||
msg.0.uuid = Some(uuid);
|
msg.0.uuid = Some(uuid);
|
||||||
let json = serde_json::to_string(&msg.0)?;
|
let json = serde_json::to_string(&msg.0)?;
|
||||||
@ -464,9 +468,12 @@ impl Handler<DeleteNetwork> for LibVirtActor {
|
|||||||
fn handle(&mut self, msg: DeleteNetwork, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: DeleteNetwork, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
log::debug!("Delete network: {}\n", msg.0.as_string());
|
log::debug!("Delete network: {}\n", msg.0.as_string());
|
||||||
let network = Network::lookup_by_uuid_string(&self.m, &msg.0.as_string())?;
|
let network = Network::lookup_by_uuid_string(&self.m, &msg.0.as_string())?;
|
||||||
let network_name = network.get_name()?;
|
let network_name = NetworkName(network.get_name()?);
|
||||||
network.undefine()?;
|
network.undefine()?;
|
||||||
|
|
||||||
|
// Remove NAT definition, if any
|
||||||
|
nat_lib::remove_nat_def(&network_name)?;
|
||||||
|
|
||||||
// Remove backup definition
|
// Remove backup definition
|
||||||
let backup_definition = AppConfig::get().net_definition_path(&network_name);
|
let backup_definition = AppConfig::get().net_definition_path(&network_name);
|
||||||
if backup_definition.exists() {
|
if backup_definition.exists() {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
use crate::constants;
|
||||||
use crate::libvirt_lib_structures::XMLUuid;
|
use crate::libvirt_lib_structures::XMLUuid;
|
||||||
|
use crate::libvirt_rest_structures::net::NetworkName;
|
||||||
use crate::libvirt_rest_structures::nw_filter::NetworkFilterName;
|
use crate::libvirt_rest_structures::nw_filter::NetworkFilterName;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
@ -250,8 +252,16 @@ impl AppConfig {
|
|||||||
self.definitions_path().join(format!("vm-{name}.json"))
|
self.definitions_path().join(format!("vm-{name}.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn net_definition_path(&self, name: &str) -> PathBuf {
|
pub fn net_definition_path(&self, name: &NetworkName) -> PathBuf {
|
||||||
self.definitions_path().join(format!("net-{name}.json"))
|
self.definitions_path().join(format!("net-{}.json", name.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nat_path(&self) -> PathBuf {
|
||||||
|
self.storage_path().join(constants::STORAGE_NAT_DIR)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn net_nat_path(&self, name: &NetworkName) -> PathBuf {
|
||||||
|
self.nat_path().join(format!("nat-{}.json", name.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn net_filter_definition_path(&self, name: &NetworkFilterName) -> PathBuf {
|
pub fn net_filter_definition_path(&self, name: &NetworkFilterName) -> PathBuf {
|
||||||
|
@ -77,3 +77,6 @@ pub const BUILTIN_NETWORK_FILTER_RULES: [&str; 24] = [
|
|||||||
|
|
||||||
/// List of valid network chains
|
/// List of valid network chains
|
||||||
pub const NETWORK_CHAINS: [&str; 8] = ["root", "mac", "stp", "vlan", "arp", "rarp", "ipv4", "ipv6"];
|
pub const NETWORK_CHAINS: [&str; 8] = ["root", "mac", "stp", "vlan", "arp", "rarp", "ipv4", "ipv6"];
|
||||||
|
|
||||||
|
/// Directory where nat rules are stored, inside storage directory
|
||||||
|
pub const STORAGE_NAT_DIR: &str = "nat";
|
||||||
|
@ -7,4 +7,5 @@ pub mod libvirt_client;
|
|||||||
pub mod libvirt_lib_structures;
|
pub mod libvirt_lib_structures;
|
||||||
pub mod libvirt_rest_structures;
|
pub mod libvirt_rest_structures;
|
||||||
pub mod middlewares;
|
pub mod middlewares;
|
||||||
|
pub mod nat;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use crate::libvirt_lib_structures::network::*;
|
use crate::libvirt_lib_structures::network::*;
|
||||||
use crate::libvirt_lib_structures::XMLUuid;
|
use crate::libvirt_lib_structures::XMLUuid;
|
||||||
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
|
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
|
||||||
|
use crate::nat::nat_definition::Nat;
|
||||||
|
use crate::nat::nat_lib;
|
||||||
use crate::utils::net_utils::{extract_ipv4, extract_ipv6};
|
use crate::utils::net_utils::{extract_ipv4, extract_ipv6};
|
||||||
use ipnetwork::{Ipv4Network, Ipv6Network};
|
use ipnetwork::{Ipv4Network, Ipv6Network};
|
||||||
use lazy_regex::regex;
|
use lazy_regex::regex;
|
||||||
@ -21,57 +23,68 @@ pub struct DHCPv4HostReservation {
|
|||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct IPv4DHCPConfig {
|
pub struct IPv4DHCPConfig {
|
||||||
start: Ipv4Addr,
|
pub start: Ipv4Addr,
|
||||||
end: Ipv4Addr,
|
pub end: Ipv4Addr,
|
||||||
hosts: Vec<DHCPv4HostReservation>,
|
pub hosts: Vec<DHCPv4HostReservation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct IPV4Config {
|
pub struct IPV4Config {
|
||||||
bridge_address: Ipv4Addr,
|
pub bridge_address: Ipv4Addr,
|
||||||
prefix: u32,
|
pub prefix: u32,
|
||||||
dhcp: Option<IPv4DHCPConfig>,
|
pub dhcp: Option<IPv4DHCPConfig>,
|
||||||
|
pub nat: Option<Vec<Nat<Ipv4Addr>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct DHCPv6HostReservation {
|
pub struct DHCPv6HostReservation {
|
||||||
name: String,
|
pub name: String,
|
||||||
ip: Ipv6Addr,
|
pub ip: Ipv6Addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct IPv6DHCPConfig {
|
pub struct IPv6DHCPConfig {
|
||||||
start: Ipv6Addr,
|
pub start: Ipv6Addr,
|
||||||
end: Ipv6Addr,
|
pub end: Ipv6Addr,
|
||||||
hosts: Vec<DHCPv6HostReservation>,
|
pub hosts: Vec<DHCPv6HostReservation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct IPV6Config {
|
pub struct IPV6Config {
|
||||||
bridge_address: Ipv6Addr,
|
pub bridge_address: Ipv6Addr,
|
||||||
prefix: u32,
|
pub prefix: u32,
|
||||||
dhcp: Option<IPv6DHCPConfig>,
|
pub dhcp: Option<IPv6DHCPConfig>,
|
||||||
|
pub nat: Option<Vec<Nat<Ipv6Addr>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||||
|
pub struct NetworkName(pub String);
|
||||||
|
|
||||||
|
impl NetworkName {
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
regex!("^[a-zA-Z0-9]+$").is_match(&self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Network configuration
|
/// Network configuration
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct NetworkInfo {
|
pub struct NetworkInfo {
|
||||||
pub name: String,
|
pub name: NetworkName,
|
||||||
pub uuid: Option<XMLUuid>,
|
pub uuid: Option<XMLUuid>,
|
||||||
title: Option<String>,
|
pub title: Option<String>,
|
||||||
description: Option<String>,
|
pub description: Option<String>,
|
||||||
forward_mode: NetworkForwardMode,
|
pub forward_mode: NetworkForwardMode,
|
||||||
device: Option<String>,
|
pub device: Option<String>,
|
||||||
bridge_name: Option<String>,
|
pub bridge_name: Option<String>,
|
||||||
dns_server: Option<Ipv4Addr>,
|
pub dns_server: Option<Ipv4Addr>,
|
||||||
domain: Option<String>,
|
pub domain: Option<String>,
|
||||||
ip_v4: Option<IPV4Config>,
|
pub ip_v4: Option<IPV4Config>,
|
||||||
ip_v6: Option<IPV6Config>,
|
pub ip_v6: Option<IPV6Config>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkInfo {
|
impl NetworkInfo {
|
||||||
pub fn as_virt_network(&self) -> anyhow::Result<NetworkXML> {
|
pub fn as_virt_network(&self) -> anyhow::Result<NetworkXML> {
|
||||||
if !regex!("^[a-zA-Z0-9]+$").is_match(&self.name) {
|
if !self.name.is_valid() {
|
||||||
return Err(StructureExtraction("network name is invalid!").into());
|
return Err(StructureExtraction("network name is invalid!").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +173,7 @@ impl NetworkInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(NetworkXML {
|
Ok(NetworkXML {
|
||||||
name: self.name.to_string(),
|
name: self.name.0.to_string(),
|
||||||
uuid: self.uuid,
|
uuid: self.uuid,
|
||||||
title: self.title.clone(),
|
title: self.title.clone(),
|
||||||
description: self.description.clone(),
|
description: self.description.clone(),
|
||||||
@ -183,8 +196,12 @@ impl NetworkInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_xml(xml: NetworkXML) -> anyhow::Result<Self> {
|
pub fn from_xml(xml: NetworkXML) -> anyhow::Result<Self> {
|
||||||
|
let name = NetworkName(xml.name);
|
||||||
|
|
||||||
|
let nat = nat_lib::load_nat_def(&name)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: xml.name,
|
name,
|
||||||
uuid: xml.uuid,
|
uuid: xml.uuid,
|
||||||
title: xml.title,
|
title: xml.title,
|
||||||
description: xml.description,
|
description: xml.description,
|
||||||
@ -227,6 +244,7 @@ impl NetworkInfo {
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}),
|
}),
|
||||||
|
nat: nat.ipv4,
|
||||||
}),
|
}),
|
||||||
ip_v6: xml
|
ip_v6: xml
|
||||||
.ips
|
.ips
|
||||||
@ -252,7 +270,25 @@ impl NetworkInfo {
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}),
|
}),
|
||||||
|
nat: nat.ipv6,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if at least one NAT definition was specified on this interface
|
||||||
|
pub fn has_nat_def(&self) -> bool {
|
||||||
|
if let Some(ipv4) = &self.ip_v4 {
|
||||||
|
if ipv4.nat.is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ipv6) = &self.ip_v6 {
|
||||||
|
if ipv6.nat.is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
files_utils::create_directory_if_missing(AppConfig::get().vnc_sockets_path()).unwrap();
|
files_utils::create_directory_if_missing(AppConfig::get().vnc_sockets_path()).unwrap();
|
||||||
files_utils::set_file_permission(AppConfig::get().vnc_sockets_path(), 0o777).unwrap();
|
files_utils::set_file_permission(AppConfig::get().vnc_sockets_path(), 0o777).unwrap();
|
||||||
files_utils::create_directory_if_missing(AppConfig::get().disks_storage_path()).unwrap();
|
files_utils::create_directory_if_missing(AppConfig::get().disks_storage_path()).unwrap();
|
||||||
|
files_utils::create_directory_if_missing(AppConfig::get().nat_path()).unwrap();
|
||||||
files_utils::create_directory_if_missing(AppConfig::get().definitions_path()).unwrap();
|
files_utils::create_directory_if_missing(AppConfig::get().definitions_path()).unwrap();
|
||||||
|
|
||||||
let conn = Data::new(LibVirtClient(
|
let conn = Data::new(LibVirtClient(
|
||||||
|
2
virtweb_backend/src/nat/mod.rs
Normal file
2
virtweb_backend/src/nat/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod nat_definition;
|
||||||
|
pub mod nat_lib;
|
39
virtweb_backend/src/nat/nat_definition.rs
Normal file
39
virtweb_backend/src/nat/nat_definition.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "lowercase")]
|
||||||
|
pub enum NatSource<IPv> {
|
||||||
|
Interface { name: String },
|
||||||
|
Ip { ip: IPv },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum NatProtocol {
|
||||||
|
TCP,
|
||||||
|
UDP,
|
||||||
|
Both,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "lowercase")]
|
||||||
|
pub enum NatHostPort {
|
||||||
|
Single { port: u16 },
|
||||||
|
Range { start: u16, end: u16 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Nat<IPv> {
|
||||||
|
pub protocol: NatProtocol,
|
||||||
|
pub host_addr: NatSource<IPv>,
|
||||||
|
pub host_port: NatHostPort,
|
||||||
|
pub guest_addr: IPv,
|
||||||
|
pub guest_port: u16,
|
||||||
|
pub comment: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||||
|
pub struct NetNat {
|
||||||
|
pub interface: String,
|
||||||
|
pub ipv4: Option<Vec<Nat<Ipv4Addr>>>,
|
||||||
|
pub ipv6: Option<Vec<Nat<Ipv6Addr>>>,
|
||||||
|
}
|
61
virtweb_backend/src/nat/nat_lib.rs
Normal file
61
virtweb_backend/src/nat/nat_lib.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::app_config::AppConfig;
|
||||||
|
use crate::libvirt_rest_structures::net::{NetworkInfo, NetworkName};
|
||||||
|
use crate::nat::nat_definition::NetNat;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
enum NatLibError {
|
||||||
|
#[error("Could not save nat definition, because network bridge name was not specified!")]
|
||||||
|
MissingNetworkBridgeName,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save nat definition
|
||||||
|
pub fn save_nat_def(net: &NetworkInfo) -> anyhow::Result<()> {
|
||||||
|
let nat = match net.has_nat_def() {
|
||||||
|
true => NetNat {
|
||||||
|
interface: net
|
||||||
|
.bridge_name
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(NatLibError::MissingNetworkBridgeName)?
|
||||||
|
.to_string(),
|
||||||
|
ipv4: net
|
||||||
|
.ip_v4
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| i.nat.clone())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
ipv6: net
|
||||||
|
.ip_v6
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| i.nat.clone())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
},
|
||||||
|
false => NetNat::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&nat)?;
|
||||||
|
|
||||||
|
std::fs::write(AppConfig::get().net_nat_path(&net.name), json)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove nat definition, if existing
|
||||||
|
pub fn remove_nat_def(name: &NetworkName) -> anyhow::Result<()> {
|
||||||
|
let nat_file = AppConfig::get().net_nat_path(name);
|
||||||
|
if nat_file.exists() {
|
||||||
|
std::fs::remove_file(nat_file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load nat definition, if available
|
||||||
|
pub fn load_nat_def(name: &NetworkName) -> anyhow::Result<NetNat> {
|
||||||
|
let nat_file = AppConfig::get().net_nat_path(name);
|
||||||
|
if !nat_file.exists() {
|
||||||
|
return Ok(NetNat::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = std::fs::read_to_string(nat_file)?;
|
||||||
|
|
||||||
|
Ok(serde_json::from_str(&file)?)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user