diff --git a/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs b/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs index ff5fdea..09bc0c7 100644 --- a/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs +++ b/virtweb_backend/src/libvirt_lib_structures/nwfilter.rs @@ -9,13 +9,6 @@ pub struct NetworkFilterRefXML { pub filter: String, } -#[derive(serde::Serialize, serde::Deserialize, Debug)] -#[serde(rename = "all")] -pub struct NetworkFilterRuleProtocolAllXML { - #[serde(rename = "@comment", skip_serializing_if = "Option::is_none")] - pub comment: Option, -} - #[derive(serde::Serialize, serde::Deserialize, Debug)] #[serde(rename = "mac")] pub struct NetworkFilterRuleProtocolMac { @@ -50,7 +43,6 @@ pub struct NetworkFilterRuleProtocolArpXML { pub arpdstipaddr: Option, #[serde(rename = "@arpdstipmask", skip_serializing_if = "Option::is_none")] pub arpdstipmask: Option, - #[serde(rename = "@comment", skip_serializing_if = "Option::is_none")] pub comment: Option, } @@ -114,7 +106,37 @@ pub struct NetworkFilterRuleProtocolLayer4 { pub dstportend: Option, #[serde(rename = "@state", skip_serializing_if = "Option::is_none")] pub state: Option, + #[serde(rename = "@comment", skip_serializing_if = "Option::is_none")] + pub comment: Option, +} +#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[serde(rename = "all")] +pub struct NetworkFilterRuleProtocolAllXML { + #[serde(rename = "@srcmacaddr", skip_serializing_if = "Option::is_none")] + pub srcmacaddr: Option, + #[serde(rename = "@srcipaddr", skip_serializing_if = "Option::is_none")] + pub srcipaddr: Option, + #[serde(rename = "@srcipmask", skip_serializing_if = "Option::is_none")] + pub srcipmask: Option, + #[serde(rename = "@dstipaddr", skip_serializing_if = "Option::is_none")] + pub dstipaddr: Option, + #[serde(rename = "@dstipmask", skip_serializing_if = "Option::is_none")] + pub dstipmask: Option, + /// Start of range of source IP address + #[serde(rename = "@srcipfrom", skip_serializing_if = "Option::is_none")] + pub srcipfrom: Option, + /// End of range of source IP address + #[serde(rename = "@srcipto", skip_serializing_if = "Option::is_none")] + pub srcipto: Option, + /// Start of range of destination IP address + #[serde(rename = "@dstipfrom", skip_serializing_if = "Option::is_none")] + pub dstipfrom: Option, + /// End of range of destination IP address + #[serde(rename = "@dstipto", skip_serializing_if = "Option::is_none")] + pub dstipto: Option, + #[serde(rename = "@state", skip_serializing_if = "Option::is_none")] + pub state: Option, #[serde(rename = "@comment", skip_serializing_if = "Option::is_none")] pub comment: Option, } @@ -129,14 +151,6 @@ pub struct NetworkFilterRuleXML { #[serde(rename = "@priority")] pub priority: Option, - /// Match all protocols - #[serde(default, rename = "all", skip_serializing_if = "Vec::is_empty")] - pub all_selectors: Vec, - - /// Match all ipv6 protocols - #[serde(default, rename = "all-ipv6", skip_serializing_if = "Vec::is_empty")] - pub all_ipv6_selectors: Vec, - /// Match mac protocol #[serde(default, rename = "mac", skip_serializing_if = "Vec::is_empty")] pub mac_selectors: Vec, @@ -173,6 +187,10 @@ pub struct NetworkFilterRuleXML { #[serde(default, rename = "icmp", skip_serializing_if = "Vec::is_empty")] pub icmp_selectors: Vec>, + /// Match all protocols + #[serde(default, rename = "all", skip_serializing_if = "Vec::is_empty")] + pub all_selectors: Vec>, + /// Match TCP IPv6 protocol #[serde(default, rename = "tcp-ipv6", skip_serializing_if = "Vec::is_empty")] pub tcp_ipv6_selectors: Vec>, @@ -188,6 +206,10 @@ pub struct NetworkFilterRuleXML { /// Match ICMP IPv6 protocol #[serde(default, rename = "icmpv6", skip_serializing_if = "Vec::is_empty")] pub imcp_ipv6_selectors: Vec>, + + /// Match all ipv6 protocols + #[serde(default, rename = "all-ipv6", skip_serializing_if = "Vec::is_empty")] + pub all_ipv6_selectors: Vec>, } #[derive(serde::Serialize, serde::Deserialize, Debug)] diff --git a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs index 755ba0e..48bfa85 100644 --- a/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs +++ b/virtweb_backend/src/libvirt_rest_structures/nw_filter.rs @@ -308,11 +308,6 @@ impl Layer4State { } } -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct NetworkSelectorAll { - comment: Option, -} - #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct NetworkSelectorMac { src_mac_addr: Option, @@ -371,10 +366,28 @@ pub struct NetworkFilterSelectorLayer4 { comment: Option, } +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct NetworkSelectorAll { + comment: Option, + 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, + state: Option, +} + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[serde(tag = "type", rename_all = "lowercase")] pub enum NetworkFilterSelector { - All(NetworkSelectorAll), Mac(NetworkSelectorMac), Arp(NetworkSelectorARP), Rarp(NetworkSelectorARP), @@ -384,11 +397,12 @@ pub enum NetworkFilterSelector { UDP(NetworkFilterSelectorLayer4), SCTP(NetworkFilterSelectorLayer4), ICMP(NetworkFilterSelectorLayer4), - Allipv6(NetworkSelectorAll), + All(NetworkSelectorAll), TCPipv6(NetworkFilterSelectorLayer4), UDPipv6(NetworkFilterSelectorLayer4), SCTPipv6(NetworkFilterSelectorLayer4), ICMPipv6(NetworkFilterSelectorLayer4), + Allipv6(NetworkSelectorAll), } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] @@ -416,12 +430,6 @@ pub struct NetworkFilter { } impl NetworkFilter { - fn lib2rest_process_all_rule(n: &NetworkFilterRuleProtocolAllXML) -> NetworkSelectorAll { - NetworkSelectorAll { - comment: n.comment.clone(), - } - } - fn lib2rest_process_mac_rule(n: &NetworkFilterRuleProtocolMac) -> NetworkFilterSelector { NetworkFilterSelector::Mac(NetworkSelectorMac { src_mac_addr: n.srcmacaddr.as_ref().map(|v| v.into()), @@ -484,28 +492,29 @@ impl NetworkFilter { }) } + fn lib2rest_process_all_rule( + n: &NetworkFilterRuleProtocolAllXML, + ) -> anyhow::Result> { + Ok(NetworkSelectorAll { + srcmacaddr: n.srcmacaddr.as_ref().map(|v| v.into()), + srcipaddr: n.srcipaddr, + srcipmask: n.srcipmask, + dstipaddr: n.dstipaddr, + dstipmask: n.dstipmask, + srcipfrom: n.srcipfrom, + srcipto: n.srcipto, + dstipfrom: n.dstipfrom, + dstipto: n.dstipto, + state: n.state.as_deref().map(Layer4State::from_xml).transpose()?, + comment: n.comment.clone(), + }) + } + pub fn lib2rest(xml: NetworkFilterXML) -> anyhow::Result { let mut rules = Vec::with_capacity(xml.rules.len()); for rule in &xml.rules { let mut selectors = Vec::new(); - // All selectors - selectors.append( - &mut rule - .all_selectors - .iter() - .map(|r| NetworkFilterSelector::All(Self::lib2rest_process_all_rule(r))) - .collect(), - ); - - selectors.append( - &mut rule - .all_ipv6_selectors - .iter() - .map(|r| NetworkFilterSelector::Allipv6(Self::lib2rest_process_all_rule(r))) - .collect(), - ); - // Mac selectors selectors.append( &mut rule @@ -638,6 +647,31 @@ impl NetworkFilter { .collect::, anyhow::Error>>()?, ); + // All selectors + selectors.append( + &mut rule + .all_selectors + .iter() + .map(|r| { + Ok(NetworkFilterSelector::All(Self::lib2rest_process_all_rule( + r, + )?)) + }) + .collect::, anyhow::Error>>()?, + ); + + selectors.append( + &mut rule + .all_ipv6_selectors + .iter() + .map(|r| { + Ok(NetworkFilterSelector::Allipv6( + Self::lib2rest_process_all_rule(r)?, + )) + }) + .collect::, anyhow::Error>>()?, + ); + rules.push(NetworkFilterRule { action: NetworkFilterAction::from_xml(&rule.action)?, direction: NetworkFilterDirection::from_xml(&rule.direction)?, @@ -664,14 +698,6 @@ impl NetworkFilter { }) } - fn rest2lib_process_all_selector( - selector: &NetworkSelectorAll, - ) -> anyhow::Result { - Ok(NetworkFilterRuleProtocolAllXML { - comment: extract_nw_filter_comment(&selector.comment)?, - }) - } - fn rest2lib_process_arp_selector( selector: &NetworkSelectorARP, ) -> anyhow::Result { @@ -728,6 +754,26 @@ impl NetworkFilter { }) } + fn rest2lib_process_all_selector( + selector: &NetworkSelectorAll, + ) -> anyhow::Result> { + Ok(NetworkFilterRuleProtocolAllXML { + 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, + state: selector.state.map(|s| s.to_xml()), + comment: extract_nw_filter_comment(&selector.comment)?, + }) + } + fn rest2lib_process_rule(rule: &NetworkFilterRule) -> anyhow::Result { let mut rule_xml = NetworkFilterRuleXML { action: rule.action.to_xml(), @@ -738,18 +784,6 @@ impl NetworkFilter { for sel in &rule.selectors { match sel { - NetworkFilterSelector::All(all) => { - rule_xml - .all_selectors - .push(Self::rest2lib_process_all_selector(all)?); - } - - NetworkFilterSelector::Allipv6(all) => { - rule_xml - .all_ipv6_selectors - .push(Self::rest2lib_process_all_selector(all)?); - } - NetworkFilterSelector::Mac(mac) => { rule_xml.mac_selectors.push(NetworkFilterRuleProtocolMac { srcmacaddr: extract_mac_address_or_var(&mac.src_mac_addr)?, @@ -765,6 +799,7 @@ impl NetworkFilter { .arp_selectors .push(Self::rest2lib_process_arp_selector(a)?); } + NetworkFilterSelector::Rarp(a) => { rule_xml .rarp_selectors @@ -774,7 +809,6 @@ impl NetworkFilter { NetworkFilterSelector::IPv4(ip) => rule_xml .ipv4_selectors .push(Self::rest2lib_process_ip_selector(ip)?), - NetworkFilterSelector::IPv6(ip) => rule_xml .ipv6_selectors .push(Self::rest2lib_process_ip_selector(ip)?), @@ -795,6 +829,12 @@ impl NetworkFilter { .icmp_selectors .push(Self::rest2lib_process_layer4_selector(icmp)?), + NetworkFilterSelector::All(all) => { + rule_xml + .all_selectors + .push(Self::rest2lib_process_all_selector(all)?); + } + NetworkFilterSelector::TCPipv6(tcpv6) => rule_xml .tcp_ipv6_selectors .push(Self::rest2lib_process_layer4_selector(tcpv6)?), @@ -810,6 +850,12 @@ impl NetworkFilter { NetworkFilterSelector::ICMPipv6(icmpv6) => rule_xml .imcp_ipv6_selectors .push(Self::rest2lib_process_layer4_selector(icmpv6)?), + + NetworkFilterSelector::Allipv6(all) => { + rule_xml + .all_ipv6_selectors + .push(Self::rest2lib_process_all_selector(all)?); + } } } diff --git a/virtweb_frontend/src/api/NWFilterApi.ts b/virtweb_frontend/src/api/NWFilterApi.ts index 4d0385f..220c228 100644 --- a/virtweb_frontend/src/api/NWFilterApi.ts +++ b/virtweb_frontend/src/api/NWFilterApi.ts @@ -6,18 +6,6 @@ export interface NWFilterChain { suffix?: string; } -export interface NWFSAllBase { - comment?: string; -} - -export type NWFSAll = NWFSAllBase & { - type: "all"; -}; - -export type NWFSAllIPv6 = NWFSAllBase & { - type: "allipv6"; -}; - export interface NWFSMac { type: "mac"; src_mac_addr?: string; @@ -97,9 +85,29 @@ export type NFWSUDPv6 = NWFSLayer4Base & { type: "udpipv6" }; export type NFWSSCTPv6 = NWFSLayer4Base & { type: "sctpipv6" }; export type NFWSICMPv6 = NWFSLayer4Base & { type: "icmpipv6" }; +export interface NWFSAllBase { + srcmacaddr?: string; + srcipaddr?: string; + srcipmask?: number; + dstipaddr?: string; + dstipmask?: number; + srcipfrom?: string; + srcipto?: string; + dstipfrom?: string; + dstipto?: string; + state?: Layer4State; + comment?: string; +} + +export type NWFSAll = NWFSAllBase & { + type: "all"; +}; + +export type NWFSAllIPv6 = NWFSAllBase & { + type: "allipv6"; +}; + export type NWFSelector = - | NWFSAll - | NWFSAllIPv6 | NWFSMac | NWFSArp | NWFSRArp @@ -109,10 +117,12 @@ export type NWFSelector = | NFWSUDPv4 | NFWSSCTPv4 | NFWSICMPv4 + | NWFSAll | NFWSTCPv6 | NFWSUDPv6 | NFWSSCTPv6 - | NFWSICMPv6; + | NFWSICMPv6 + | NWFSAllIPv6; export interface NWFilterRule { action: "drop" | "reject" | "accept" | "return" | "continue"; diff --git a/virtweb_frontend/src/widgets/forms/NWFConnStateInput.tsx b/virtweb_frontend/src/widgets/forms/NWFConnStateInput.tsx new file mode 100644 index 0000000..1aa3a39 --- /dev/null +++ b/virtweb_frontend/src/widgets/forms/NWFConnStateInput.tsx @@ -0,0 +1,27 @@ +import { Layer4State } from "../../api/NWFilterApi"; +import { SelectInput } from "./SelectInput"; + +export function NWFConnStateInput(p: { + editable: boolean; + value?: Layer4State; + onChange: (s?: Layer4State) => void; +}): React.ReactElement { + return ( + { + p.onChange?.(s as any); + }} + options={[ + { label: "None", value: undefined }, + { label: "NEW", value: "NEW" }, + { label: "ESTABLISHED", value: "ESTABLISHED" }, + { label: "RELATED", value: "RELATED" }, + { label: "INVALID", value: "INVALID" }, + { label: "NONE", value: "NONE" }, + ]} + /> + ); +} diff --git a/virtweb_frontend/src/widgets/forms/NWFilterRules.tsx b/virtweb_frontend/src/widgets/forms/NWFilterRules.tsx index b68dc2f..8ea8907 100644 --- a/virtweb_frontend/src/widgets/forms/NWFilterRules.tsx +++ b/virtweb_frontend/src/widgets/forms/NWFilterRules.tsx @@ -12,6 +12,7 @@ import { Tooltip, } from "@mui/material"; import { + NWFSAllBase, NWFSArpOrRARP, NWFSIPBase, NWFSLayer4Base, @@ -23,10 +24,11 @@ import { ServerApi } from "../../api/ServerApi"; import { EditSection } from "./EditSection"; import { IPInput, IPInputWithMask } from "./IPInput"; import { MACInput } from "./MACInput"; +import { NWFConnStateInput } from "./NWFConnStateInput"; +import { NWFilterPriorityInput } from "./NWFilterPriorityInput"; import { PortInput } from "./PortInput"; import { SelectInput } from "./SelectInput"; import { TextInput } from "./TextInput"; -import { NWFilterPriorityInput } from "./NWFilterPriorityInput"; export function NWFilterRules(p: { editable: boolean; @@ -216,9 +218,6 @@ function NWFSelectorEdit(p: { }} value={p.selector.type} options={[ - { label: "All over IPv4", value: "all" }, - { label: "All over IPv6", value: "allipv6" }, - { label: "MAC (Ethernet)", value: "mac" }, { label: "ARP", value: "arp" }, @@ -232,10 +231,14 @@ function NWFSelectorEdit(p: { { label: "SCTP over IPv4", value: "sctp" }, { label: "ICMPv4", value: "icmp" }, + { label: "All over IPv4", value: "all" }, + { label: "TCP over IPv6", value: "tcpipv6" }, { label: "UDP over IPv6", value: "udpipv6" }, { label: "SCTP over IPv6", value: "sctpipv6" }, { label: "ICMPv6", value: "icmpipv6" }, + + { label: "All over IPv6", value: "allipv6" }, ]} /> @@ -262,6 +265,10 @@ function NWFSelectorEdit(p: { )} + {p.selector.type === "all" && ( + + )} + {(p.selector.type === "tcpipv6" || p.selector.type === "udpipv6" || p.selector.type === "sctpipv6" || @@ -269,6 +276,10 @@ function NWFSelectorEdit(p: { )} + {p.selector.type === "allipv6" && ( + + )} + - { - p.selector.state = s as any; + onChange={(v) => { + p.selector.state = v; + p.onChange?.(); + }} + /> + + ); +} + +function NWFSelectorAll( + p: SpecificSelectorEditorWithIPVersion +): React.ReactElement { + return ( + <> + { + p.selector.srcmacaddr = v; + p.onChange?.(); + }} + /> + { + p.selector.srcipaddr = ip; + p.selector.srcipmask = mask; + p.onChange?.(); + }} + /> + { + p.selector.dstipaddr = ip; + p.selector.dstipmask = mask; + p.onChange?.(); + }} + /> + { + p.selector.srcipfrom = ip; + p.onChange?.(); + }} + /> + { + p.selector.srcipto = ip; + p.onChange?.(); + }} + /> + { + p.selector.dstipfrom = ip; + p.onChange?.(); + }} + /> + { + p.selector.dstipto = ip; + p.onChange?.(); + }} + /> + { + p.selector.state = v; p.onChange?.(); }} - options={[ - { label: "None", value: undefined }, - { label: "NEW", value: "NEW" }, - { label: "ESTABLISHED", value: "ESTABLISHED" }, - { label: "RELATED", value: "RELATED" }, - { label: "INVALID", value: "INVALID" }, - { label: "NONE", value: "NONE" }, - ]} /> );