diff --git a/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs b/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs index a7cd628..0febbd9 100644 --- a/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs +++ b/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs @@ -238,8 +238,8 @@ pub struct NetworkFilterRuleXML { pub priority: Option, /// Match all protocols - #[serde(skip_serializing_if = "Vec::is_empty")] - pub all: Vec, + #[serde(default, rename = "all", skip_serializing_if = "Vec::is_empty")] + pub all_selectors: Vec, /// Match mac protocol #[serde(default, rename = "mac", skip_serializing_if = "Vec::is_empty")] diff --git a/virtweb_backend/src/libvirt_rest_structures/mod.rs b/virtweb_backend/src/libvirt_rest_structures/mod.rs index d97dea0..8984e81 100644 --- a/virtweb_backend/src/libvirt_rest_structures/mod.rs +++ b/virtweb_backend/src/libvirt_rest_structures/mod.rs @@ -11,4 +11,6 @@ enum LibVirtStructError { DomainExtraction(String), #[error("ParseFilteringChain: {0}")] ParseFilteringChain(String), + #[error("NetworkFilterExtractionError: {0}")] + NetworkFilterExtraction(String), } diff --git a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs index ce5e6d7..c817d6f 100644 --- a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs +++ b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs @@ -5,10 +5,17 @@ use crate::libvirt_lib_structures::nwfilter::{ }; use crate::libvirt_lib_structures::XMLUuid; use crate::libvirt_rest_structures::LibVirtStructError; -use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction; +use crate::libvirt_rest_structures::LibVirtStructError::{ + NetworkFilterExtraction, StructureExtraction, +}; +use crate::utils::net_utils; use lazy_regex::regex; use std::net::{Ipv4Addr, Ipv6Addr}; +pub fn is_var_def(var: &str) -> bool { + lazy_regex::regex!("^\\$[a-zA-Z0-9_]+$").is_match(var) +} + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct NetworkFilterName(pub String); @@ -18,6 +25,47 @@ impl NetworkFilterName { } } +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct NetworkFilterMacAddressOrVar(pub String); + +impl NetworkFilterMacAddressOrVar { + pub fn is_valid(&self) -> bool { + is_var_def(&self.0) || net_utils::is_mac_address_valid(&self.0) + } +} + +impl From<&String> for NetworkFilterMacAddressOrVar { + fn from(value: &String) -> Self { + Self(value.to_string()) + } +} + +fn extract_mac_address_or_var( + n: &Option, +) -> anyhow::Result> { + if let Some(mac) = n { + if !mac.is_valid() { + return Err(NetworkFilterExtraction(format!( + "Invalid mac address or variable! {}", + mac.0 + )) + .into()); + } + } + + Ok(n.as_ref().map(|n| n.0.to_string())) +} + +fn extract_nw_filter_comment(n: &Option) -> anyhow::Result> { + if let Some(comment) = n { + if comment.len() > 256 || comment.contains('\"') || comment.contains('\n') { + return Err(NetworkFilterExtraction(format!("Invalid comment! {}", comment)).into()); + } + } + + Ok(n.clone()) +} + #[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)] #[serde(rename_all = "lowercase")] pub enum NetworkFilterChainProtocol { @@ -113,20 +161,20 @@ impl NetworkFilter { fn lib2rest_process_mac_rule(n: &NetworkFilterRuleProtocolMac) -> NetworkFilterSelector { NetworkFilterSelector::Mac(NetworkSelectorMac { - src_mac_addr: n.srcmacaddr.clone(), - src_mac_mask: n.srcmacmask.clone(), - dst_mac_addr: n.dstmacaddr.clone(), - dst_mac_mask: n.dstmacmask.clone(), + src_mac_addr: n.srcmacaddr.as_ref().map(|v| v.into()), + src_mac_mask: n.srcmacmask.as_ref().map(|v| v.into()), + dst_mac_addr: n.dstmacaddr.as_ref().map(|v| v.into()), + dst_mac_mask: n.dstmacmask.as_ref().map(|v| v.into()), comment: n.comment.clone(), }) } fn lib2rest_process_arp_rule(n: &NetworkFilterRuleProtocolArp) -> NetworkSelectorARP { NetworkSelectorARP { - srcmacaddr: n.srcmacaddr.clone(), - srcmacmask: n.srcmacmask.clone(), - dstmacaddr: n.dstmacaddr.clone(), - dstmacmask: n.dstmacmask.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()), arpsrcipaddr: n.arpsrcipaddr.clone(), arpsrcipmask: n.arpsrcipmask, arpdstipaddr: n.arpdstipaddr.clone(), @@ -153,7 +201,7 @@ impl NetworkFilter { n: &NetworkFilterRuleProtocolLayer4, ) -> anyhow::Result> { Ok(NetworkSelectorLayer4 { - srcmacaddr: n.srcmacaddr.clone(), + srcmacaddr: n.srcmacaddr.as_ref().map(|v| v.into()), srcipaddr: n.srcipaddr, srcipmask: n.srcipmask, dstipaddr: n.dstipaddr, @@ -179,7 +227,7 @@ impl NetworkFilter { // All selector selectors.append( &mut rule - .all + .all_selectors .iter() .map(Self::lib2rest_process_all_rule) .collect(), @@ -348,7 +396,7 @@ impl NetworkFilter { action: rule.action.to_xml(), direction: rule.direction.to_xml(), priority: rule.priority, - all: vec![], + all_selectors: vec![], mac_selectors: vec![], arp_selectors: vec![], rarp_selectors: vec![], @@ -367,18 +415,17 @@ impl NetworkFilter { for sel in &rule.selectors { match sel { NetworkFilterSelector::All => { - rule_xml.all.push(NetworkFilterRuleProtocolAll {}); + rule_xml.all_selectors.push(NetworkFilterRuleProtocolAll {}); } NetworkFilterSelector::Mac(mac) => { - todo!() - /*rule_xml.mac_selectors.push(NetworkFilterRuleProtocolMac { - srcmacaddr: mac.src_mac_addr, - srcmacmask: mac.src_mac_mask, - dstmacaddr: mac., - dstmacmask: None, - comment: None, - })*/ + rule_xml.mac_selectors.push(NetworkFilterRuleProtocolMac { + srcmacaddr: extract_mac_address_or_var(&mac.src_mac_addr)?, + srcmacmask: extract_mac_address_or_var(&mac.src_mac_mask)?, + dstmacaddr: extract_mac_address_or_var(&mac.dst_mac_addr)?, + dstmacmask: extract_mac_address_or_var(&mac.dst_mac_mask)?, + comment: extract_nw_filter_comment(&mac.comment)?, + }) } NetworkFilterSelector::Arp(_) => todo!(), @@ -401,12 +448,16 @@ impl NetworkFilter { pub fn rest2lib(&self) -> anyhow::Result { if !self.name.is_valid() { - return Err(StructureExtraction("Network filter name is invalid!").into()); + return Err( + NetworkFilterExtraction("Network filter name is invalid!".to_string()).into(), + ); } if let Some(priority) = self.priority { if !(-1000..=1000).contains(&priority) { - return Err(StructureExtraction("Network priority is invalid!").into()); + return Err( + NetworkFilterExtraction("Network priority is invalid!".to_string()).into(), + ); } } @@ -572,19 +623,19 @@ pub struct NetworkFilterSelectorIP { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct NetworkSelectorMac { - src_mac_addr: Option, - src_mac_mask: Option, - dst_mac_addr: Option, - dst_mac_mask: Option, + src_mac_addr: Option, + src_mac_mask: Option, + dst_mac_addr: Option, + dst_mac_mask: Option, comment: Option, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct NetworkSelectorARP { - srcmacaddr: Option, - srcmacmask: Option, - dstmacaddr: Option, - dstmacmask: Option, + srcmacaddr: Option, + srcmacmask: Option, + dstmacaddr: Option, + dstmacmask: Option, arpsrcipaddr: Option, arpsrcipmask: Option, arpdstipaddr: Option, @@ -594,7 +645,7 @@ pub struct NetworkSelectorARP { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct NetworkSelectorLayer4 { - srcmacaddr: Option, + srcmacaddr: Option, srcipaddr: Option, srcipmask: Option, dstipaddr: Option, @@ -645,3 +696,19 @@ pub struct NetworkFilterRule { priority: Option, selectors: Vec, } + +#[cfg(test)] +mod test { + use crate::libvirt_rest_structures::nw_filter::is_var_def; + + #[test] + pub fn var_def() { + assert!(is_var_def("$MAC")); + assert!(is_var_def("$MAC_ADDRESS")); + + assert!(!is_var_def("$$MAC")); + assert!(!is_var_def("$$MACĂ©")); + assert!(!is_var_def("$$MAC@")); + assert!(!is_var_def("$$MAC TEST")); + } +}