diff --git a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs index 76fd522..535d1f0 100644 --- a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs +++ b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs @@ -57,25 +57,36 @@ fn extract_mac_address_or_var( } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct NetworkFilterIPv4OrVar(pub String); +pub struct NetworkFilterIPOrVar(pub String); +pub type NetworkFilterIPv4OrVar = NetworkFilterIPOrVar<4>; -impl NetworkFilterIPv4OrVar { +impl NetworkFilterIPOrVar { pub fn is_valid(&self) -> bool { - is_var_def(&self.0) || net_utils::is_ipv4_address_valid(&self.0) + is_var_def(&self.0) + || match V { + 4 => net_utils::is_ipv4_address_valid(&self.0), + 6 => net_utils::is_ipv6_address_valid(&self.0), + _ => panic!("Unsupported IP version!"), + } } } -impl From<&String> for NetworkFilterIPv4OrVar { +impl From<&String> for NetworkFilterIPOrVar { fn from(value: &String) -> Self { + if V != 4 && V != 6 { + panic!("Unsupported IP version!"); + } Self(value.to_string()) } } -fn extract_ipv4_or_var(n: &Option) -> anyhow::Result> { +fn extract_ip_or_var( + n: &Option>, +) -> anyhow::Result> { if let Some(ip) = n { if !ip.is_valid() { return Err(NetworkFilterExtraction(format!( - "Invalid IPv4 address or variable! {}", + "Invalid IPv{V} address or variable! {}", ip.0 )) .into()); @@ -85,6 +96,16 @@ fn extract_ipv4_or_var(n: &Option) -> anyhow::Result(n: &Option) -> anyhow::Result> { + if let Some(mask) = n { + if !net_utils::is_mask_valid(V, *mask) { + return Err(NetworkFilterExtraction(format!("Invalid IPv{V} mask! {}", mask)).into()); + } + } + + Ok(*n) +} + fn extract_nw_filter_comment(n: &Option) -> anyhow::Result> { if let Some(comment) = n { if comment.len() > 256 || comment.contains('\"') || comment.contains('\n') { @@ -212,15 +233,17 @@ impl NetworkFilter { } } - fn lib2rest_process_ip_rule(n: &NetworkFilterRuleProtocolIpvx) -> NetworkFilterSelectorIP { + fn lib2rest_process_ip_rule( + n: &NetworkFilterRuleProtocolIpvx, + ) -> NetworkFilterSelectorIP { NetworkFilterSelectorIP { - srcmacaddr: n.srcmacaddr.clone(), - srcmacmask: n.srcmacmask.clone(), - dstmacaddr: n.dstmacaddr.clone(), - dstmacmask: n.dstmacmask.clone(), - srcipaddr: n.srcipaddr.clone(), + srcmacaddr: n.srcmacaddr.as_ref().map(|v| v.into()), + srcmacmask: n.srcmacmask.as_ref().map(|v| v.into()), + dstmacaddr: n.dstmacaddr.as_ref().map(|v| v.into()), + dstmacmask: n.dstmacmask.as_ref().map(|v| v.into()), + srcipaddr: n.srcipaddr.as_ref().map(|v| v.into()), srcipmask: n.srcipmask, - dstipaddr: n.dstipaddr.clone(), + dstipaddr: n.dstipaddr.as_ref().map(|v| v.into()), dstipmask: n.dstipmask, comment: n.comment.clone(), } @@ -428,14 +451,30 @@ impl NetworkFilter { srcmacmask: extract_mac_address_or_var(&selector.srcmacmask)?, dstmacaddr: extract_mac_address_or_var(&selector.dstmacaddr)?, dstmacmask: extract_mac_address_or_var(&selector.dstmacmask)?, - arpsrcipaddr: extract_ipv4_or_var(&selector.arpsrcipaddr)?, + arpsrcipaddr: extract_ip_or_var(&selector.arpsrcipaddr)?, arpsrcipmask: selector.arpsrcipmask, - arpdstipaddr: extract_ipv4_or_var(&selector.arpdstipaddr)?, + arpdstipaddr: extract_ip_or_var(&selector.arpdstipaddr)?, arpdstipmask: selector.arpdstipmask, comment: extract_nw_filter_comment(&selector.comment)?, }) } + fn rest2lib_process_ip_selector( + selector: &NetworkFilterSelectorIP, + ) -> anyhow::Result { + Ok(NetworkFilterRuleProtocolIpvx { + srcmacaddr: extract_mac_address_or_var(&selector.srcmacaddr)?, + srcmacmask: extract_mac_address_or_var(&selector.srcmacmask)?, + dstmacaddr: extract_mac_address_or_var(&selector.dstmacaddr)?, + dstmacmask: extract_mac_address_or_var(&selector.dstmacmask)?, + srcipaddr: extract_ip_or_var(&selector.srcipaddr)?, + srcipmask: extract_ip_mask::(&selector.srcipmask)?, + dstipaddr: extract_ip_or_var(&selector.dstipaddr)?, + dstipmask: extract_ip_mask::(&selector.dstipmask)?, + comment: extract_nw_filter_comment(&selector.comment)?, + }) + } + fn rest2lib_process_rule(rule: &NetworkFilterRule) -> anyhow::Result { let mut rule_xml = NetworkFilterRuleXML { action: rule.action.to_xml(), @@ -484,8 +523,13 @@ impl NetworkFilter { .push(Self::rest2lib_process_arp_selector(a)?); } - NetworkFilterSelector::IPv4(_) => todo!(), - NetworkFilterSelector::IPv6(_) => todo!(), + NetworkFilterSelector::IPv4(ip) => rule_xml + .ipv4_selectors + .push(Self::rest2lib_process_ip_selector(ip)?), + + NetworkFilterSelector::IPv6(ip) => rule_xml + .ipv6_selectors + .push(Self::rest2lib_process_ip_selector(ip)?), NetworkFilterSelector::TCP(_) => todo!(), NetworkFilterSelector::UDP(_) => todo!(), @@ -686,14 +730,14 @@ pub struct NetworkSelectorARP { } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct NetworkFilterSelectorIP { - srcmacaddr: Option, - srcmacmask: Option, - dstmacaddr: Option, - dstmacmask: Option, - srcipaddr: Option, +pub struct NetworkFilterSelectorIP { + srcmacaddr: Option, + srcmacmask: Option, + dstmacaddr: Option, + dstmacmask: Option, + srcipaddr: Option>, srcipmask: Option, - dstipaddr: Option, + dstipaddr: Option>, dstipmask: Option, comment: Option, } @@ -728,8 +772,8 @@ pub enum NetworkFilterSelector { Mac(NetworkSelectorMac), Arp(NetworkSelectorARP), Rarp(NetworkSelectorARP), - IPv4(NetworkFilterSelectorIP), - IPv6(NetworkFilterSelectorIP), + IPv4(NetworkFilterSelectorIP<4>), + IPv6(NetworkFilterSelectorIP<6>), TCP(NetworkSelectorLayer4), UDP(NetworkSelectorLayer4), SCTP(NetworkSelectorLayer4), diff --git a/virtweb_backend/src/utils/net_utils.rs b/virtweb_backend/src/utils/net_utils.rs index e4b29b2..d9bf679 100644 --- a/virtweb_backend/src/utils/net_utils.rs +++ b/virtweb_backend/src/utils/net_utils.rs @@ -23,13 +23,35 @@ pub fn is_ipv4_address_valid>(ip: D) -> bool { Ipv4Addr::from_str(ip.as_ref()).is_ok() } +pub fn is_ipv6_address_valid>(ip: D) -> bool { + Ipv6Addr::from_str(ip.as_ref()).is_ok() +} + +pub fn is_ipv4_mask_valid(mask: u8) -> bool { + mask <= 32 +} + +pub fn is_ipv6_mask_valid(mask: u8) -> bool { + mask <= 64 +} + +pub fn is_mask_valid(ipv: usize, mask: u8) -> bool { + match ipv { + 4 => is_ipv4_mask_valid(mask), + 6 => is_ipv6_mask_valid(mask), + _ => panic!("Unsupported IP version"), + } +} + pub fn is_mac_address_valid>(mac: D) -> bool { lazy_regex::regex!("^([a-fA-F0-9]{2}[:-]){5}[a-fA-F0-9]{2}$").is_match(mac.as_ref()) } #[cfg(test)] mod tests { - use crate::utils::net_utils::{is_ipv4_address_valid, is_mac_address_valid}; + use crate::utils::net_utils::{ + is_ipv4_address_valid, is_ipv6_address_valid, is_mac_address_valid, is_mask_valid, + }; #[test] fn test_is_mac_address_valid() { @@ -49,5 +71,28 @@ mod tests { assert!(!is_ipv4_address_valid("tata")); assert!(!is_ipv4_address_valid("1.25.25.288")); assert!(!is_ipv4_address_valid("5.5.5.5.5")); + assert!(!is_ipv4_address_valid("fe80::")); + } + + #[test] + fn test_is_ipv6_address_valid() { + assert!(is_ipv6_address_valid("fe80::")); + assert!(is_ipv6_address_valid("fe80:dd::")); + assert!(is_ipv6_address_valid("00:00:00:00:00::")); + + assert!(!is_ipv6_address_valid("tata")); + assert!(!is_ipv6_address_valid("2.56.58.156")); + assert!(!is_ipv6_address_valid("fe::dd::dd")); + } + + #[test] + fn test_is_mask_valid() { + assert!(is_mask_valid(4, 25)); + assert!(is_mask_valid(4, 32)); + assert!(is_mask_valid(6, 32)); + assert!(is_mask_valid(6, 34)); + + assert!(!is_mask_valid(4, 34)); + assert!(!is_mask_valid(6, 69)); } }