From e0acbf304bf024f2f74803956f1f83b7f2b3fe8b Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Fri, 18 Oct 2024 00:48:57 -0400 Subject: [PATCH] wip: Full opnsense deserializer almost done, works on a slightly cheated config file, next step is to try the real config file --- harmony-rs/Cargo.lock | 3 +- harmony-rs/opnsense-config/Cargo.toml | 7 +- harmony-rs/opnsense-config/src/config.rs | 2 +- .../src/modules/deserializer/interfaces.rs | 110 + .../src/modules/deserializer/mod.rs | 3 + .../opnsense-config/src/modules/dhcp.rs | 4 + harmony-rs/opnsense-config/src/modules/mod.rs | 1 + .../opnsense-config/src/modules/opnsense.rs | 1948 ++++++++++++++++- .../src/tests/data/config-structure.xml | 1421 ++++++++++++ 9 files changed, 3475 insertions(+), 24 deletions(-) create mode 100644 harmony-rs/opnsense-config/src/modules/deserializer/interfaces.rs create mode 100644 harmony-rs/opnsense-config/src/modules/deserializer/mod.rs create mode 100644 harmony-rs/opnsense-config/src/tests/data/config-structure.xml diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index 6fc9ac4..a115db3 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -1240,6 +1240,7 @@ name = "opnsense-config" version = "0.1.0" dependencies = [ "async-trait", + "env_logger", "log", "russh", "russh-keys", @@ -2596,7 +2597,6 @@ dependencies = [ [[package]] name = "yaserde" version = "0.11.1" -source = "git+https://git.nationtech.io/NationTech/yaserde#353558737f3ef73e93164c596ff920d4344f30a3" dependencies = [ "log", "xml-rs", @@ -2605,7 +2605,6 @@ dependencies = [ [[package]] name = "yaserde_derive" version = "0.11.1" -source = "git+https://git.nationtech.io/NationTech/yaserde#353558737f3ef73e93164c596ff920d4344f30a3" dependencies = [ "heck", "log", diff --git a/harmony-rs/opnsense-config/Cargo.toml b/harmony-rs/opnsense-config/Cargo.toml index d79b74e..7f6894d 100644 --- a/harmony-rs/opnsense-config/Cargo.toml +++ b/harmony-rs/opnsense-config/Cargo.toml @@ -6,10 +6,13 @@ edition = "2021" [dependencies] serde = { version = "1.0.123", features = [ "derive" ] } log = { workspace = true } +env_logger = { workspace = true } russh = { workspace = true } russh-keys = { workspace = true } -yaserde = { git = "https://git.nationtech.io/NationTech/yaserde" } -yaserde_derive = { git = "https://git.nationtech.io/NationTech/yaserde" } +#yaserde = { git = "https://git.nationtech.io/NationTech/yaserde" } +#yaserde_derive = { git = "https://git.nationtech.io/NationTech/yaserde" } +yaserde = { path = "../../../../github/yaserde/yaserde" } +yaserde_derive = { path = "../../../../github/yaserde/yaserde_derive" } xml-rs = "0.8" thiserror = "1.0" async-trait = { workspace = true } diff --git a/harmony-rs/opnsense-config/src/config.rs b/harmony-rs/opnsense-config/src/config.rs index 41f2ee3..6d26b65 100644 --- a/harmony-rs/opnsense-config/src/config.rs +++ b/harmony-rs/opnsense-config/src/config.rs @@ -176,7 +176,7 @@ mod tests { #[tokio::test] async fn test_load_config_from_local_file() { let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - test_file_path.push("src/tests/data/config-full-1.xml"); + test_file_path.push("src/tests/data/config-structure.xml"); let config_file_path = test_file_path.to_str().unwrap().to_string(); println!("File path {config_file_path}"); diff --git a/harmony-rs/opnsense-config/src/modules/deserializer/interfaces.rs b/harmony-rs/opnsense-config/src/modules/deserializer/interfaces.rs new file mode 100644 index 0000000..ee20f80 --- /dev/null +++ b/harmony-rs/opnsense-config/src/modules/deserializer/interfaces.rs @@ -0,0 +1,110 @@ +use xml::reader::XmlEvent; +use yaserde::{YaDeserialize, YaSerialize}; + +use crate::modules::opnsense::{Interface, Interfaces}; + +impl YaSerialize for Interfaces { + fn serialize( + &self, + writer: &mut yaserde::ser::Serializer, + ) -> Result<(), String> { + todo!() + } + + fn serialize_attributes( + &self, + attributes: Vec, + namespace: xml::namespace::Namespace, + ) -> Result< + ( + Vec, + xml::namespace::Namespace, + ), + String, + > { + todo!() + } +} + +impl YaDeserialize for Interfaces { + fn deserialize( + reader: &mut yaserde::de::Deserializer, + ) -> Result { + let mut interfaces = Interfaces::default(); + let mut current_interface_name = String::new(); + let mut current_interface = Interface::default(); + + let mut event = reader.next_event()?; + loop { + match event { + xml::reader::XmlEvent::EndElement { name } => { + println!( + "Handling EndElement {}, current_interface_name: {}", + name.local_name, current_interface_name + ); + println!("Peeking after EndElement {:?}", reader.peek()?); + if name.local_name == "interfaces" { + break; + } + todo!("Should not hit here"); + } + xml::reader::XmlEvent::StartElement { ref name, .. } => { + println!("Got start Element {:?}", name); + current_interface_name = name.local_name.clone(); + println!("About to deserialize interface from name {:?}", name); + // TODO store names + 'inner_iface: loop { + let peek = reader.peek()?; + println!("Peeking before forcing next event {:?}", peek); + + if let XmlEvent::EndElement { name } = peek { + // TODO save the interface name and struct in the hashmap + println!("Forcing next_event"); + reader.next_event()?; + + let peek = reader.peek()?; + println!("Peeking after next event deserializing {:?}", peek); + if let XmlEvent::EndElement { name } = peek { + println!("Got two EndElement in a row, breaking out of loop"); + break 'inner_iface; + } + } + println!("Peeking before deserializing {:?}", reader.peek()?); + let interface = ::deserialize(reader)?; + println!("Interface deserialized {:?}", interface); + println!("Peeking after deserializing {:?}", reader.peek()?); + } + println!( + "Done with inner interface, loop completed {:?}", + reader.peek()? + ); + } + xml::reader::XmlEvent::EndDocument => break, + _ => { + return Err( + "This Interfaces Deserializer does not support all XmlEvent types".into(), + ) + } + } + let peek = reader.peek()?; + + let mut read_next = true; + if let XmlEvent::EndElement { name } = peek { + if name.local_name == "interfaces" { + println!("Got endElement interfaces, not reading next event"); + break; + } + } + + if read_next { + event = reader.next_event()?; + } + println!("Outer loop got event {:?}", event); + println!("Outer loop got peek {:?}", reader.peek()?); + } + println!("Done with interfaces {:?}", interfaces); + println!("reader peeking shows {:?}", reader.peek()); + + Ok(interfaces) + } +} diff --git a/harmony-rs/opnsense-config/src/modules/deserializer/mod.rs b/harmony-rs/opnsense-config/src/modules/deserializer/mod.rs new file mode 100644 index 0000000..e358e80 --- /dev/null +++ b/harmony-rs/opnsense-config/src/modules/deserializer/mod.rs @@ -0,0 +1,3 @@ + +mod interfaces; + diff --git a/harmony-rs/opnsense-config/src/modules/dhcp.rs b/harmony-rs/opnsense-config/src/modules/dhcp.rs index 2bb7362..8d7172a 100644 --- a/harmony-rs/opnsense-config/src/modules/dhcp.rs +++ b/harmony-rs/opnsense-config/src/modules/dhcp.rs @@ -14,6 +14,10 @@ impl<'a> DhcpConfig<'a> { mac, ipaddr, hostname, + descr: Some("Automatically generated".into()), + winsserver: None, + dnsserver: None, + ntpserver: None, }; self.opnsense.dhcpd.lan.staticmaps.push(static_map); } diff --git a/harmony-rs/opnsense-config/src/modules/mod.rs b/harmony-rs/opnsense-config/src/modules/mod.rs index d847dcb..0518c7f 100644 --- a/harmony-rs/opnsense-config/src/modules/mod.rs +++ b/harmony-rs/opnsense-config/src/modules/mod.rs @@ -1,2 +1,3 @@ pub mod opnsense; pub mod dhcp; +mod deserializer; diff --git a/harmony-rs/opnsense-config/src/modules/opnsense.rs b/harmony-rs/opnsense-config/src/modules/opnsense.rs index 2ca04c0..d7c2730 100644 --- a/harmony-rs/opnsense-config/src/modules/opnsense.rs +++ b/harmony-rs/opnsense-config/src/modules/opnsense.rs @@ -3,26 +3,142 @@ use yaserde_derive::{YaDeserialize, YaSerialize}; #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] #[yaserde(rename = "opnsense")] pub struct OPNsense { - #[yaserde(rename = "dhcpd")] pub dhcpd: Dhcpd, - // Add other top-level elements as needed + pub theme: String, + pub sysctl: Sysctl, + pub system: System, + pub interfaces: Interfaces, + pub snmpd: Snmpd, + pub syslog: Syslog, + pub nat: Nat, + pub filter: Filters, + pub load_balancer: LoadBalancer, + pub ntpd: Ntpd, + pub widgets: Widgets, + pub revision: Revision, + #[yaserde(rename = "OPNsense")] + pub opnsense: OPNsenseConfig, + pub staticroutes: StaticRoutes, + pub ca: Option, + pub gateways: Gateways, + pub cert: Cert, + pub dhcpdv6: DhcpDv6, + pub virtualip: VirtualIp, + pub openvpn: OpenVpn, + pub ppps: Ppps, + pub dyndnses: Dyndnses, + pub vlans: Vlans, + pub bridges: Bridges, + pub gifs: Gifs, + pub gres: Gres, + pub laggs: Laggs, + pub wireless: Wireless, + pub hasync: Hasync, + pub ifgroups: Ifgroups, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct LoadBalancer { + pub monitor_type: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct MonitorType { + pub name: String, + #[yaserde(rename = "type")] + pub r#type: String, + pub descr: String, + pub options: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Ntpd { + pub prefer: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Widgets { + pub sequence: String, + pub column_count: i32, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Revision { + pub username: String, + pub time: f64, + pub description: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Options { + pub path: Option, + pub host: Option, + pub code: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Filters { + #[yaserde(rename = "rule")] + pub rules: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Rule { + #[yaserde(attribute)] + pub uuid: Option, + #[yaserde(rename = "associated-rule-id")] + pub associated_rule_id: Option, + #[yaserde(rename = "type")] + pub r#type: Option, + pub interface: String, + pub ipprotocol: String, + pub statetype: String, + pub descr: String, + pub direction: Option, + pub category: Option, + pub quick: Option, + pub protocol: String, + pub source: Source, + pub destination: Destination, + pub updated: Option, + pub created: Created, + pub disabled: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Source { + pub any: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Destination { + pub network: Option, + pub address: Option, + pub port: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Username { + pub user: String, + pub host: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Sysctl { + pub item: SysctlItem, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct SysctlItem { + pub descr: String, + pub tunable: String, + pub value: String, } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct Dhcpd { #[yaserde(rename = "lan")] pub lan: DhcpInterface, - // Add other interfaces as needed -} - -#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] -pub struct DhcpInterface { - #[yaserde(rename = "enable")] - pub enable: bool, - #[yaserde(rename = "range")] - pub range: DhcpRange, - #[yaserde(rename = "staticmap")] - pub staticmaps: Vec, } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] @@ -34,11 +150,1805 @@ pub struct DhcpRange { } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] -pub struct StaticMap { - #[yaserde(rename = "mac")] - pub mac: String, - #[yaserde(rename = "ipaddr")] - pub ipaddr: String, - #[yaserde(rename = "hostname")] +pub struct System { + #[yaserde(rename = "use_mfs_tmp")] + pub use_mfs_tmp: Option, + #[yaserde(rename = "use_mfs_var")] + pub use_mfs_var: Option, + pub serialspeed: u32, + pub primaryconsole: String, + pub secondaryconsole: String, + pub optimization: String, pub hostname: String, + pub domain: String, + pub group: Vec, + pub user: Vec, + pub nextuid: u32, + pub nextgid: u32, + pub timezone: String, + pub timeservers: String, + pub webgui: WebGui, + pub usevirtualterminal: bool, + pub disableconsolemenu: bool, + pub disablevlanhwfilter: bool, + pub disablechecksumoffloading: bool, + pub disablesegmentationoffloading: bool, + pub disablelargereceiveoffloading: bool, + pub ipv6allow: bool, + pub powerd_ac_mode: String, + pub powerd_battery_mode: String, + pub powerd_normal_mode: String, + pub bogons: Bogons, + pub crypto_hardware: String, + pub pf_share_forward: bool, + pub lb_use_sticky: bool, + pub kill_states: bool, + pub ssh: Ssh, + pub firmware: Firmware, + pub sudo_allow_wheel: bool, + pub sudo_allow_group: String, + pub enablenatreflectionhelper: String, + pub rulesetoptimization: String, + pub maximumstates: Option, + pub maximumfrags: Option, + pub aliasesresolveinterval: Option, + pub maximumtableentries: Option, + pub language: String, + pub dnsserver: Option, + #[yaserde(rename = "dns1gw")] + pub dns1gw: String, + #[yaserde(rename = "dns2gw")] + pub dns2gw: String, + #[yaserde(rename = "dns3gw")] + pub dns3gw: String, + #[yaserde(rename = "dns4gw")] + pub dns4gw: String, + #[yaserde(rename = "dns5gw")] + pub dns5gw: String, + #[yaserde(rename = "dns6gw")] + pub dns6gw: String, + #[yaserde(rename = "dns7gw")] + pub dns7gw: String, + #[yaserde(rename = "dns8gw")] + pub dns8gw: String, + pub dnsallowoverride: bool, + pub dnsallowoverride_exclude: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Ssh { + pub group: String, + pub noauto: bool, + pub interfaces: Option, + pub kex: Option, + pub ciphers: Option, + pub macs: Option, + pub keys: Option, + pub enabled: String, + pub passwordauth: bool, + pub keysig: Option, + pub permitrootlogin: bool, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Firmware { + #[yaserde(attribute)] + pub version: String, + pub mirror: Option, + pub flavour: Option, + pub plugins: String, + #[yaserde(rename = "type")] + pub firmware_type: Option, + pub subscription: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Bogons { + pub interval: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Group { + pub name: String, + pub description: String, + pub scope: String, + pub gid: u32, + pub member: Vec, + #[yaserde(rename = "priv")] + pub priv_: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct User { + pub name: String, + pub descr: Option, + pub scope: String, + pub groupname: Option, + pub password: String, + pub uid: u32, + pub expires: Option, + pub authorizedkeys: Option, + pub ipsecpsk: Option, + pub otp_seed: Option, + pub shell: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct WebGui { + pub protocol: String, + #[yaserde(rename = "ssl-certref")] + pub ssl_certref: String, + pub port: Option, + #[yaserde(rename = "ssl-ciphers")] + pub ssl_ciphers: Option, + pub interfaces: Option, + + pub compression: Option, +} + +use std::collections::HashMap; + +#[derive(Default, PartialEq, Debug)] +pub struct Interfaces { + pub interfaces: HashMap, +} + +#[derive(Default, PartialEq, Debug, YaDeserialize, YaSerialize)] +pub struct Interface { + #[yaserde(rename = "if")] + pub physical_interface_name: String, + pub descr: String, + pub enable: u8, + #[yaserde(rename = "spoofmac")] + pub spoof_mac: Option, + pub internal_dynamic: Option, + pub ipaddr: Option, + #[yaserde(rename = "blockpriv")] + pub block_priv: Option, + #[yaserde(rename = "blockbogons")] + pub block_bogons: Option, + pub lock: Option, + #[yaserde(rename = "type")] + pub r#type: Option, + #[yaserde(rename = "virtual")] + pub r#virtual: Option, + pub subnet: Option, + pub networks: Option, + pub subnetv6: Option, + pub ipaddrv6: Option, + #[yaserde(rename = "track6-interface")] + pub track6_interface: Option, + #[yaserde(rename = "track6-prefix-id")] + pub track6_prefix_id: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct DhcpInterface { + pub enable: i32, + pub gateway: String, + pub pool: Option, + pub domain: String, + #[yaserde(rename = "ddnsdomainalgorithm")] + pub ddns_domain_algorithm: String, + #[yaserde(rename = "numberoptions")] + pub number_options: Vec, + #[yaserde(rename = "range")] + pub range: Range, + pub winsserver: Option, + pub dnsserver: String, + pub ntpserver: Option, + #[yaserde(rename = "staticmap")] + pub staticmaps: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct NumberOption { + item: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Range { + #[yaserde(rename = "from")] + pub from: String, + #[yaserde(rename = "to")] + pub to: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct StaticMap { + pub mac: String, + pub ipaddr: String, + pub hostname: String, + pub descr: Option, + pub winsserver: Option, + pub dnsserver: Option, + pub ntpserver: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Snmpd { + pub syslocation: Option, + pub syscontact: Option, + pub rocommunity: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Syslog { + pub reverse: Option, + pub preservelogs: i32, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Nat { + #[yaserde(rename = "outbound")] + pub outbound: Outbound, + #[yaserde(rename = "rule")] + pub rules: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Outbound { + pub mode: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct NatRule { + pub protocol: String, + pub interface: String, + pub category: Option, + pub ipprotocol: String, + pub descr: Option, + pub tag: Option, + pub tagged: Option, + pub poolopts: PoolOpts, + #[yaserde(rename = "associated-rule-id")] + pub associated_rule_id: String, + pub target: String, + #[yaserde(rename = "local-port")] + pub local_port: i32, + pub source: Source, + pub destination: Destination, + pub updated: Updated, + pub created: Created, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct PoolOpts { + // No specific fields for this element, can be added as needed +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Updated { + pub username: String, + pub time: f64, + pub description: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Created { + pub username: String, + pub time: f64, + pub description: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +#[yaserde(rename = "OPNsense")] +pub struct OPNsenseConfig { + pub captiveportal: Option, + pub cron: Option, + #[yaserde(rename = "Netflow")] + pub netflow: Option, + #[yaserde(rename = "Firewall")] + pub firewall: Option, + #[yaserde(rename = "IDS")] + pub ids: Option, + #[yaserde(rename = "IPsec")] + pub ipsec: Option, + #[yaserde(rename = "Interfaces")] + pub interfaces: Option, + pub monit: Option, + #[yaserde(rename = "OpenVPNExport")] + pub openvpn_export: Option, + pub proxy: Option, + #[yaserde(rename = "Syslog")] + pub syslog: Option, + #[yaserde(rename = "TrafficShaper")] + pub traffic_shaper: Option, + pub unboundplus: Option, + pub wireguard: Option, + #[yaserde(rename = "Swanctl")] + pub swanctl: Swanctl, + #[yaserde(rename = "DynDNS")] + pub dyndns: DynDNS, + #[yaserde(rename = "OpenVPN")] + pub openvpn: ConfigOpenVPN, + #[yaserde(rename = "Gateways")] + pub gateways: ConfigGateways, + #[yaserde(rename = "HAProxy")] + pub haproxy: HAProxy, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +#[yaserde(rename = "IDS")] +struct IDS { + #[yaserde(attribute)] + version: String, + rules: Option, + policies: Option, + #[yaserde(rename = "userDefinedRules")] + user_defined_rules: Option, + files: Option, + #[yaserde(rename = "fileTags")] + file_tags: Option, + general: IDSGeneral, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct IDSGeneral { + enabled: Option, + ips: Option, + promisc: Option, + interfaces: String, + homenet: String, + defaultPacketSize: Option, + UpdateCron: Option, + AlertLogrotate: String, + AlertSaveLogs: u8, + MPMAlgo: String, + detect: Detect, + syslog: Option, + syslog_eve: Option, + LogPayload: Option, + verbosity: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Detect { + Profile: String, + toclient_groups: Option, + toserver_groups: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +#[yaserde(rename = "IPsec")] +pub struct IPsec { + #[yaserde(attribute)] + version: String, + general: GeneralIpsec, + keyPairs: Option, + preSharedKeys: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct GeneralIpsec { + enabled: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +#[yaserde(rename = "Interfaces")] +pub struct ConfigInterfaces { + vxlans: Vxlan, + loopbacks: Loopback, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +struct Vxlan { + #[yaserde(attribute)] + version: String, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +struct Loopback { + #[yaserde(attribute)] + version: String, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +#[yaserde(rename = "monit")] +struct Monit { + #[yaserde(attribute)] + version: String, + general: GeneralMonit, + alert: Option, + service: Option, + test: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +struct GeneralMonit { + enabled: u8, + interval: u32, + startdelay: u32, + mailserver: String, + port: u16, + username: Option, + password: Option, + ssl: u8, + sslversion: String, + sslverify: u8, + logfile: String, + statefile: Option, + eventqueuePath: Option, + eventqueueSlots: Option, + httpdEnabled: u8, + httpdUsername: String, + httpdPassword: String, + httpdPort: u16, + httpdAllow: Option, + mmonitUrl: Option, + mmonitTimeout: u32, + mmonitRegisterCredentials: u8, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +struct Alert { + #[yaserde(attribute)] + uuid: String, + enabled: u8, + recipient: String, + noton: u8, + events: Option, + format: Option, + reminder: u32, + description: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +struct Service { + #[yaserde(attribute)] + uuid: String, + enabled: u8, + name: String, + description: Option, + #[yaserde(rename = "type")] + r#type: String, + pidfile: Option, + #[yaserde(rename = "match")] + r#match: Option, + path: Option, + timeout: u32, + starttimeout: u32, + address: Option, + interface: Option, + start: Option, + stop: Option, + tests: String, + depends: Option, + polltime: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +struct Test { + #[yaserde(attribute)] + uuid: String, + name: String, + #[yaserde(rename = "type")] + r#type: String, + condition: String, + action: String, + path: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct CaptivePortal { + #[yaserde(attribute)] + pub version: String, + #[yaserde(rename = "zones")] + pub zones: Option, + #[yaserde(rename = "templates")] + pub templates: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Cron { + #[yaserde(attribute)] + pub version: String, + #[yaserde(rename = "jobs")] + pub jobs: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Netflow { + #[yaserde(attribute)] + pub version: String, + #[yaserde(rename = "capture")] + pub capture: Option, + #[yaserde(rename = "collect")] + pub collect: Option, + #[yaserde(rename = "activeTimeout")] + pub active_timeout: Option, + #[yaserde(rename = "inactiveTimeout")] + pub inactive_timeout: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Capture { + #[yaserde(rename = "interfaces")] + pub interfaces: Option, + #[yaserde(rename = "egress_only")] + pub egress_only: Option, + #[yaserde(rename = "version")] + pub version: Option, + #[yaserde(rename = "targets")] + pub targets: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Collect { + #[yaserde(rename = "enable")] + pub enable: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Zones { + // Define fields for Zones, e.g.: + #[yaserde(rename = "zone")] + pub zones: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Zone { + #[yaserde(attribute)] + pub uuid: Option, + #[yaserde(rename = "name")] + pub name: Option, + // Add other fields as needed +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Templates { + // Define fields for Templates, e.g.: + #[yaserde(rename = "template")] + pub templates: Vec