Add Layer4 selectors extraction

This commit is contained in:
Pierre HUBERT 2024-01-01 15:59:31 +01:00
parent c6c1ce26d3
commit 388a1ed478
2 changed files with 272 additions and 238 deletions

View File

@ -227,7 +227,7 @@ pub struct NetworkFilterRuleProtocolLayer4<IPv> {
pub comment: Option<String>,
}
#[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<NetworkFilterRuleProtocolLayer4<Ipv4Addr>>,
pub icmp_selectors: Vec<NetworkFilterRuleProtocolLayer4<Ipv4Addr>>,
/// Match TCP IPv6 protocol
#[serde(default, rename = "tcp-ipv6", skip_serializing_if = "Vec::is_empty")]

View File

@ -96,14 +96,14 @@ fn extract_ip_or_var<const V: usize>(
Ok(n.as_ref().map(|n| n.0.to_string()))
}
fn extract_ip_mask<const V: usize>(n: &Option<u8>) -> anyhow::Result<Option<u8>> {
fn extract_ip_mask<const V: usize>(n: Option<u8>) -> anyhow::Result<Option<u8>> {
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<String>) -> anyhow::Result<Option<String>> {
@ -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<Self> {
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<Self> {
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<Self> {
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<NetworkFilterMacAddressOrVar>,
src_mac_mask: Option<NetworkFilterMacAddressOrVar>,
dst_mac_addr: Option<NetworkFilterMacAddressOrVar>,
dst_mac_mask: Option<NetworkFilterMacAddressOrVar>,
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NetworkSelectorARP {
srcmacaddr: Option<NetworkFilterMacAddressOrVar>,
srcmacmask: Option<NetworkFilterMacAddressOrVar>,
dstmacaddr: Option<NetworkFilterMacAddressOrVar>,
dstmacmask: Option<NetworkFilterMacAddressOrVar>,
arpsrcipaddr: Option<NetworkFilterIPv4OrVar>,
arpsrcipmask: Option<u8>,
arpdstipaddr: Option<NetworkFilterIPv4OrVar>,
arpdstipmask: Option<u8>,
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NetworkFilterSelectorIP<const V: usize> {
srcmacaddr: Option<NetworkFilterMacAddressOrVar>,
srcmacmask: Option<NetworkFilterMacAddressOrVar>,
dstmacaddr: Option<NetworkFilterMacAddressOrVar>,
dstmacmask: Option<NetworkFilterMacAddressOrVar>,
srcipaddr: Option<NetworkFilterIPOrVar<V>>,
srcipmask: Option<u8>,
dstipaddr: Option<NetworkFilterIPOrVar<V>>,
dstipmask: Option<u8>,
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NetworkFilterSelectorLayer4<IPv> {
srcmacaddr: Option<NetworkFilterMacAddressOrVar>,
srcipaddr: Option<IPv>,
srcipmask: Option<u8>,
dstipaddr: Option<IPv>,
dstipmask: Option<u8>,
/// Start of range of source IP address
srcipfrom: Option<IPv>,
/// End of range of source IP address
srcipto: Option<IPv>,
/// Start of range of destination IP address
dstipfrom: Option<IPv>,
/// End of range of destination IP address
dstipto: Option<IPv>,
srcportstart: Option<u16>,
srcportend: Option<u16>,
dstportstart: Option<u16>,
dstportend: Option<u16>,
state: Option<Layer4State>,
comment: Option<String>,
}
#[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<Ipv4Addr>),
UDP(NetworkFilterSelectorLayer4<Ipv4Addr>),
SCTP(NetworkFilterSelectorLayer4<Ipv4Addr>),
ICMP(NetworkFilterSelectorLayer4<Ipv4Addr>),
TCPipv6(NetworkFilterSelectorLayer4<Ipv6Addr>),
UDPipv6(NetworkFilterSelectorLayer4<Ipv6Addr>),
SCTPipv6(NetworkFilterSelectorLayer4<Ipv6Addr>),
ICMPipv6(NetworkFilterSelectorLayer4<Ipv6Addr>),
}
#[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<i32>,
selectors: Vec<NetworkFilterSelector>,
}
/// Network filter definition
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NetworkFilter {
@ -251,8 +456,8 @@ impl NetworkFilter {
fn lib2rest_process_layer4_rule<IPv: Copy>(
n: &NetworkFilterRuleProtocolLayer4<IPv>,
) -> anyhow::Result<NetworkSelectorLayer4<IPv>> {
Ok(NetworkSelectorLayer4 {
) -> anyhow::Result<NetworkFilterSelectorLayer4<IPv>> {
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::<V>(&selector.srcipmask)?,
srcipmask: extract_ip_mask::<V>(selector.srcipmask)?,
dstipaddr: extract_ip_or_var(&selector.dstipaddr)?,
dstipmask: extract_ip_mask::<V>(&selector.dstipmask)?,
dstipmask: extract_ip_mask::<V>(selector.dstipmask)?,
comment: extract_nw_filter_comment(&selector.comment)?,
})
}
fn rest2lib_process_layer4_selector<IPv: Copy>(
selector: &NetworkFilterSelectorLayer4<IPv>,
) -> anyhow::Result<NetworkFilterRuleProtocolLayer4<IPv>> {
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<Self> {
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<Self> {
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<Self> {
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<NetworkFilterMacAddressOrVar>,
src_mac_mask: Option<NetworkFilterMacAddressOrVar>,
dst_mac_addr: Option<NetworkFilterMacAddressOrVar>,
dst_mac_mask: Option<NetworkFilterMacAddressOrVar>,
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NetworkSelectorARP {
srcmacaddr: Option<NetworkFilterMacAddressOrVar>,
srcmacmask: Option<NetworkFilterMacAddressOrVar>,
dstmacaddr: Option<NetworkFilterMacAddressOrVar>,
dstmacmask: Option<NetworkFilterMacAddressOrVar>,
arpsrcipaddr: Option<NetworkFilterIPv4OrVar>,
arpsrcipmask: Option<u8>,
arpdstipaddr: Option<NetworkFilterIPv4OrVar>,
arpdstipmask: Option<u8>,
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NetworkFilterSelectorIP<const V: usize> {
srcmacaddr: Option<NetworkFilterMacAddressOrVar>,
srcmacmask: Option<NetworkFilterMacAddressOrVar>,
dstmacaddr: Option<NetworkFilterMacAddressOrVar>,
dstmacmask: Option<NetworkFilterMacAddressOrVar>,
srcipaddr: Option<NetworkFilterIPOrVar<V>>,
srcipmask: Option<u8>,
dstipaddr: Option<NetworkFilterIPOrVar<V>>,
dstipmask: Option<u8>,
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NetworkSelectorLayer4<IPv> {
srcmacaddr: Option<NetworkFilterMacAddressOrVar>,
srcipaddr: Option<IPv>,
srcipmask: Option<u8>,
dstipaddr: Option<IPv>,
dstipmask: Option<u8>,
/// Start of range of source IP address
srcipfrom: Option<IPv>,
/// End of range of source IP address
srcipto: Option<IPv>,
/// Start of range of destination IP address
dstipfrom: Option<IPv>,
/// End of range of destination IP address
dstipto: Option<IPv>,
srcportstart: Option<u16>,
srcportend: Option<u16>,
dstportstart: Option<u16>,
dstportend: Option<u16>,
state: Option<Layer4State>,
comment: Option<String>,
}
#[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<Ipv4Addr>),
UDP(NetworkSelectorLayer4<Ipv4Addr>),
SCTP(NetworkSelectorLayer4<Ipv4Addr>),
ICMP(NetworkSelectorLayer4<Ipv4Addr>),
TCPipv6(NetworkSelectorLayer4<Ipv6Addr>),
UDPipv6(NetworkSelectorLayer4<Ipv6Addr>),
SCTPipv6(NetworkSelectorLayer4<Ipv6Addr>),
ICMPipv6(NetworkSelectorLayer4<Ipv6Addr>),
}
#[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<i32>,
selectors: Vec<NetworkFilterSelector>,
}
#[cfg(test)]
mod test {
use crate::libvirt_rest_structures::nw_filter::is_var_def;