Improve network checks
This commit is contained in:
parent
672e866897
commit
e86b29c03a
@ -43,14 +43,6 @@ pub struct NetworkDomainXML {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalid_prefix() -> u32 {
|
|
||||||
u32::MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
fn invalid_ip() -> IpAddr {
|
|
||||||
IpAddr::V4(Ipv4Addr::BROADCAST)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Network ip information
|
/// Network ip information
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||||
#[serde(rename = "ip")]
|
#[serde(rename = "ip")]
|
||||||
@ -60,12 +52,13 @@ pub struct NetworkIPXML {
|
|||||||
#[serde(rename = "@address")]
|
#[serde(rename = "@address")]
|
||||||
pub address: IpAddr,
|
pub address: IpAddr,
|
||||||
/// Network Prefix
|
/// Network Prefix
|
||||||
#[serde(rename = "@prefix", default = "invalid_prefix")]
|
#[serde(rename = "@prefix")]
|
||||||
pub prefix: u32,
|
pub prefix: Option<u8>,
|
||||||
/// Network Netmask. This field is never serialized, but because we can't know if LibVirt will
|
/// 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
|
/// provide us netmask or prefix, we need to handle both of these fields
|
||||||
#[serde(rename = "@netmask", default = "invalid_ip", skip_serializing)]
|
#[serde(rename = "@netmask", skip_serializing)]
|
||||||
pub netmask: IpAddr,
|
pub netmask: Option<IpAddr>,
|
||||||
|
|
||||||
pub dhcp: Option<NetworkDHCPXML>,
|
pub dhcp: Option<NetworkDHCPXML>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ 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_definition::Nat;
|
||||||
use crate::nat::nat_lib;
|
use crate::nat::nat_lib;
|
||||||
|
use crate::utils::net_utils;
|
||||||
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;
|
||||||
@ -31,7 +32,7 @@ pub struct IPv4DHCPConfig {
|
|||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct IPV4Config {
|
pub struct IPV4Config {
|
||||||
pub bridge_address: Ipv4Addr,
|
pub bridge_address: Ipv4Addr,
|
||||||
pub prefix: u32,
|
pub prefix: u8,
|
||||||
pub dhcp: Option<IPv4DHCPConfig>,
|
pub dhcp: Option<IPv4DHCPConfig>,
|
||||||
pub nat: Option<Vec<Nat<Ipv4Addr>>>,
|
pub nat: Option<Vec<Nat<Ipv4Addr>>>,
|
||||||
}
|
}
|
||||||
@ -52,7 +53,7 @@ pub struct IPv6DHCPConfig {
|
|||||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
|
||||||
pub struct IPV6Config {
|
pub struct IPV6Config {
|
||||||
pub bridge_address: Ipv6Addr,
|
pub bridge_address: Ipv6Addr,
|
||||||
pub prefix: u32,
|
pub prefix: u8,
|
||||||
pub dhcp: Option<IPv6DHCPConfig>,
|
pub dhcp: Option<IPv6DHCPConfig>,
|
||||||
pub nat: Option<Vec<Nat<Ipv6Addr>>>,
|
pub nat: Option<Vec<Nat<Ipv6Addr>>>,
|
||||||
}
|
}
|
||||||
@ -115,18 +116,26 @@ impl NetworkInfo {
|
|||||||
let mut ips = Vec::with_capacity(2);
|
let mut ips = Vec::with_capacity(2);
|
||||||
|
|
||||||
if let Some(ipv4) = &self.ip_v4 {
|
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());
|
return Err(StructureExtraction("IPv4 prefix is invalid!").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(nat) = &ipv4.nat {
|
||||||
|
for n in nat {
|
||||||
|
n.check()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ips.push(NetworkIPXML {
|
ips.push(NetworkIPXML {
|
||||||
family: "ipv4".to_string(),
|
family: "ipv4".to_string(),
|
||||||
address: IpAddr::V4(ipv4.bridge_address),
|
address: IpAddr::V4(ipv4.bridge_address),
|
||||||
prefix: ipv4.prefix,
|
prefix: Some(ipv4.prefix),
|
||||||
netmask: Ipv4Network::new(ipv4.bridge_address, ipv4.prefix as u8)
|
netmask: Some(
|
||||||
.unwrap()
|
Ipv4Network::new(ipv4.bridge_address, ipv4.prefix)
|
||||||
.mask()
|
.unwrap()
|
||||||
.into(),
|
.mask()
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
dhcp: ipv4.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
|
dhcp: ipv4.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
|
||||||
range: NetworkDHCPRangeXML {
|
range: NetworkDHCPRangeXML {
|
||||||
start: IpAddr::V4(dhcp.start),
|
start: IpAddr::V4(dhcp.start),
|
||||||
@ -146,14 +155,26 @@ impl NetworkInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ipv6) = &self.ip_v6 {
|
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 {
|
ips.push(NetworkIPXML {
|
||||||
family: "ipv6".to_string(),
|
family: "ipv6".to_string(),
|
||||||
address: IpAddr::V6(ipv6.bridge_address),
|
address: IpAddr::V6(ipv6.bridge_address),
|
||||||
prefix: ipv6.prefix,
|
prefix: Some(ipv6.prefix),
|
||||||
netmask: Ipv6Network::new(ipv6.bridge_address, ipv6.prefix as u8)
|
netmask: Some(
|
||||||
.unwrap()
|
Ipv6Network::new(ipv6.bridge_address, ipv6.prefix)
|
||||||
.mask()
|
.unwrap()
|
||||||
.into(),
|
.mask()
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
dhcp: ipv6.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
|
dhcp: ipv6.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
|
||||||
range: NetworkDHCPRangeXML {
|
range: NetworkDHCPRangeXML {
|
||||||
start: IpAddr::V6(dhcp.start),
|
start: IpAddr::V6(dhcp.start),
|
||||||
@ -226,10 +247,9 @@ impl NetworkInfo {
|
|||||||
.map(|i| IPV4Config {
|
.map(|i| IPV4Config {
|
||||||
bridge_address: extract_ipv4(i.address),
|
bridge_address: extract_ipv4(i.address),
|
||||||
prefix: match i.prefix {
|
prefix: match i.prefix {
|
||||||
u32::MAX => ipnetwork::ipv4_mask_to_prefix(extract_ipv4(i.netmask))
|
None => ipnetwork::ipv4_mask_to_prefix(extract_ipv4(i.netmask.unwrap()))
|
||||||
.expect("Failed to convert IPv4 netmask to network")
|
.expect("Failed to convert IPv4 netmask to network"),
|
||||||
as u32,
|
Some(p) => p,
|
||||||
p => p,
|
|
||||||
},
|
},
|
||||||
dhcp: i.dhcp.as_ref().map(|d| IPv4DHCPConfig {
|
dhcp: i.dhcp.as_ref().map(|d| IPv4DHCPConfig {
|
||||||
start: extract_ipv4(d.range.start),
|
start: extract_ipv4(d.range.start),
|
||||||
@ -253,10 +273,9 @@ impl NetworkInfo {
|
|||||||
.map(|i| IPV6Config {
|
.map(|i| IPV6Config {
|
||||||
bridge_address: extract_ipv6(i.address),
|
bridge_address: extract_ipv6(i.address),
|
||||||
prefix: match i.prefix {
|
prefix: match i.prefix {
|
||||||
u32::MAX => ipnetwork::ipv6_mask_to_prefix(extract_ipv6(i.netmask))
|
None => ipnetwork::ipv6_mask_to_prefix(extract_ipv6(i.netmask.unwrap()))
|
||||||
.expect("Failed to convert IPv6 netmask to network")
|
.expect("Failed to convert IPv6 netmask to network"),
|
||||||
as u32,
|
Some(p) => p,
|
||||||
p => p,
|
|
||||||
},
|
},
|
||||||
dhcp: i.dhcp.as_ref().map(|d| IPv6DHCPConfig {
|
dhcp: i.dhcp.as_ref().map(|d| IPv6DHCPConfig {
|
||||||
start: extract_ipv6(d.range.start),
|
start: extract_ipv6(d.range.start),
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
|
use crate::utils::net_utils;
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
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)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(tag = "type", rename_all = "lowercase")]
|
#[serde(tag = "type", rename_all = "lowercase")]
|
||||||
pub enum NatSource<IPv> {
|
pub enum NatSource<IPv> {
|
||||||
@ -31,6 +38,36 @@ pub struct Nat<IPv> {
|
|||||||
pub comment: Option<String>,
|
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)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||||
pub struct NetNat {
|
pub struct NetNat {
|
||||||
pub interface: String,
|
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())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::utils::net_utils::{
|
use crate::utils::net_utils::{
|
||||||
is_ipv4_address_valid, is_ipv6_address_valid, is_mac_address_valid, is_mask_valid,
|
is_ipv4_address_valid, is_ipv6_address_valid, is_mac_address_valid, is_mask_valid,
|
||||||
|
is_net_interface_name_valid,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -98,4 +103,13 @@ mod tests {
|
|||||||
assert!(is_mask_valid(6, 128));
|
assert!(is_mask_valid(6, 128));
|
||||||
assert!(!is_mask_valid(6, 129));
|
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 "));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user