Parse NW filters XML structure
This commit is contained in:
		@@ -1,5 +1,7 @@
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use crate::libvirt_lib_structures::{DomainState, DomainXML, NetworkXML, XMLUuid};
 | 
			
		||||
use crate::libvirt_lib_structures::{
 | 
			
		||||
    DomainState, DomainXML, NetworkFilterXML, NetworkXML, XMLUuid,
 | 
			
		||||
};
 | 
			
		||||
use crate::libvirt_rest_structures::*;
 | 
			
		||||
use actix::{Actor, Context, Handler, Message};
 | 
			
		||||
use image::ImageOutputFormat;
 | 
			
		||||
@@ -7,6 +9,7 @@ use std::io::Cursor;
 | 
			
		||||
use virt::connect::Connect;
 | 
			
		||||
use virt::domain::Domain;
 | 
			
		||||
use virt::network::Network;
 | 
			
		||||
use virt::nwfilter::NWFilter;
 | 
			
		||||
use virt::stream::Stream;
 | 
			
		||||
use virt::sys;
 | 
			
		||||
use virt::sys::VIR_DOMAIN_XML_SECURE;
 | 
			
		||||
@@ -549,3 +552,39 @@ impl Handler<StopNetwork> for LibVirtActor {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Message)]
 | 
			
		||||
#[rtype(result = "anyhow::Result<Vec<XMLUuid>>")]
 | 
			
		||||
pub struct GetNWFiltersListReq;
 | 
			
		||||
 | 
			
		||||
impl Handler<GetNWFiltersListReq> for LibVirtActor {
 | 
			
		||||
    type Result = anyhow::Result<Vec<XMLUuid>>;
 | 
			
		||||
 | 
			
		||||
    fn handle(&mut self, _msg: GetNWFiltersListReq, _ctx: &mut Self::Context) -> Self::Result {
 | 
			
		||||
        log::debug!("Get full list of network filters");
 | 
			
		||||
        let networks = self.m.list_all_nw_filters(0)?;
 | 
			
		||||
        let mut ids = Vec::with_capacity(networks.len());
 | 
			
		||||
 | 
			
		||||
        for d in networks {
 | 
			
		||||
            ids.push(XMLUuid::parse_from_str(&d.get_uuid_string()?)?);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(ids)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Message)]
 | 
			
		||||
#[rtype(result = "anyhow::Result<NetworkFilterXML>")]
 | 
			
		||||
pub struct GetNWFilterXMLReq(pub XMLUuid);
 | 
			
		||||
 | 
			
		||||
impl Handler<GetNWFilterXMLReq> for LibVirtActor {
 | 
			
		||||
    type Result = anyhow::Result<NetworkFilterXML>;
 | 
			
		||||
 | 
			
