Improve network checks
This commit is contained in:
		@@ -43,14 +43,6 @@ pub struct NetworkDomainXML {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn invalid_prefix() -> u32 {
 | 
			
		||||
    u32::MAX
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn invalid_ip() -> IpAddr {
 | 
			
		||||
    IpAddr::V4(Ipv4Addr::BROADCAST)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Network ip information
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "ip")]
 | 
			
		||||
@@ -60,12 +52,13 @@ pub struct NetworkIPXML {
 | 
			
		||||
    #[serde(rename = "@address")]
 | 
			
		||||
    pub address: IpAddr,
 | 
			
		||||
    /// Network Prefix
 | 
			
		||||
    #[serde(rename = "@prefix", default = "invalid_prefix")]
 | 
			
		||||
    pub prefix: u32,
 | 
			
		||||
    #[serde(rename = "@prefix")]
 | 
			
		||||
    pub prefix: Option<u8>,
 | 
			
		||||
    /// Network Netmask. This field is never serialized, but because we can't know if LibVirt will
 | 
			
		||||
    /// provide us netmask or prefix, we need to handle both of these fields
 | 
			
		||||
    #[serde(rename = "@netmask", default = "invalid_ip", skip_serializing)]
 | 
			
		||||
    pub netmask: IpAddr,
 | 
			
		||||
    #[serde(rename = "@netmask", skip_serializing)]
 | 
			
		||||
    pub netmask: Option<IpAddr>,
 | 
			
		||||
 | 
			
		||||
    pub dhcp: Option<NetworkDHCPXML>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ use crate::libvirt_lib_structures::XMLUuid;
 | 
			
		||||
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
 | 
			
		||||
use crate::nat::nat_definition::Nat;
 | 
			
		||||
use crate::nat::nat_lib;
 | 
			
		||||
use crate::utils::net_utils;
 | 
			
		||||
use crate::utils::net_utils::{extract_ipv4, extract_ipv6};
 | 
			
		||||
use ipnetwork::{Ipv4Network, Ipv6Network};
 | 
			
		||||
use lazy_regex::regex;
 | 
			
		||||
@@ -31,7 +32,7 @@ pub struct IPv4DHCPConfig {
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct IPV4Config {
 | 
			
		||||
    pub bridge_address: Ipv4Addr,
 | 
			
		||||
    pub prefix: u32,
 | 
			
		||||
    pub prefix: u8,
 | 
			
		||||
    pub dhcp: Option<IPv4DHCPConfig>,
 | 
			
		||||
    pub nat: Option<Vec<Nat<Ipv4Addr>>>,
 | 
			
		||||
}
 | 
			
		||||
@@ -52,7 +53,7 @@ pub struct IPv6DHCPConfig {
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct IPV6Config {
 | 
			
		||||
    pub bridge_address: Ipv6Addr,
 | 
			
		||||
    pub prefix: u32,
 | 
			
		||||
    pub prefix: u8,
 | 
			
		||||
    pub dhcp: Option<IPv6DHCPConfig>,
 | 
			
		||||
    pub nat: Option<Vec<Nat<Ipv6Addr>>>,
 | 
			
		||||
}
 | 
			
		||||
@@ -115,18 +116,26 @@ impl NetworkInfo {
 | 
			
		||||
        let mut ips = Vec::with_capacity(2);
 | 
			
		||||
 | 
			
		||||
        if let Some(ipv4) = &self.ip_v4 {
 | 
			
		||||
            if ipv4.prefix > 32 {
 | 
			
		||||
            if !net_utils::is_ipv4_mask_valid(ipv4.prefix) {
 | 
			
		||||
                return Err(StructureExtraction("IPv4 prefix is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if let Some(nat) = &ipv4.nat {
 | 
			
		||||
                for n in nat {
 | 
			
		||||
                    n.check()?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ips.push(NetworkIPXML {
 | 
			
		||||
                family: "ipv4".to_string(),
 | 
			
		||||
                address: IpAddr::V4(ipv4.bridge_address),
 | 
			
		||||
                prefix: ipv4.prefix,
 | 
			
		||||
                netmask: Ipv4Network::new(ipv4.bridge_address, ipv4.prefix as u8)
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .mask()
 | 
			
		||||
                    .into(),
 | 
			
		||||
                prefix: Some(ipv4.prefix),
 | 
			
		||||
                netmask: Some(
 | 
			
		||||
                    Ipv4Network::new(ipv4.bridge_address, ipv4.prefix)
 | 
			
		||||
                        .unwrap()
 | 
			
		||||
                        .mask()
 | 
			
		||||
                        .into(),
 | 
			
		||||
                ),
 | 
			
		||||
                dhcp: ipv4.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
 | 
			
		||||
                    range: NetworkDHCPRangeXML {
 | 
			
		||||
                        start: IpAddr::V4(dhcp.start),
 | 
			
		||||
@@ -146,14 +155,26 @@ impl NetworkInfo {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(ipv6) = &self.ip_v6 {
 | 
			
		||||
            if !net_utils::is_ipv6_mask_valid(ipv6.prefix) {
 | 
			
		||||
                return Err(StructureExtraction("IPv6 prefix is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if let Some(nat) = &ipv6.nat {
 | 
			
		||||
                for n in nat {
 | 
			
		||||
                    n.check()?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ips.push(NetworkIPXML {
 | 
			
		||||
                family: "ipv6".to_string(),
 | 
			
		||||
                address: IpAddr::V6(ipv6.bridge_address),
 | 
			
		||||
                prefix: ipv6.prefix,
 | 
			
		||||
                netmask: Ipv6Network::new(ipv6.bridge_address, ipv6.prefix as u8)
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .mask()
 | 
			
		||||
                    .into(),
 | 
			
		||||
                prefix: Some(ipv6.prefix),
 | 
			
		||||
                netmask: Some(
 | 
			
		||||
                    Ipv6Network::new(ipv6.bridge_address, ipv6.prefix)
 | 
			
		||||
                        .unwrap()
 | 
			
		||||
                        .mask()
 | 
			
		||||
                        .into(),
 | 
			
		||||
                ),
 | 
			
		||||
                dhcp: ipv6.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
 | 
			
		||||
                    range: NetworkDHCPRangeXML {
 | 
			
		||||
                        start: IpAddr::V6(dhcp.start),
 | 
			
		||||
@@ -226,10 +247,9 @@ impl NetworkInfo {
 | 
			
		||||
                .map(|i| IPV4Config {
 | 
			
		||||
                    bridge_address: extract_ipv4(i.address),
 | 
			
		||||
                    prefix: match i.prefix {
 | 
			
		||||
                        u32::MAX => ipnetwork::ipv4_mask_to_prefix(extract_ipv4(i.netmask))
 | 
			
		||||
                            .expect("Failed to convert IPv4 netmask to network")
 | 
			
		||||
                            as u32,
 | 
			
		||||
                        p => p,
 | 
			
		||||
                        None => ipnetwork::ipv4_mask_to_prefix(extract_ipv4(i.netmask.unwrap()))
 | 
			
		||||
                            .expect("Failed to convert IPv4 netmask to network"),
 | 
			
		||||
                        Some(p) => p,
 | 
			
		||||
                    },
 | 
			
		||||
                    dhcp: i.dhcp.as_ref().map(|d| IPv4DHCPConfig {
 | 
			
		||||
                        start: extract_ipv4(d.range.start),
 | 
			
		||||
@@ -253,10 +273,9 @@ impl NetworkInfo {
 | 
			
		||||
                .map(|i| IPV6Config {
 | 
			
		||||
                    bridge_address: extract_ipv6(i.address),
 | 
			
		||||
                    prefix: match i.prefix {
 | 
			
		||||
                        u32::MAX => ipnetwork::ipv6_mask_to_prefix(extract_ipv6(i.netmask))
 | 
			
		||||
                            .expect("Failed to convert IPv6 netmask to network")
 | 
			
		||||
                            as u32,
 | 
			
		||||
                        p => p,
 | 
			
		||||
                        None => ipnetwork::ipv6_mask_to_prefix(extract_ipv6(i.netmask.unwrap()))
 | 
			
		||||
                            .expect("Failed to convert IPv6 netmask to network"),
 | 
			
		||||
                        Some(p) => p,
 | 
			
		||||
                    },
 | 
			
		||||
                    dhcp: i.dhcp.as_ref().map(|d| IPv6DHCPConfig {
 | 
			
		||||
                        start: extract_ipv6(d.range.start),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
use crate::utils::net_utils;
 | 
			
		||||
use std::net::{Ipv4Addr, Ipv6Addr};
 | 
			
		||||
 | 
			
		||||
#[derive(thiserror::Error, Debug)]
 | 
			
		||||
enum NatDefError {
 | 
			
		||||
    #[error("Invalid nat definition: {0}")]
 | 
			
		||||
    InvalidNatDef(&'static str),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 | 
			
		||||
#[serde(tag = "type", rename_all = "lowercase")]
 | 
			
		||||
pub enum NatSource<IPv> {
 | 
			
		||||
@@ -31,6 +38,36 @@ pub struct Nat<IPv> {
 | 
			
		||||
    pub comment: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<IPv> Nat<IPv> {
 | 
			
		||||
    pub fn check(&self) -> anyhow::Result<()> {
 | 
			
		||||
        if let NatSource::Interface { name } = &self.host_addr {
 | 
			
		||||
            if !net_utils::is_net_interface_name_valid(name) {
 | 
			
		||||
                return Err(NatDefError::InvalidNatDef("Invalid nat interface name!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let NatHostPort::Range { start, end } = &self.host_port {
 | 
			
		||||
            if *start == 0 {
 | 
			
		||||
                return Err(NatDefError::InvalidNatDef("Invalid start range!").into());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if start > end {
 | 
			
		||||
                return Err(NatDefError::InvalidNatDef("Invalid port range!").into());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if u16::MAX - (end - start) < self.guest_port {
 | 
			
		||||
                return Err(NatDefError::InvalidNatDef("Guest port is too high!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if self.guest_port == 0 {
 | 
			
		||||
            return Err(NatDefError::InvalidNatDef("Invalid guest port!").into());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
 | 
			
		||||
pub struct NetNat {
 | 
			
		||||
    pub interface: String,
 | 
			
		||||
 
 | 
			
		||||
@@ -47,10 +47,15 @@ pub fn is_mac_address_valid<D: AsRef<str>>(mac: D) -> bool {
 | 
			
		||||
    lazy_regex::regex!("^([a-fA-F0-9]{2}[:-]){5}[a-fA-F0-9]{2}$").is_match(mac.as_ref())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::utils::net_utils::{
 | 
			
		||||
        is_ipv4_address_valid, is_ipv6_address_valid, is_mac_address_valid, is_mask_valid,
 | 
			
		||||
        is_net_interface_name_valid,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -98,4 +103,13 @@ mod tests {
 | 
			
		||||
        assert!(is_mask_valid(6, 128));
 | 
			
		||||
        assert!(!is_mask_valid(6, 129));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_is_net_interface_name_valid() {
 | 
			
		||||
        assert!(is_net_interface_name_valid("eth0"));
 | 
			
		||||
        assert!(is_net_interface_name_valid("enp0s25"));
 | 
			
		||||
 | 
			
		||||
        assert!(!is_net_interface_name_valid("enp0s25 "));
 | 
			
		||||
        assert!(!is_net_interface_name_valid("@enp0s25 "));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user