From 388a1ed478ddbcb3a60d08e306a3bc366e604bf8 Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Mon, 1 Jan 2024 15:59:31 +0100 Subject: [PATCH] Add Layer4 selectors extraction --- .../src/libvirt_lib_structures/nwfilter.rs | 4 +- .../src/libvirt_rest_structures/nw_filter.rs | 506 ++++++++++-------- 2 files changed, 272 insertions(+), 238 deletions(-) diff --git a/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs b/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs index 68944cb..b429a69 100644 --- a/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs +++ b/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs @@ -227,7 +227,7 @@ pub struct NetworkFilterRuleProtocolLayer4 { pub comment: Option, } -#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Default)] #[serde(rename = "rule")] pub struct NetworkFilterRuleXML { #[serde(rename(serialize = "@action"))] @@ -275,7 +275,7 @@ pub struct NetworkFilterRuleXML { /// Match ICMP protocol #[serde(default, rename = "icmp", skip_serializing_if = "Vec::is_empty")] - pub imcp_selectors: Vec>, + pub icmp_selectors: Vec>, /// Match TCP IPv6 protocol #[serde(default, rename = "tcp-ipv6", skip_serializing_if = "Vec::is_empty")] diff --git a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs index 535d1f0..69a2dbb 100644 --- a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs +++ b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs @@ -96,14 +96,14 @@ fn extract_ip_or_var( Ok(n.as_ref().map(|n| n.0.to_string())) } -fn extract_ip_mask(n: &Option) -> anyhow::Result> { +fn extract_ip_mask(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()); + if !net_utils::is_mask_valid(V, mask) { + return Err(NetworkFilterExtraction(format!("Invalid IPv{V} mask! {mask}")).into()); } } - Ok(*n) + Ok(n) } fn extract_nw_filter_comment(n: &Option) -> anyhow::Result> { @@ -192,6 +192,211 @@ impl NetworkFilterChain { } } +#[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "lowercase")] +pub enum NetworkFilterAction { + /// matching the rule silently discards the packet with no further analysis + Drop, + /// matching the rule generates an ICMP reject message with no further analysis + Reject, + /// matching the rule accepts the packet with no further analysis + Accept, + /// matching the rule passes this filter, but returns control to the calling filter for further + /// analysis + Return, + /// matching the rule goes on to the next rule for further analysis + Continue, +} + +impl NetworkFilterAction { + pub fn from_xml(xml: &str) -> anyhow::Result { + Ok(match xml { + "drop" => Self::Drop, + "reject" => Self::Reject, + "accept" => Self::Accept, + "return" => Self::Return, + "continue" => Self::Continue, + s => { + return Err(LibVirtStructError::ParseFilteringChain(format!( + "Unkown filter action {s}!" + )) + .into()); + } + }) + } + + pub fn to_xml(&self) -> String { + match self { + Self::Drop => "drop", + Self::Reject => "reject", + Self::Accept => "accept", + Self::Return => "return", + Self::Continue => "continue", + } + .to_string() + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +#[serde(rename_all = "lowercase")] +pub enum NetworkFilterDirection { + In, + Out, + InOut, +} + +impl NetworkFilterDirection { + pub fn from_xml(xml: &str) -> anyhow::Result { + Ok(match xml { + "in" => Self::In, + "out" => Self::Out, + "inout" => Self::InOut, + s => { + return Err(LibVirtStructError::ParseFilteringChain(format!( + "Unkown filter direction {s}!" + )) + .into()); + } + }) + } + + pub fn to_xml(&self) -> String { + match self { + NetworkFilterDirection::In => "in", + NetworkFilterDirection::Out => "out", + NetworkFilterDirection::InOut => "inout", + } + .to_string() + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)] +pub enum Layer4State { + NEW, + ESTABLISHED, + RELATED, + INVALID, + NONE, +} + +impl Layer4State { + pub fn from_xml(xml: &str) -> anyhow::Result { + Ok(match xml { + "NEW" => Self::NEW, + "ESTABLISHED" => Self::ESTABLISHED, + "RELATED" => Self::RELATED, + "INVALID" => Self::INVALID, + "NONE" => Self::NONE, + s => { + return Err(LibVirtStructError::ParseFilteringChain(format!( + "Unkown layer4 state '{s}'!" + )) + .into()); + } + }) + } + + pub fn to_xml(&self) -> String { + match self { + Self::NEW => "NEW", + Self::ESTABLISHED => "ESTABLISHED", + Self::RELATED => "RELATED", + Self::INVALID => "INVALID", + Self::NONE => "NONE", + } + .to_string() + } +} + +#[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, + comment: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct NetworkSelectorARP { + srcmacaddr: Option, + srcmacmask: Option, + dstmacaddr: Option, + dstmacmask: Option, + arpsrcipaddr: Option, + arpsrcipmask: Option, + arpdstipaddr: Option, + arpdstipmask: Option, + comment: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct NetworkFilterSelectorIP { + srcmacaddr: Option, + srcmacmask: Option, + dstmacaddr: Option, + dstmacmask: Option, + srcipaddr: Option>, + srcipmask: Option, + dstipaddr: Option>, + dstipmask: Option, + comment: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct NetworkFilterSelectorLayer4 { + srcmacaddr: Option, + srcipaddr: Option, + srcipmask: Option, + dstipaddr: Option, + dstipmask: Option, + /// Start of range of source IP address + srcipfrom: Option, + /// End of range of source IP address + srcipto: Option, + /// Start of range of destination IP address + dstipfrom: Option, + /// End of range of destination IP address + dstipto: Option, + srcportstart: Option, + srcportend: Option, + dstportstart: Option, + dstportend: Option, + state: Option, + comment: Option, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum NetworkFilterSelector { + All, + Mac(NetworkSelectorMac), + Arp(NetworkSelectorARP), + Rarp(NetworkSelectorARP), + IPv4(NetworkFilterSelectorIP<4>), + IPv6(NetworkFilterSelectorIP<6>), + TCP(NetworkFilterSelectorLayer4), + UDP(NetworkFilterSelectorLayer4), + SCTP(NetworkFilterSelectorLayer4), + ICMP(NetworkFilterSelectorLayer4), + TCPipv6(NetworkFilterSelectorLayer4), + UDPipv6(NetworkFilterSelectorLayer4), + SCTPipv6(NetworkFilterSelectorLayer4), + ICMPipv6(NetworkFilterSelectorLayer4), +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct NetworkFilterRule { + action: NetworkFilterAction, + direction: NetworkFilterDirection, + /// optional; the priority of the rule controls the order in which the rule will be instantiated + /// relative to other rules + /// + /// Valid values are in the range of -1000 to 1000. + priority: Option, + selectors: Vec, +} + /// Network filter definition #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct NetworkFilter { @@ -251,8 +456,8 @@ impl NetworkFilter { fn lib2rest_process_layer4_rule( n: &NetworkFilterRuleProtocolLayer4, - ) -> anyhow::Result> { - Ok(NetworkSelectorLayer4 { + ) -> anyhow::Result> { + Ok(NetworkFilterSelectorLayer4 { srcmacaddr: n.srcmacaddr.as_ref().map(|v| v.into()), srcipaddr: n.srcipaddr, srcipmask: n.srcipmask, @@ -362,7 +567,7 @@ impl NetworkFilter { ); selectors.append( &mut rule - .imcp_selectors + .icmp_selectors .iter() .map(|r| { Ok(NetworkFilterSelector::ICMP( @@ -468,9 +673,33 @@ impl NetworkFilter { 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)?, + srcipmask: extract_ip_mask::(selector.srcipmask)?, dstipaddr: extract_ip_or_var(&selector.dstipaddr)?, - dstipmask: extract_ip_mask::(&selector.dstipmask)?, + dstipmask: extract_ip_mask::(selector.dstipmask)?, + comment: extract_nw_filter_comment(&selector.comment)?, + }) + } + + fn rest2lib_process_layer4_selector( + selector: &NetworkFilterSelectorLayer4, + ) -> anyhow::Result> { + Ok(NetworkFilterRuleProtocolLayer4 { + srcmacaddr: extract_mac_address_or_var(&selector.srcmacaddr)?, + srcipaddr: selector.srcipaddr, + // This IP mask is not checked + srcipmask: selector.srcipmask, + dstipaddr: selector.dstipaddr, + // This IP mask is not checked + dstipmask: selector.dstipmask, + srcipfrom: selector.srcipfrom, + srcipto: selector.srcipto, + dstipfrom: selector.dstipfrom, + dstipto: selector.dstipto, + srcportstart: selector.srcportstart, + srcportend: selector.srcportend, + dstportstart: selector.dstportstart, + dstportend: selector.dstportend, + state: selector.state.map(|s| s.to_xml()), comment: extract_nw_filter_comment(&selector.comment)?, }) } @@ -480,20 +709,7 @@ impl NetworkFilter { action: rule.action.to_xml(), direction: rule.direction.to_xml(), priority: rule.priority, - all_selectors: vec![], - mac_selectors: vec![], - arp_selectors: vec![], - rarp_selectors: vec![], - ipv4_selectors: vec![], - ipv6_selectors: vec![], - tcp_selectors: vec![], - udp_selectors: vec![], - sctp_selectors: vec![], - imcp_selectors: vec![], - tcp_ipv6_selectors: vec![], - udp_ipv6_selectors: vec![], - sctp_ipv6_selectors: vec![], - imcp_ipv6_selectors: vec![], + ..Default::default() }; for sel in &rule.selectors { @@ -531,14 +747,37 @@ impl NetworkFilter { .ipv6_selectors .push(Self::rest2lib_process_ip_selector(ip)?), - NetworkFilterSelector::TCP(_) => todo!(), - NetworkFilterSelector::UDP(_) => todo!(), - NetworkFilterSelector::SCTP(_) => todo!(), - NetworkFilterSelector::ICMP(_) => todo!(), - NetworkFilterSelector::TCPipv6(_) => todo!(), - NetworkFilterSelector::UDPipv6(_) => todo!(), - NetworkFilterSelector::SCTPipv6(_) => todo!(), - NetworkFilterSelector::ICMPipv6(_) => todo!(), + NetworkFilterSelector::TCP(tcp) => rule_xml + .tcp_selectors + .push(Self::rest2lib_process_layer4_selector(tcp)?), + + NetworkFilterSelector::UDP(udp) => rule_xml + .udp_selectors + .push(Self::rest2lib_process_layer4_selector(udp)?), + + NetworkFilterSelector::SCTP(sctp) => rule_xml + .sctp_selectors + .push(Self::rest2lib_process_layer4_selector(sctp)?), + + NetworkFilterSelector::ICMP(icmp) => rule_xml + .icmp_selectors + .push(Self::rest2lib_process_layer4_selector(icmp)?), + + NetworkFilterSelector::TCPipv6(tcpv6) => rule_xml + .tcp_ipv6_selectors + .push(Self::rest2lib_process_layer4_selector(tcpv6)?), + + NetworkFilterSelector::UDPipv6(udpv6) => rule_xml + .udp_ipv6_selectors + .push(Self::rest2lib_process_layer4_selector(udpv6)?), + + NetworkFilterSelector::SCTPipv6(sctpv6) => rule_xml + .sctp_ipv6_selectors + .push(Self::rest2lib_process_layer4_selector(sctpv6)?), + + NetworkFilterSelector::ICMPipv6(icmpv6) => rule_xml + .imcp_ipv6_selectors + .push(Self::rest2lib_process_layer4_selector(icmpv6)?), } } @@ -591,211 +830,6 @@ impl NetworkFilter { } } -#[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)] -#[serde(rename_all = "lowercase")] -pub enum NetworkFilterAction { - /// matching the rule silently discards the packet with no further analysis - Drop, - /// matching the rule generates an ICMP reject message with no further analysis - Reject, - /// matching the rule accepts the packet with no further analysis - Accept, - /// matching the rule passes this filter, but returns control to the calling filter for further - /// analysis - Return, - /// matching the rule goes on to the next rule for further analysis - Continue, -} - -impl NetworkFilterAction { - pub fn from_xml(xml: &str) -> anyhow::Result { - Ok(match xml { - "drop" => Self::Drop, - "reject" => Self::Reject, - "accept" => Self::Accept, - "return" => Self::Return, - "continue" => Self::Continue, - s => { - return Err(LibVirtStructError::ParseFilteringChain(format!( - "Unkown filter action {s}!" - )) - .into()); - } - }) - } - - pub fn to_xml(&self) -> String { - match self { - Self::Drop => "drop", - Self::Reject => "reject", - Self::Accept => "accept", - Self::Return => "return", - Self::Continue => "continue", - } - .to_string() - } -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -#[serde(rename_all = "lowercase")] -pub enum NetworkFilterDirection { - In, - Out, - InOut, -} - -impl NetworkFilterDirection { - pub fn from_xml(xml: &str) -> anyhow::Result { - Ok(match xml { - "in" => Self::In, - "out" => Self::Out, - "inout" => Self::InOut, - s => { - return Err(LibVirtStructError::ParseFilteringChain(format!( - "Unkown filter direction {s}!" - )) - .into()); - } - }) - } - - pub fn to_xml(&self) -> String { - match self { - NetworkFilterDirection::In => "in", - NetworkFilterDirection::Out => "out", - NetworkFilterDirection::InOut => "inout", - } - .to_string() - } -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub enum Layer4State { - NEW, - ESTABLISHED, - RELATED, - INVALID, - NONE, -} - -impl Layer4State { - pub fn from_xml(xml: &str) -> anyhow::Result { - Ok(match xml { - "NEW" => Self::NEW, - "ESTABLISHED" => Self::ESTABLISHED, - "RELATED" => Self::RELATED, - "INVALID" => Self::INVALID, - "NONE" => Self::NONE, - s => { - return Err(LibVirtStructError::ParseFilteringChain(format!( - "Unkown layer4 state '{s}'!" - )) - .into()); - } - }) - } - - pub fn to_xml(&self) -> String { - match self { - Self::NEW => "NEW", - Self::ESTABLISHED => "ESTABLISHED", - Self::RELATED => "RELATED", - Self::INVALID => "INVALID", - Self::NONE => "NONE", - } - .to_string() - } -} - -#[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, - comment: Option, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct NetworkSelectorARP { - srcmacaddr: Option, - srcmacmask: Option, - dstmacaddr: Option, - dstmacmask: Option, - arpsrcipaddr: Option, - arpsrcipmask: Option, - arpdstipaddr: Option, - arpdstipmask: Option, - comment: Option, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct NetworkFilterSelectorIP { - srcmacaddr: Option, - srcmacmask: Option, - dstmacaddr: Option, - dstmacmask: Option, - srcipaddr: Option>, - srcipmask: Option, - dstipaddr: Option>, - dstipmask: Option, - comment: Option, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct NetworkSelectorLayer4 { - srcmacaddr: Option, - srcipaddr: Option, - srcipmask: Option, - dstipaddr: Option, - dstipmask: Option, - /// Start of range of source IP address - srcipfrom: Option, - /// End of range of source IP address - srcipto: Option, - /// Start of range of destination IP address - dstipfrom: Option, - /// End of range of destination IP address - dstipto: Option, - srcportstart: Option, - srcportend: Option, - dstportstart: Option, - dstportend: Option, - state: Option, - comment: Option, -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -#[serde(tag = "type", rename_all = "lowercase")] -pub enum NetworkFilterSelector { - All, - Mac(NetworkSelectorMac), - Arp(NetworkSelectorARP), - Rarp(NetworkSelectorARP), - IPv4(NetworkFilterSelectorIP<4>), - IPv6(NetworkFilterSelectorIP<6>), - TCP(NetworkSelectorLayer4), - UDP(NetworkSelectorLayer4), - SCTP(NetworkSelectorLayer4), - ICMP(NetworkSelectorLayer4), - TCPipv6(NetworkSelectorLayer4), - UDPipv6(NetworkSelectorLayer4), - SCTPipv6(NetworkSelectorLayer4), - ICMPipv6(NetworkSelectorLayer4), -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct NetworkFilterRule { - action: NetworkFilterAction, - direction: NetworkFilterDirection, - /// optional; the priority of the rule controls the order in which the rule will be instantiated - /// relative to other rules - /// - /// Valid values are in the range of -1000 to 1000. - priority: Option, - selectors: Vec, -} - #[cfg(test)] mod test { use crate::libvirt_rest_structures::nw_filter::is_var_def;