Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3eac02716 |
@ -0,0 +1,85 @@
|
||||
From ad48eb571ff4966c3ebfbbf12fe8adf293658e20 Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Lukoshko <alukoshko@almalinux.org>
|
||||
Date: Tue, 10 Mar 2026 13:30:54 +0100
|
||||
Subject: fix(nftables): batch ipset elements in set_restore to avoid O(n^2)
|
||||
insertion cost
|
||||
|
||||
set_restore() currently creates one "add element" nft operation per
|
||||
ipset entry, then sends them in chunks of 1000 operations. On older
|
||||
nftables/kernel combinations (e.g. nftables 1.0.4 / kernel 4.18
|
||||
shipped in RHEL 8) each incremental element insertion into an interval
|
||||
set has O(n) cost proportional to the current set size, making the
|
||||
overall complexity O(n^2).
|
||||
|
||||
With 12,000 ipset entries this causes firewall-cmd --reload to take
|
||||
~80 seconds on RHEL 8 with the nftables backend. Even on newer
|
||||
systems (nftables 1.0.9 / kernel 5.14) batching yields a ~20%
|
||||
improvement.
|
||||
|
||||
This patch accumulates all element fragments and creates batched
|
||||
"add element" operations - one per chunk of 1000 elements - instead
|
||||
of one per entry. This lets nftables handle bulk element insertion
|
||||
natively and reduces nft operations from N to ceil(N/1000).
|
||||
|
||||
Benchmark (12k ipset entries, nftables backend):
|
||||
RHEL 8 (nftables 1.0.4, kernel 4.18): ~79,100ms -> ~2,500ms
|
||||
RHEL 9 (nftables 1.0.9, kernel 5.14): ~1,560ms -> ~1,240ms
|
||||
|
||||
Backport of upstream PR #1544 for firewalld 0.9.11 (RHEL/AlmaLinux 8).
|
||||
NOTE: erig0's original RHEL-8 backport was missing rules.clear()
|
||||
between chunks, causing "No buffer space available" on large ipsets.
|
||||
This version includes the fix.
|
||||
|
||||
Fixes #933
|
||||
---
|
||||
src/firewall/core/nftables.py | 23 ++++++++++++-----------
|
||||
1 file changed, 12 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
||||
index XXXXXXX..XXXXXXX 100644
|
||||
--- a/src/firewall/core/nftables.py
|
||||
+++ b/src/firewall/core/nftables.py
|
||||
@@ -1896,14 +1896,19 @@ class nftables(object):
|
||||
fragment.append(entry_tokens[i])
|
||||
return [{"concat": fragment}] if len(type_format) > 1 else fragment
|
||||
|
||||
- def build_set_add_rules(self, name, entry):
|
||||
+ def build_set_add_rules(self, name, entries):
|
||||
rules = []
|
||||
- element = self._set_entry_fragment(name, entry)
|
||||
+ elements = []
|
||||
+ if not isinstance(entries, (list, tuple)):
|
||||
+ entries = [entries]
|
||||
+ for element in entries:
|
||||
+ elements.extend(self._set_entry_fragment(name, element))
|
||||
+
|
||||
for family in ["inet", "ip", "ip6"]:
|
||||
rules.append({"add": {"element": {"family": family,
|
||||
"table": TABLE_NAME,
|
||||
"name": name,
|
||||
- "elem": element}}})
|
||||
+ "elem": elements}}})
|
||||
return rules
|
||||
|
||||
def set_add(self, name, entry):
|
||||
@@ -1952,13 +1957,9 @@ class nftables(object):
|
||||
rules.extend(self.build_set_flush_rules(set_name))
|
||||
|
||||
# avoid large memory usage by chunking the entries
|
||||
- chunk = 0
|
||||
- for entry in entries:
|
||||
- rules.extend(self.build_set_add_rules(set_name, entry))
|
||||
- chunk += 1
|
||||
- if chunk >= 1000:
|
||||
- self.set_rules(rules, self._fw.get_log_denied())
|
||||
- rules.clear()
|
||||
- chunk = 0
|
||||
- else:
|
||||
+ for i in range(0, len(entries), 1000):
|
||||
+ rules.extend(self.build_set_add_rules(set_name, entries[i : i + 1000]))
|
||||
+ self.set_rules(rules, self._fw.get_log_denied())
|
||||
+ rules.clear()
|
||||
+ else:
|
||||
self.set_rules(rules, self._fw.get_log_denied())
|
||||
--
|
||||
2.43.5
|
||||
@ -1,7 +1,7 @@
|
||||
Summary: A firewall daemon with D-Bus interface providing a dynamic firewall
|
||||
Name: firewalld
|
||||
Version: 0.9.11
|
||||
Release: 10%{?dist}
|
||||
Release: 10%{?dist}.alma.1
|
||||
URL: http://www.firewalld.org
|
||||
License: GPLv2+
|
||||
Source0: https://github.com/firewalld/firewalld/releases/download/v%{version}/firewalld-%{version}.tar.gz
|
||||
@ -37,6 +37,9 @@ Patch29: 0029-v2.0.0-feat-direct-avoid-iptables-flush-if-using-nft.patch
|
||||
Patch30: 0030-v2.0.0-test-direct-avoid-iptables-flush-if-using-nft.patch
|
||||
Patch31: 0031-v2.2.0-fix-service-update-highest-port-number-for-ce.patch
|
||||
|
||||
# AlmaLinux Patch
|
||||
Patch1000: 1000-fix-nftables-batch-ipset-elements-to-avoid-O-n2-inse.patch
|
||||
|
||||
BuildArch: noarch
|
||||
BuildRequires: autoconf
|
||||
BuildRequires: automake
|
||||
@ -237,6 +240,10 @@ desktop-file-install --delete-original \
|
||||
%{_mandir}/man1/firewall-config*.1*
|
||||
|
||||
%changelog
|
||||
* Wed Mar 11 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 0.9.11-10.alma.1
|
||||
- fix(nftables): batch ipset elements to avoid O(n^2) insertion cost
|
||||
- Resolves: almalinux/almalinux-deploy#186
|
||||
|
||||
* Tue Feb 04 2025 Eric Garver <egarver@redhat.com> - 0.9.11-10
|
||||
- fix(service): update highest port number for ceph
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user