* [pve-devel] [PATCH proxmox{-ve-rs, -firewall} v2 0/4] Fix ipfilters in proxmox-firewall
@ 2025-09-25 12:21 Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-ve-rs v2 1/1] config: guest: store network devices in BTreeMap Stefan Hanreich
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Stefan Hanreich @ 2025-09-25 12:21 UTC (permalink / raw)
To: pve-devel
This patch series addresses two issues with ipfilters:
* containers would have the wrong CIDR inserted into the auto-generated ipfilter
ipsets
* The nomatch logic isn't working correctly, due to wrong inversion of logic,
leading to ipfilters not working at all
Including the rustfmt patch here as well, instead of separately since we touch
some of the imports that get changed there - leading to conflicts on applying
otherwise.
Changes from v1:
* properly regenerate test-output with the proxmox-ve-rs patch applied
* improve documentation of handle_set and handle_ipfilter
proxmox-ve-rs:
Stefan Hanreich (1):
config: guest: store network devices in BTreeMap
proxmox-ve-config/src/guest/vm.rs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
proxmox-firewall:
Stefan Hanreich (3):
run rustfmt
ipfilter: fix wrong entries for containers
fix #6336: fix ipfilter matching logic
proxmox-firewall/src/config.rs | 6 +-
proxmox-firewall/src/firewall.rs | 16 +-
proxmox-firewall/src/object.rs | 6 +-
proxmox-firewall/src/rule.rs | 161 +++++--
proxmox-firewall/tests/input/100.conf | 1 +
.../integration_tests__firewall.snap | 416 ++++++++++++++++++
6 files changed, 565 insertions(+), 41 deletions(-)
Summary over all repositories:
7 files changed, 569 insertions(+), 45 deletions(-)
--
Generated by git-murpp 0.8.0
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pve-devel] [PATCH proxmox-ve-rs v2 1/1] config: guest: store network devices in BTreeMap
2025-09-25 12:21 [pve-devel] [PATCH proxmox{-ve-rs, -firewall} v2 0/4] Fix ipfilters in proxmox-firewall Stefan Hanreich
@ 2025-09-25 12:21 ` Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 1/3] run rustfmt Stefan Hanreich
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Stefan Hanreich @ 2025-09-25 12:21 UTC (permalink / raw)
To: pve-devel
Enables iterating over the network devices in order, without
performing any additional sorting at the call sites. This makes the
output in proxmox-firewall stable, which is useful for test cases as
well as for comparing the output of different proxmox-firewall runs.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
proxmox-ve-config/src/guest/vm.rs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/proxmox-ve-config/src/guest/vm.rs b/proxmox-ve-config/src/guest/vm.rs
index cc97781..baff1b8 100644
--- a/proxmox-ve-config/src/guest/vm.rs
+++ b/proxmox-ve-config/src/guest/vm.rs
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use std::collections::BTreeMap;
use std::io;
use std::str::FromStr;
@@ -278,7 +278,7 @@ impl FromStr for NetworkDevice {
#[derive(Debug, Default)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct NetworkConfig {
- network_devices: HashMap<i64, NetworkDevice>,
+ network_devices: BTreeMap<i64, NetworkDevice>,
}
impl NetworkConfig {
@@ -300,12 +300,12 @@ impl NetworkConfig {
bail!("No index found in net key string: {key}")
}
- pub fn network_devices(&self) -> &HashMap<i64, NetworkDevice> {
+ pub fn network_devices(&self) -> &BTreeMap<i64, NetworkDevice> {
&self.network_devices
}
pub fn parse<R: io::BufRead>(input: R) -> Result<Self, Error> {
- let mut network_devices = HashMap::new();
+ let mut network_devices = BTreeMap::new();
for line in input.lines() {
let line = line?;
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pve-devel] [PATCH proxmox-firewall v2 1/3] run rustfmt
2025-09-25 12:21 [pve-devel] [PATCH proxmox{-ve-rs, -firewall} v2 0/4] Fix ipfilters in proxmox-firewall Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-ve-rs v2 1/1] config: guest: store network devices in BTreeMap Stefan Hanreich
@ 2025-09-25 12:21 ` Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 2/3] ipfilter: fix wrong entries for containers Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 3/3] fix #6336: fix ipfilter matching logic Stefan Hanreich
3 siblings, 0 replies; 5+ messages in thread
From: Stefan Hanreich @ 2025-09-25 12:21 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
proxmox-firewall/src/config.rs | 6 +++---
proxmox-firewall/src/firewall.rs | 6 +++---
proxmox-firewall/src/object.rs | 6 +++---
proxmox-firewall/src/rule.rs | 30 ++++++++++++++----------------
4 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/proxmox-firewall/src/config.rs b/proxmox-firewall/src/config.rs
index d6a4df5..8bd9f2a 100644
--- a/proxmox-firewall/src/config.rs
+++ b/proxmox-firewall/src/config.rs
@@ -3,7 +3,7 @@ use std::default::Default;
use std::fs::{self, DirEntry, File, ReadDir};
use std::io::{self, BufReader};
-use anyhow::{bail, format_err, Context, Error};
+use anyhow::{Context, Error, bail, format_err};
use proxmox_log as log;
@@ -17,10 +17,10 @@ use proxmox_ve_config::guest::types::Vmid;
use proxmox_ve_config::guest::{GuestEntry, GuestMap};
use proxmox_ve_config::host::types::BridgeName;
-use proxmox_network_api::{get_network_interfaces, AltnameMapping};
+use proxmox_network_api::{AltnameMapping, get_network_interfaces};
+use proxmox_nftables::NftClient;
use proxmox_nftables::command::{CommandOutput, Commands, List, ListOutput};
use proxmox_nftables::types::ListChain;
-use proxmox_nftables::NftClient;
use proxmox_ve_config::sdn::{
config::{RunningConfig, SdnConfig},
ipam::{Ipam, IpamJson},
diff --git a/proxmox-firewall/src/firewall.rs b/proxmox-firewall/src/firewall.rs
index 8cac190..5012610 100644
--- a/proxmox-firewall/src/firewall.rs
+++ b/proxmox-firewall/src/firewall.rs
@@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use std::fs;
-use anyhow::{Error, bail};
+use anyhow::{bail, Error};
use proxmox_log as log;
@@ -25,17 +25,17 @@ use proxmox_ve_config::firewall::guest::Config as GuestConfig;
use proxmox_ve_config::firewall::host::Config as HostConfig;
use proxmox_network_types::ip_address::{Cidr, Ipv6Cidr};
-use proxmox_ve_config::firewall::types::Group;
use proxmox_ve_config::firewall::types::ipset::{
Ipfilter, Ipset, IpsetEntry, IpsetName, IpsetScope,
};
use proxmox_ve_config::firewall::types::log::{LogLevel as ConfigLogLevel, LogRateLimit};
use proxmox_ve_config::firewall::types::rule::{Direction, Verdict as ConfigVerdict};
+use proxmox_ve_config::firewall::types::Group;
use proxmox_ve_config::guest::types::Vmid;
use crate::config::FirewallConfig;
use crate::object::{NftObjectEnv, ToNftObjects};
-use crate::rule::{NftRule, NftRuleEnv, generate_verdict};
+use crate::rule::{generate_verdict, NftRule, NftRuleEnv};
static CLUSTER_TABLE_NAME: &str = "proxmox-firewall";
static HOST_TABLE_NAME: &str = "proxmox-firewall";
diff --git a/proxmox-firewall/src/object.rs b/proxmox-firewall/src/object.rs
index a7575bb..42423b9 100644
--- a/proxmox-firewall/src/object.rs
+++ b/proxmox-firewall/src/object.rs
@@ -1,19 +1,19 @@
-use anyhow::{Error, format_err};
+use anyhow::{format_err, Error};
use proxmox_log as log;
use proxmox_nftables::{
- Command, Expression,
command::{Add, Flush},
expression::Prefix,
types::{
AddCtHelper, AddElement, CtHelperProtocol, ElementType, L3Protocol, SetConfig, SetFlag,
SetName, TablePart,
},
+ Command, Expression,
};
use proxmox_ve_config::{
firewall::{
ct_helper::CtHelperMacro,
- types::{Alias, Ipset, alias::AliasName, ipset::IpsetAddress},
+ types::{alias::AliasName, ipset::IpsetAddress, Alias, Ipset},
},
guest::types::Vmid,
};
diff --git a/proxmox-firewall/src/rule.rs b/proxmox-firewall/src/rule.rs
index 77bc6ea..2512537 100644
--- a/proxmox-firewall/src/rule.rs
+++ b/proxmox-firewall/src/rule.rs
@@ -1,20 +1,19 @@
use std::ops::{Deref, DerefMut};
-use anyhow::{Error, bail, format_err};
+use anyhow::{bail, format_err, Error};
use proxmox_log as log;
use proxmox_nftables::{
- Expression, Statement,
expression::{Ct, IpFamily, Meta, Payload, Prefix},
statement::{Log, LogLevel, Match, Operator},
types::{AddRule, ChainPart, SetName, TableFamily, TablePart},
+ Expression, Statement,
};
use proxmox_ve_config::{
firewall::{
ct_helper::CtHelperMacro,
- fw_macros::{FwMacro, get_macro},
+ fw_macros::{get_macro, FwMacro},
types::{
- Alias, Rule,
alias::AliasName,
ipset::{Ipfilter, IpsetName},
log::LogRateLimit,
@@ -22,6 +21,7 @@ use proxmox_ve_config::{
rule_match::{
Icmp, Icmpv6, IpAddrMatch, IpMatch, Ports, Protocol, RuleMatch, Sctp, Tcp, Udp,
},
+ Alias, Rule,
},
},
guest::types::Vmid,
@@ -691,18 +691,16 @@ impl ToNftRules for Ipfilter<'_> {
if env.contains_family(Family::V4) {
base_rule.set_family(Family::V4);
- base_rule.append(&mut vec![
- Match::new_ne(
- Payload::field("arp", "saddr ip"),
- Expression::set_name(&SetName::ipset_name(
- Family::V4,
- self.ipset().name(),
- env.vmid,
- false,
- )),
- )
- .into(),
- ]);
+ base_rule.append(&mut vec![Match::new_ne(
+ Payload::field("arp", "saddr ip"),
+ Expression::set_name(&SetName::ipset_name(
+ Family::V4,
+ self.ipset().name(),
+ env.vmid,
+ false,
+ )),
+ )
+ .into()]);
rules.push(base_rule);
}
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pve-devel] [PATCH proxmox-firewall v2 2/3] ipfilter: fix wrong entries for containers
2025-09-25 12:21 [pve-devel] [PATCH proxmox{-ve-rs, -firewall} v2 0/4] Fix ipfilters in proxmox-firewall Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-ve-rs v2 1/1] config: guest: store network devices in BTreeMap Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 1/3] run rustfmt Stefan Hanreich
@ 2025-09-25 12:21 ` Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 3/3] fix #6336: fix ipfilter matching logic Stefan Hanreich
3 siblings, 0 replies; 5+ messages in thread
From: Stefan Hanreich @ 2025-09-25 12:21 UTC (permalink / raw)
To: pve-devel
The firewall used the CIDR from the container network configuration
for autogenerating the IP filter. If an IP of 192.0.2.1/24 was
configured, then the whole 192.0.2.0/24 range was allowed instead of
only 192.0.2.1 .
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
proxmox-firewall/src/firewall.rs | 10 +-
proxmox-firewall/tests/input/100.conf | 1 +
.../integration_tests__firewall.snap | 316 ++++++++++++++++++
3 files changed, 324 insertions(+), 3 deletions(-)
diff --git a/proxmox-firewall/src/firewall.rs b/proxmox-firewall/src/firewall.rs
index 5012610..ddf839b 100644
--- a/proxmox-firewall/src/firewall.rs
+++ b/proxmox-firewall/src/firewall.rs
@@ -24,7 +24,7 @@ use proxmox_ve_config::firewall::ct_helper::get_cthelper;
use proxmox_ve_config::firewall::guest::Config as GuestConfig;
use proxmox_ve_config::firewall::host::Config as HostConfig;
-use proxmox_network_types::ip_address::{Cidr, Ipv6Cidr};
+use proxmox_network_types::ip_address::{Cidr, Ipv4Cidr, Ipv6Cidr};
use proxmox_ve_config::firewall::types::ipset::{
Ipfilter, Ipset, IpsetEntry, IpsetName, IpsetScope,
};
@@ -815,11 +815,15 @@ impl Firewall {
ipset.push(IpsetEntry::from(Cidr::from(cidr)));
if let Some(ip_address) = network_device.ip() {
- ipset.push(IpsetEntry::from(Cidr::from(ip_address)));
+ ipset.push(IpsetEntry::from(Cidr::from(Ipv4Cidr::from(
+ *ip_address.address(),
+ ))));
}
if let Some(ip6_address) = network_device.ip6() {
- ipset.push(IpsetEntry::from(Cidr::from(ip6_address)));
+ ipset.push(IpsetEntry::from(Cidr::from(Ipv6Cidr::from(
+ *ip6_address.address(),
+ ))));
}
commands.append(&mut ipset.to_nft_objects(&env)?);
diff --git a/proxmox-firewall/tests/input/100.conf b/proxmox-firewall/tests/input/100.conf
index cf9af7f..1f81186 100644
--- a/proxmox-firewall/tests/input/100.conf
+++ b/proxmox-firewall/tests/input/100.conf
@@ -5,6 +5,7 @@ hostname: host1
memory: 512
net1: name=eth0,bridge=simple1,firewall=1,hwaddr=BC:24:11:4D:B0:FF,ip=dhcp,ip6=fd80::1234/64,type=veth
net2: name=eth0,bridge=simple1,hwaddr=BC:24:11:4D:B0:FF,ip=dhcp,ip6=fd80::1234/64,type=veth
+net3: name=eth0,bridge=simple2,firewall=1,hwaddr=BC:24:11:4D:B0:FE,ip=192.0.2.10/24,ip6=fd80::1235/64,type=veth
ostype: debian
rootfs: local-lvm:vm-90001-disk-0,size=2G
swap: 512
diff --git a/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap b/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap
index e3db8ae..feeda5b 100644
--- a/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap
+++ b/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap
@@ -4384,6 +4384,306 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")"
}
}
},
+ {
+ "add": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v4-guest-100/ipfilter-net3",
+ "type": "ipv4_addr",
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ },
+ {
+ "flush": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v4-guest-100/ipfilter-net3"
+ }
+ }
+ },
+ {
+ "add": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v4-guest-100/ipfilter-net3-nomatch",
+ "type": "ipv4_addr",
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ },
+ {
+ "flush": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v4-guest-100/ipfilter-net3-nomatch"
+ }
+ }
+ },
+ {
+ "add": {
+ "element": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v4-guest-100/ipfilter-net3",
+ "elem": [
+ {
+ "prefix": {
+ "addr": "192.0.2.10",
+ "len": 32
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v6-guest-100/ipfilter-net3",
+ "type": "ipv6_addr",
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ },
+ {
+ "flush": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v6-guest-100/ipfilter-net3"
+ }
+ }
+ },
+ {
+ "add": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v6-guest-100/ipfilter-net3-nomatch",
+ "type": "ipv6_addr",
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ },
+ {
+ "flush": {
+ "set": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v6-guest-100/ipfilter-net3-nomatch"
+ }
+ }
+ },
+ {
+ "add": {
+ "element": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "name": "v6-guest-100/ipfilter-net3",
+ "elem": [
+ {
+ "prefix": {
+ "addr": "fe80::be24:11ff:fe4d:b0fe",
+ "len": 128
+ }
+ },
+ {
+ "prefix": {
+ "addr": "fd80::1235",
+ "len": 128
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-in",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "veth100i3"
+ }
+ },
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "payload": {
+ "protocol": "arp",
+ "field": "daddr ip"
+ }
+ },
+ "right": "@v4-guest-100/ipfilter-net3"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-out",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "veth100i3"
+ }
+ },
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@v4-guest-100/ipfilter-net3"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@v4-guest-100/ipfilter-net3-nomatch"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-out",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "veth100i3"
+ }
+ },
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ },
+ "right": "@v6-guest-100/ipfilter-net3"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ },
+ "right": "@v6-guest-100/ipfilter-net3-nomatch"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-out",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "veth100i3"
+ }
+ },
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "payload": {
+ "protocol": "arp",
+ "field": "saddr ip"
+ }
+ },
+ "right": "@v4-guest-100/ipfilter-net3"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
{
"add": {
"rule": {
@@ -4742,6 +5042,14 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")"
"target": "guest-100-in"
}
}
+ ],
+ [
+ "veth100i3",
+ {
+ "goto": {
+ "target": "guest-100-in"
+ }
+ }
]
]
}
@@ -4886,6 +5194,14 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")"
"target": "guest-100-out"
}
}
+ ],
+ [
+ "veth100i3",
+ {
+ "goto": {
+ "target": "guest-100-out"
+ }
+ }
]
]
}
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pve-devel] [PATCH proxmox-firewall v2 3/3] fix #6336: fix ipfilter matching logic
2025-09-25 12:21 [pve-devel] [PATCH proxmox{-ve-rs, -firewall} v2 0/4] Fix ipfilters in proxmox-firewall Stefan Hanreich
` (2 preceding siblings ...)
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 2/3] ipfilter: fix wrong entries for containers Stefan Hanreich
@ 2025-09-25 12:21 ` Stefan Hanreich
3 siblings, 0 replies; 5+ messages in thread
From: Stefan Hanreich @ 2025-09-25 12:21 UTC (permalink / raw)
To: pve-devel
Matching on ipsets in the firewall generally works by matching on two
sets (one for match, one for nomatch):
ip saddr @ipfilter ip saddr != @ipfilter-nomatch <verdict>
Ipfilters were created with the comparison operators simply inverted,
which leads to ipfilters with empty nomatch sets never working, since
the second expression always evaluates to false on empty sets:
ip saddr != @ipfilter ip saddr = @ipfilter-nomatch drop
In order to properly invert the logic, two statements need to be
generated (analogous to negation in boolean logic):
ip saddr != @ipfilter drop
ip saddr = @ipfilter-nomatch drop
To avoid cluttering the existing set handling function and to clearly
separate the rule generation logic, introduce a completely new
function that handles ipfilters separately. This also allows the set
handling function to validate the name of ipsets in a later commit,
since it only operates on ipsets that have to exist in the firewall
config, whereas ipfilters do not necessarily exist in the firewall
configuration.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
proxmox-firewall/src/rule.rs | 131 ++++++++++++++++--
.../integration_tests__firewall.snap | 100 +++++++++++++
2 files changed, 218 insertions(+), 13 deletions(-)
diff --git a/proxmox-firewall/src/rule.rs b/proxmox-firewall/src/rule.rs
index 2512537..23e87a5 100644
--- a/proxmox-firewall/src/rule.rs
+++ b/proxmox-firewall/src/rule.rs
@@ -283,12 +283,15 @@ impl ToNftRules for RuleMatch {
}
}
+/// Handle matching on an ipset.
+///
+/// This function adds statements to the `rules` that match on the ipset with the given name
+/// `name`. It matches the IPs contained in the ipset with the field given in `field_name`.
fn handle_set(
rules: &mut Vec<NftRule>,
name: &IpsetName,
field_name: &str,
env: &NftRuleEnv,
- contains: bool,
) -> Result<(), Error> {
let mut new_rules = rules
.drain(..)
@@ -303,7 +306,7 @@ fn handle_set(
rule.append(&mut vec![
Match::new(
- if contains { Operator::Eq } else { Operator::Ne },
+ Operator::Eq,
field.clone(),
Expression::set_name(&SetName::ipset_name(
Family::V4,
@@ -314,7 +317,7 @@ fn handle_set(
)
.into(),
Match::new(
- if contains { Operator::Ne } else { Operator::Eq },
+ Operator::Ne,
field,
Expression::set_name(&SetName::ipset_name(
Family::V4,
@@ -337,7 +340,7 @@ fn handle_set(
rule.append(&mut vec![
Match::new(
- if contains { Operator::Eq } else { Operator::Ne },
+ Operator::Eq,
field.clone(),
Expression::set_name(&SetName::ipset_name(
Family::V6,
@@ -348,7 +351,7 @@ fn handle_set(
)
.into(),
Match::new(
- if contains { Operator::Ne } else { Operator::Eq },
+ Operator::Ne,
field,
Expression::set_name(&SetName::ipset_name(
Family::V6,
@@ -372,6 +375,114 @@ fn handle_set(
Ok(())
}
+/// Handle matching on an ipfilter.
+///
+/// This function adds statements to the `rules` that match if the IP in the field `field_name`
+/// does not fulfill the criteria of the given ipfilter.
+fn handle_ipfilter(
+ rules: &mut Vec<NftRule>,
+ name: &IpsetName,
+ field_name: &str,
+ env: &NftRuleEnv,
+) -> Result<(), Error> {
+ let mut new_rules = rules
+ .drain(..)
+ .flat_map(|rule| {
+ let mut new_rules = Vec::new();
+
+ if matches!(rule.family(), Some(Family::V4) | None) && env.contains_family(Family::V4) {
+ let field = Payload::field("ip", field_name);
+
+ let mut match_rule = rule.clone();
+ match_rule.set_family(Family::V4);
+
+ match_rule.push(
+ Match::new(
+ Operator::Ne,
+ field.clone(),
+ Expression::set_name(&SetName::ipset_name(
+ Family::V4,
+ name,
+ env.vmid,
+ false,
+ )),
+ )
+ .into(),
+ );
+
+ new_rules.push(match_rule);
+
+ let mut nomatch_rule = rule.clone();
+ nomatch_rule.set_family(Family::V4);
+
+ nomatch_rule.push(
+ Match::new(
+ Operator::Eq,
+ field,
+ Expression::set_name(&SetName::ipset_name(
+ Family::V4,
+ name,
+ env.vmid,
+ true,
+ )),
+ )
+ .into(),
+ );
+
+ new_rules.push(nomatch_rule);
+ }
+
+ if matches!(rule.family(), Some(Family::V6) | None) && env.contains_family(Family::V6) {
+ let field = Payload::field("ip6", field_name);
+
+ let mut match_rule = rule.clone();
+ match_rule.set_family(Family::V4);
+
+ match_rule.push(
+ Match::new(
+ Operator::Ne,
+ field.clone(),
+ Expression::set_name(&SetName::ipset_name(
+ Family::V6,
+ name,
+ env.vmid,
+ false,
+ )),
+ )
+ .into(),
+ );
+
+ new_rules.push(match_rule);
+
+ let mut nomatch_rule = rule.clone();
+ nomatch_rule.set_family(Family::V4);
+
+ nomatch_rule.push(
+ Match::new(
+ Operator::Eq,
+ field,
+ Expression::set_name(&SetName::ipset_name(
+ Family::V6,
+ name,
+ env.vmid,
+ true,
+ )),
+ )
+ .into(),
+ );
+
+ new_rules.push(nomatch_rule);
+ }
+
+ new_rules
+ })
+ .collect::<Vec<NftRule>>();
+
+ rules.append(&mut new_rules);
+
+ Ok(())
+}
+
fn handle_match(
rules: &mut Vec<NftRule>,
ip: &IpAddrMatch,
@@ -447,7 +558,7 @@ fn handle_match(
Ok(())
}
- IpAddrMatch::Set(name) => handle_set(rules, name, field_name, env, true),
+ IpAddrMatch::Set(name) => handle_set(rules, name, field_name, env),
}
}
@@ -679,13 +790,7 @@ impl ToNftRules for Ipfilter<'_> {
);
let mut ipfilter_rules = vec![base_rule.clone()];
- handle_set(
- &mut ipfilter_rules,
- self.ipset().name(),
- "saddr",
- env,
- false,
- )?;
+ handle_ipfilter(&mut ipfilter_rules, self.ipset().name(), "saddr", env)?;
rules.append(&mut ipfilter_rules);
if env.contains_family(Family::V4) {
diff --git a/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap b/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap
index feeda5b..71285af 100644
--- a/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap
+++ b/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap
@@ -4279,6 +4279,31 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")"
"right": "@v4-guest-100/ipfilter-net1"
}
},
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-out",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "veth100i1"
+ }
+ },
{
"match": {
"op": "==",
@@ -4328,6 +4353,31 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")"
"right": "@v6-guest-100/ipfilter-net1"
}
},
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-out",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "veth100i1"
+ }
+ },
{
"match": {
"op": "==",
@@ -4579,6 +4629,31 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")"
"right": "@v4-guest-100/ipfilter-net3"
}
},
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-out",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "veth100i3"
+ }
+ },
{
"match": {
"op": "==",
@@ -4628,6 +4703,31 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")"
"right": "@v6-guest-100/ipfilter-net3"
}
},
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ },
+ {
+ "add": {
+ "rule": {
+ "family": "bridge",
+ "table": "proxmox-firewall-guests",
+ "chain": "guest-100-out",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "veth100i3"
+ }
+ },
{
"match": {
"op": "==",
--
2.47.3
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-09-25 12:21 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-25 12:21 [pve-devel] [PATCH proxmox{-ve-rs, -firewall} v2 0/4] Fix ipfilters in proxmox-firewall Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-ve-rs v2 1/1] config: guest: store network devices in BTreeMap Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 1/3] run rustfmt Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 2/3] ipfilter: fix wrong entries for containers Stefan Hanreich
2025-09-25 12:21 ` [pve-devel] [PATCH proxmox-firewall v2 3/3] fix #6336: fix ipfilter matching logic Stefan Hanreich
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.