		||||
    fn handle(&mut self, msg: GetNWFilterXMLReq, _ctx: &mut Self::Context) -> Self::Result {
 | 
			
		||||
        log::debug!("Get network filter XML:\n{}", msg.0.as_string());
 | 
			
		||||
        let filter = NWFilter::lookup_by_uuid_string(&self.m, &msg.0.as_string())?;
 | 
			
		||||
        let xml = filter.get_xml_desc(0)?;
 | 
			
		||||
        log::debug!("XML = {}", xml);
 | 
			
		||||
        NetworkFilterXML::parse_xml(xml)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -46,3 +46,31 @@ pub const DISK_SIZE_MAX: usize = 1000 * 1000 * 2;
 | 
			
		||||
 | 
			
		||||
/// Network mac address default prefix
 | 
			
		||||
pub const NET_MAC_ADDR_PREFIX: &str = "52:54:00";
 | 
			
		||||
 | 
			
		||||
/// Built-in network filter rules
 | 
			
		||||
pub const BUILTIN_NETWORK_FILTER_RULES: [&str; 24] = [
 | 
			
		||||
    "allow-arp",
 | 
			
		||||
    "allow-dhcp",
 | 
			
		||||
    "allow-dhcp-server",
 | 
			
		||||
    "allow-dhcpv6",
 | 
			
		||||
    "allow-dhcpv6-server",
 | 
			
		||||
    "allow-incoming-ipv4",
 | 
			
		||||
    "allow-incoming-ipv6",
 | 
			
		||||
    "allow-ipv4",
 | 
			
		||||
    "allow-ipv6",
 | 
			
		||||
    "clean-traffic",
 | 
			
		||||
    "clean-traffic-gateway",
 | 
			
		||||
    "no-arp-ip-spoofing",
 | 
			
		||||
    "no-arp-mac-spoofing",
 | 
			
		||||
    "no-arp-spoofing",
 | 
			
		||||
    "no-ip-multicast",
 | 
			
		||||
    "no-ip-spoofing",
 | 
			
		||||
    "no-ipv6-multicast",
 | 
			
		||||
    "no-ipv6-spoofing",
 | 
			
		||||
    "no-mac-broadcast",
 | 
			
		||||
    "no-mac-spoofing",
 | 
			
		||||
    "no-other-l2-traffic",
 | 
			
		||||
    "no-other-rarp-traffic",
 | 
			
		||||
    "qemu-announce-self",
 | 
			
		||||
    "qemu-announce-self-rarp",
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ use std::io::ErrorKind;
 | 
			
		||||
pub mod auth_controller;
 | 
			
		||||
pub mod iso_controller;
 | 
			
		||||
pub mod network_controller;
 | 
			
		||||
pub mod nwfilter_controller;
 | 
			
		||||
pub mod server_controller;
 | 
			
		||||
pub mod static_controller;
 | 
			
		||||
pub mod vm_controller;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								virtweb_backend/src/controllers/nwfilter_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								virtweb_backend/src/controllers/nwfilter_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
use crate::controllers::{HttpResult, LibVirtReq};
 | 
			
		||||
use crate::libvirt_lib_structures::XMLUuid;
 | 
			
		||||
use actix_web::{web, HttpResponse};
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct NetworkFilterID {
 | 
			
		||||
    uid: XMLUuid,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the list of network filters
 | 
			
		||||
pub async fn list(client: LibVirtReq) -> HttpResult {
 | 
			
		||||
    let networks = match client.get_full_network_filters_list().await {
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            log::error!("Failed to get the list of network filters! {e}");
 | 
			
		||||
            return Ok(HttpResponse::InternalServerError()
 | 
			
		||||
                .json(format!("Failed to get the list of networks! {e}")));
 | 
			
		||||
        }
 | 
			
		||||
        Ok(l) => l,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /*let networks = networks
 | 
			
		||||
    .into_iter()
 | 
			
		||||
    .map(|n| NetworkInfo::from_xml(n).unwrap())
 | 
			
		||||
    .collect::<Vec<_>>();*/
 | 
			
		||||
    // TODO : turn into lib structure
 | 
			
		||||
    println!("{:#?}", networks);
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().body(format!("{:#?}", networks)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the information about a single network filter
 | 
			
		||||
pub async fn get_single(client: LibVirtReq, req: web::Path<NetworkFilterID>) -> HttpResult {
 | 
			
		||||
    let nwfilter = client.get_single_network_filter(req.uid).await?;
 | 
			
		||||
    // TODO : turn into lib structure
 | 
			
		||||
    Ok(HttpResponse::Ok().body(format!("{:#?}", nwfilter)))
 | 
			
		||||
}
 | 
			
		||||
@@ -16,6 +16,7 @@ struct StaticConfig {
 | 
			
		||||
    iso_mimetypes: &'static [&'static str],
 | 
			
		||||
    net_mac_prefix: &'static str,
 | 
			
		||||
    constraints: ServerConstraints,
 | 
			
		||||
    builtin_network_rules: &'static [&'static str],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
@@ -45,6 +46,7 @@ pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
 | 
			
		||||
        oidc_auth_enabled: !AppConfig::get().disable_oidc,
 | 
			
		||||
        iso_mimetypes: &constants::ALLOWED_ISO_MIME_TYPES,
 | 
			
		||||
        net_mac_prefix: constants::NET_MAC_ADDR_PREFIX,
 | 
			
		||||
        builtin_network_rules: &constants::BUILTIN_NETWORK_FILTER_RULES,
 | 
			
		||||
        constraints: ServerConstraints {
 | 
			
		||||
            iso_max_size: constants::ISO_MAX_SIZE,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
use crate::actors::libvirt_actor;
 | 
			
		||||
use crate::actors::libvirt_actor::LibVirtActor;
 | 
			
		||||
use crate::libvirt_lib_structures::{DomainState, DomainXML, NetworkXML, XMLUuid};
 | 
			
		||||
use crate::libvirt_lib_structures::{
 | 
			
		||||
    DomainState, DomainXML, NetworkFilterXML, NetworkXML, XMLUuid,
 | 
			
		||||
};
 | 
			
		||||
use crate::libvirt_rest_structures::{HypervisorInfo, NetworkInfo, VMInfo};
 | 
			
		||||
use actix::Addr;
 | 
			
		||||
 | 
			
		||||
@@ -165,4 +167,19 @@ impl LibVirtClient {
 | 
			
		||||
    pub async fn stop_network(&self, id: XMLUuid) -> anyhow::Result<()> {
 | 
			
		||||
        self.0.send(libvirt_actor::StopNetwork(id)).await?
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the full list of network filters
 | 
			
		||||
    pub async fn get_full_network_filters_list(&self) -> anyhow::Result<Vec<NetworkFilterXML>> {
 | 
			
		||||
        let ids = self.0.send(libvirt_actor::GetNWFiltersListReq).await??;
 | 
			
		||||
        let mut info = Vec::with_capacity(ids.len());
 | 
			
		||||
        for id in ids {
 | 
			
		||||
            info.push(self.get_single_network_filter(id).await?)
 | 
			
		||||
        }
 | 
			
		||||
        Ok(info)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the information about a single domain
 | 
			
		||||
    pub async fn get_single_network_filter(&self, id: XMLUuid) -> anyhow::Result<NetworkFilterXML> {
 | 
			
		||||
        self.0.send(libvirt_actor::GetNWFilterXMLReq(id)).await?
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
use std::fmt::Display;
 | 
			
		||||
use std::net::{IpAddr, Ipv4Addr};
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
 | 
			
		||||
@@ -548,3 +549,316 @@ impl NetworkXML {
 | 
			
		||||
        Ok(network_xml)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "filterref")]
 | 
			
		||||
pub struct NetworkFilterRefXML {
 | 
			
		||||
    #[serde(rename(serialize = "@filter"))]
 | 
			
		||||
    pub filter: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "all")]
 | 
			
		||||
pub struct NetworkFilterRuleProtocolAll {}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "mac")]
 | 
			
		||||
pub struct NetworkFilterRuleProtocolMac {
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcmacaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcmacaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcmacmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcmacmask: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstmacaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstmacaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstmacmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstmacmask: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@comment"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    comment: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "arp")]
 | 
			
		||||
pub struct NetworkFilterRuleProtocolArp {
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcmacaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcmacaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcmacmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcmacmask: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstmacaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstmacaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstmacmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstmacmask: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@arpsrcipaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    arpsrcipaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@arpsrcipmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    arpsrcipmask: Option<u8>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@arpdstipaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    arpdstipaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@arpdstipmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    arpdstipmask: Option<u8>,
 | 
			
		||||
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@comment"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    comment: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "ipvx")]
 | 
			
		||||
pub struct NetworkFilterRuleProtocolIpvx {
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcmacaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcmacaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcmacmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcmacmask: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstmacaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstmacaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstmacmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstmacmask: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcipaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcipaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcipmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcipmask: Option<u8>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstipaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstipaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstipmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstipmask: Option<u8>,
 | 
			
		||||
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@comment"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    comment: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "layer4")]
 | 
			
		||||
pub struct NetworkFilterRuleProtocolLayer4 {
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcmacaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcmacaddr: Option<String>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcipaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcipaddr: Option<IpAddr>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcipmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcipmask: Option<u8>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstipaddr"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstipaddr: Option<IpAddr>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstipmask"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstipmask: Option<u8>,
 | 
			
		||||
    /// Start of range of source IP address
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcipfrom"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcipfrom: Option<IpAddr>,
 | 
			
		||||
    /// End of range of source IP address
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcipto"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcipto: Option<IpAddr>,
 | 
			
		||||
    /// Start of range of destination IP address
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstipfrom"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstipfrom: Option<IpAddr>,
 | 
			
		||||
    /// End of range of destination IP address
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstipto"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstipto: Option<IpAddr>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcportstart"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcportstart: Option<u16>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@srcportend"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    srcportend: Option<u16>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstportstart"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstportstart: Option<u16>,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@dstportend"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    dstportend: Option<u16>,
 | 
			
		||||
    #[serde(rename(serialize = "@state"), skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    state: Option<String>,
 | 
			
		||||
 | 
			
		||||
    #[serde(
 | 
			
		||||
        rename(serialize = "@comment"),
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    comment: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "rule")]
 | 
			
		||||
pub struct NetworkFilterRuleXML {
 | 
			
		||||
    #[serde(rename(serialize = "@action"))]
 | 
			
		||||
    pub action: String,
 | 
			
		||||
    #[serde(rename(serialize = "@direction"))]
 | 
			
		||||
    pub direction: String,
 | 
			
		||||
    #[serde(rename(serialize = "@priority"))]
 | 
			
		||||
    pub priority: Option<i32>,
 | 
			
		||||
 | 
			
		||||
    /// Match all protocols
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub all: Option<NetworkFilterRuleProtocolAll>,
 | 
			
		||||
 | 
			
		||||
    /// Match mac protocol
 | 
			
		||||
    #[serde(default, rename = "mac", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub mac_rules: Vec<NetworkFilterRuleProtocolMac>,
 | 
			
		||||
 | 
			
		||||
    /// Match arp protocol
 | 
			
		||||
    #[serde(default, rename = "arp", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub arp_rules: Vec<NetworkFilterRuleProtocolArp>,
 | 
			
		||||
 | 
			
		||||
    /// Match IPv4 protocol
 | 
			
		||||
    #[serde(default, rename = "ip", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub ipv4_rules: Vec<NetworkFilterRuleProtocolIpvx>,
 | 
			
		||||
 | 
			
		||||
    /// Match IPv6 protocol
 | 
			
		||||
    #[serde(default, rename = "ipv6", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub ipv6_rules: Vec<NetworkFilterRuleProtocolIpvx>,
 | 
			
		||||
 | 
			
		||||
    /// Match TCP protocol
 | 
			
		||||
    #[serde(default, rename = "tcp", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub tcp_rules: Vec<NetworkFilterRuleProtocolLayer4>,
 | 
			
		||||
 | 
			
		||||
    /// Match UDP protocol
 | 
			
		||||
    #[serde(default, rename = "udp", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub udp_rules: Vec<NetworkFilterRuleProtocolLayer4>,
 | 
			
		||||
 | 
			
		||||
    /// Match SCTP protocol
 | 
			
		||||
    #[serde(default, rename = "sctp", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub sctp_rules: Vec<NetworkFilterRuleProtocolLayer4>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
 | 
			
		||||
#[serde(rename = "filter")]
 | 
			
		||||
pub struct NetworkFilterXML {
 | 
			
		||||
    #[serde(rename(serialize = "@name"))]
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    #[serde(rename(serialize = "@chain"), default)]
 | 
			
		||||
    pub chain: String,
 | 
			
		||||
    #[serde(
 | 
			
		||||
        skip_serializing_if = "Option::is_none",
 | 
			
		||||
        rename(serialize = "@priority"),
 | 
			
		||||
        default
 | 
			
		||||
    )]
 | 
			
		||||
    pub priority: Option<i32>,
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub uuid: Option<XMLUuid>,
 | 
			
		||||
    #[serde(default, rename = "filterref", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub filterrefs: Vec<NetworkFilterRefXML>,
 | 
			
		||||
    #[serde(default, rename = "rule", skip_serializing_if = "Vec::is_empty")]
 | 
			
		||||
    pub rules: Vec<NetworkFilterRuleXML>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NetworkFilterXML {
 | 
			
		||||
    pub fn parse_xml<D: Display>(xml: D) -> anyhow::Result<Self> {
 | 
			
		||||
        let xml = xml.to_string();
 | 
			
		||||
 | 
			
		||||
        // We need to put all filter refs at the same location
 | 
			
		||||
        let mut filter_refs = Vec::new();
 | 
			
		||||
        let xml = lazy_regex::regex_replace_all!(r#"<filterref.*/>"#, &xml, |r: &str| {
 | 
			
		||||
            filter_refs.push(r.to_string());
 | 
			
		||||
 | 
			
		||||
            if r.contains('\n') {
 | 
			
		||||
                log::warn!("A filterref contain a new line. This is a symptom of a new unsupported child attribute of <filterref /> object!");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ""
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let filter_refs = filter_refs.join("\n");
 | 
			
		||||
        let xml = xml.replace("</filter>", &format!("{filter_refs}</filter>"));
 | 
			
		||||
        log::debug!("Effective NW filter rule parsed: {xml}");
 | 
			
		||||
 | 
			
		||||
        Ok(serde_xml_rs::from_str(&xml)?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -700,6 +700,144 @@ impl NetworkInfo {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum NetworkFilterChain {
 | 
			
		||||
    Root,
 | 
			
		||||
    Mac,
 | 
			
		||||
    STP,
 | 
			
		||||
    VLAN,
 | 
			
		||||
    ARP,
 | 
			
		||||
    RARP,
 | 
			
		||||
    IPv4,
 | 
			
		||||
    IPv6,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Network filter definition
 | 
			
		||||
pub struct NetworkFilter {
 | 
			
		||||
    name: String,
 | 
			
		||||
    chain: Option<NetworkFilterChain>,
 | 
			
		||||
    priority: Option<i32>,
 | 
			
		||||
    uuid: Option<XMLUuid>,
 | 
			
		||||
    /// Referenced filters rules
 | 
			
		||||
    join_rules: Vec<String>,
 | 
			
		||||
    rules: Vec<NetworkFilterRule>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum NetworkFilterDirection {
 | 
			
		||||
    In,
 | 
			
		||||
    Out,
 | 
			
		||||
    InOut,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum Layer4State {
 | 
			
		||||
    NEW,
 | 
			
		||||
    ESTABLISHED,
 | 
			
		||||
    RELATED,
 | 
			
		||||
    INVALID,
 | 
			
		||||
    NONE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum Layer4Type {
 | 
			
		||||
    TCP,
 | 
			
		||||
    UDP,
 | 
			
		||||
    SCTP,
 | 
			
		||||
    ICMP,
 | 
			
		||||
    TCPipv6,
 | 
			
		||||
    UDPipv6,
 | 
			
		||||
    SCTPipv6,
 | 
			
		||||
    ICMPipv6,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum NetworkFilterSelector {
 | 
			
		||||
    All,
 | 
			
		||||
    Mac {
 | 
			
		||||
        src_mac_addr: Option<String>,
 | 
			
		||||
        src_mac_mask: Option<String>,
 | 
			
		||||
        dst_mac_addr: Option<String>,
 | 
			
		||||
        dst_mac_mask: Option<String>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
    Arp {
 | 
			
		||||
        srcmacaddr: Option<String>,
 | 
			
		||||
        srcmacmask: Option<String>,
 | 
			
		||||
        dstmacaddr: Option<String>,
 | 
			
		||||
        dstmacmask: Option<String>,
 | 
			
		||||
        arpsrcipaddr: Option<IpAddr>,
 | 
			
		||||
        arpsrcipmask: Option<u8>,
 | 
			
		||||
        arpdstipaddr: Option<IpAddr>,
 | 
			
		||||
        arpdstipmask: Option<u8>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
    IPv4 {
 | 
			
		||||
        srcmacaddr: Option<String>,
 | 
			
		||||
        srcmacmask: Option<String>,
 | 
			
		||||
        dstmacaddr: Option<String>,
 | 
			
		||||
        dstmacmask: Option<String>,
 | 
			
		||||
        srcipaddr: Option<Ipv4Addr>,
 | 
			
		||||
        srcipmask: Option<u8>,
 | 
			
		||||
        dstipaddr: Option<Ipv4Addr>,
 | 
			
		||||
        dstipmask: Option<u8>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
    IPv6 {
 | 
			
		||||
        srcmacaddr: Option<String>,
 | 
			
		||||
        srcmacmask: Option<String>,
 | 
			
		||||
        dstmacaddr: Option<String>,
 | 
			
		||||
        dstmacmask: Option<String>,
 | 
			
		||||
        srcipaddr: Option<Ipv6Addr>,
 | 
			
		||||
        srcipmask: Option<u8>,
 | 
			
		||||
        dstipaddr: Option<Ipv6Addr>,
 | 
			
		||||
        dstipmask: Option<u8>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
    Layer4 {
 | 
			
		||||
        r#type: Layer4Type,
 | 
			
		||||
        srcmacaddr: Option<String>,
 | 
			
		||||
        srcipaddr: Option<IpAddr>,
 | 
			
		||||
        srcipmask: Option<u8>,
 | 
			
		||||
        dstipaddr: Option<IpAddr>,
 | 
			
		||||
        dstipmask: Option<u8>,
 | 
			
		||||
        /// Start of range of source IP address
 | 
			
		||||
        srcipfrom: Option<IpAddr>,
 | 
			
		||||
        /// End of range of source IP address
 | 
			
		||||
        srcipto: Option<IpAddr>,
 | 
			
		||||
        /// Start of range of destination IP address
 | 
			
		||||
        dstipfrom: Option<IpAddr>,
 | 
			
		||||
        /// End of range of destination IP address
 | 
			
		||||
        dstipto: Option<IpAddr>,
 | 
			
		||||
        srcportstart: Option<u16>,
 | 
			
		||||
        srcportend: Option<u16>,
 | 
			
		||||
        dstportstart: Option<u16>,
 | 
			
		||||
        dstportend: Option<u16>,
 | 
			
		||||
        state: Option<Layer4State>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn extract_ipv4(ip: IpAddr) -> Ipv4Addr {
 | 
			
		||||
    match ip {
 | 
			
		||||
        IpAddr::V4(i) => i,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@ use virtweb_backend::constants::{
 | 
			
		||||
    MAX_INACTIVITY_DURATION, MAX_SESSION_DURATION, SESSION_COOKIE_NAME,
 | 
			
		||||
};
 | 
			
		||||
use virtweb_backend::controllers::{
 | 
			
		||||
    auth_controller, iso_controller, network_controller, server_controller, static_controller,
 | 
			
		||||
    vm_controller,
 | 
			
		||||
    auth_controller, iso_controller, network_controller, nwfilter_controller, server_controller,
 | 
			
		||||
    static_controller, vm_controller,
 | 
			
		||||
};
 | 
			
		||||
use virtweb_backend::libvirt_client::LibVirtClient;
 | 
			
		||||
use virtweb_backend::middlewares::auth_middleware::AuthChecker;
 | 
			
		||||
@@ -239,6 +239,15 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
                "/api/network/{uid}/stop",
 | 
			
		||||
                web::get().to(network_controller::stop),
 | 
			
		||||
            )
 | 
			
		||||
            // Network filters controller
 | 
			
		||||
            .route(
 | 
			
		||||
                "/api/nwfilter/list",
 | 
			
		||||
                web::get().to(nwfilter_controller::list),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/api/nwfilter/{uid}",
 | 
			
		||||
                web::get().to(nwfilter_controller::get_single),
 | 
			
		||||
            )
 | 
			
		||||
            // Static assets
 | 
			
		||||
            .route("/", web::get().to(static_controller::root_index))
 | 
			
		||||
            .route(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user