Repository navigation
ACL with multiple CIDRs does not honor rule id #12668
Description
Activity
@bradh352
I tested with the same CIDR
iptables rules look ok
@@ -71,6 +71,8 @@ -A ACL_INBOUND_eth2 -j DROP -A ACL_INBOUND_eth3 -d 225.0.0.50/32 -j ACCEPT -A ACL_INBOUND_eth3 -d 224.0.0.18/32 -j ACCEPT +-A ACL_INBOUND_eth3 -s 2.3.4.5/32 -p tcp -m tcp --dport 22:23 -j ACCEPT +-A ACL_INBOUND_eth3 -s 1.2.3.4/32 -p tcp -m tcp --dport 22:23 -j ACCEPT -A ACL_INBOUND_eth3 -j DROP -A NETWORK_STATS_eth1 -s 172.16.0.0/20 -o eth1 -A NETWORK_STATS_eth1 -d 172.16.0.0/20 -i eth1I can reproduce this on a VPC virtual router (CloudStack 4.23.0.0, VR template <4.22.0>, KVM, iptables v1.8.9 (nf_tables)).
Why a single multi-CIDR rule looks fine: it only goes wrong when the multi-CIDR rule is followed by other rules in the same ACL list. With just one rule there is nothing after it, so the order cannot be seen to be wrong.
Setup: a tier ACL list with ingress rules such as 10 (allow tcp 80, 10.10.0.0/24,10.10.7.0/24), 12 (allow tcp 135, three CIDRs), 13 (allow tcp 443, 0.0.0.0/0), more single- and multi-CIDR rules, and a deny at 9998.
Result on the VR (iptables -L ACL_INBOUND_eth9 -n -v --line-numbers, and the same for ACL_OUTBOUND_* in the mangle table):
3 ACCEPT tcp 10.10.7.0/24 tcp dpt:80 <- only the LAST cidr of rule 10
5 ACCEPT tcp 10.10.4.0/24 tcp dpt:135 <- only the LAST cidr of rule 12
6 ACCEPT tcp 0.0.0.0/0 tcp dpt:443
21 DROP <- explicit deny (rule 9998)
29 ACCEPT tcp 10.10.2.0/24 tcp dpt:135 <- other CIDRs of rule 12, behind the DROP
30 ACCEPT tcp 10.10.0.10 tcp dpt:135
31 ACCEPT tcp 10.10.0.0/24 tcp dpt:80 <- other CIDR of rule 10, behind the DROP
32 DROP <- the VR's own final DROP
In every multi-CIDR rule only the last CIDR lands in the right place. The rest are appended after the explicit deny, so they never match. It happens in both ingress (filter) and egress (mangle). Single-CIDR rules are fine.Impact: allow rules silently stop working for all but one CIDR. A deny with several CIDRs (for example "block private ranges" placed before an internet allow) would end up after the allow and stop denying.
Possible cause (from reading configure.py, not tested): AclDevice.process() raises the insert index by 1 per ACL rule, and AclRule.create() emits one -A -s a,b,c ....
iptables expands the comma-separated source into several rules, so the tracked index drifts after every multi-CIDR rule, and CsNetfilter inserts "right before the DROP all" at the wrong position.
Workaround: one CIDR per rule, as @bradh352 wrote.
Follow-up: the IPv6 side of the same problem is worse, those rules are not misplaced, they are not applied at all.
On the same router (4.23.0.0, VR template 4.22.0), the ACL list has 20 IPv6 ingress rules.
nft list chain ip6 ip6_acl eth9_ingress_policycontains 15 of them. The 5 that are missing are exactly the rules whose cidrlist has two or more IPv6 CIDRs (tcp 4172, tcp 4521, tcp 6283-6284, tcp 52000-60000 and an ICMPv6 rule). Rules with exactly one IPv6 CIDR, including mixed IPv4+IPv6 rules, are applied correctly. Nothing is reported in the UI or API.Cause:
AclDevice.__process_ip6inconfigure.pybuilds the rule from the whole comma-separated string (addr = "ip6 saddr " + cidr), and nft does not accept a bare comma list. Checked on the VR withnft -c(nothing applied):$ nft -c add rule ip6 ip6_acl eth9_ingress_policy ip6 saddr 2001:db8:a1:2009::/64,2001:db8:a1:afd2::/64 tcp dport 4172 accept Error: syntax error, unexpected /, expecting end of file or newline or semicolon $ nft -c add rule ip6 ip6_acl eth9_ingress_policy ip6 saddr '{ 2001:db8:a1:2009::/64, 2001:db8:a1:afd2::/64 }' tcp dport 4172 accept (no error)cloud.logshows each IPv6 ACL rule being added one at a time withnft add rule ip6 ip6_acl ..., so only the failing rules disappear and the rest of the list loads.- added a commit that references this issue
on Oct 8, 2026 Fix in PR #14350
Metadata
Metadata
Assignees
Type
Projects
- StatusShow more project fieldsDiscuss
problem
If you have a CIDR list like:
rule 1: [ "1.2.3.4/32", "2.3.4.5/32"] tcp allow port 22
rule 65535: [ "0.0.0.0/0"] deny port any
What you end up with when inspecting the VR is:
This is clearly not the desired behavior.
versions
Cloudstack 4.22.0
The steps to reproduce the bug
See description
What to do about it?
Don't use more than one CIDR per rule