import firewalld-0.9.3-13.el8

This commit is contained in:
CentOS Sources 2022-02-27 05:25:03 +00:00 committed by Stepan Oksanichenko
parent b3b966a3e7
commit c2c6a1782f
6 changed files with 756 additions and 1 deletions

View File

@ -0,0 +1,302 @@
From fb11903b8efd287f72e634fb8a4b4ff2034151fe Mon Sep 17 00:00:00 2001
From: Paul Laufer <50234787+refual@users.noreply.github.com>
Date: Fri, 27 Nov 2020 12:23:11 +0100
Subject: [PATCH 47/48] feat(config): add CleanupModulesOnExit configuration
option
Fixes: rhbz 1520532
Fixes: #533
Closes: #721
(cherry picked from commit 152a51537a7840afd0879ab4b60178bef4ec16a2)
---
config/firewalld.conf | 9 +++++++-
doc/xml/firewalld.conf.xml | 11 ++++++++++
doc/xml/firewalld.dbus.xml | 9 ++++++++
src/firewall/config/__init__.py.in | 1 +
src/firewall/core/fw.py | 29 +++++++++++++++++++-------
src/firewall/core/io/firewalld_conf.py | 19 +++++++++++++----
src/firewall/server/config.py | 23 +++++++++++++-------
src/tests/dbus/firewalld.conf.at | 2 ++
8 files changed, 82 insertions(+), 21 deletions(-)
diff --git a/config/firewalld.conf b/config/firewalld.conf
index a0556c0bbf5b..3abbc9c998c1 100644
--- a/config/firewalld.conf
+++ b/config/firewalld.conf
@@ -7,10 +7,17 @@ DefaultZone=public
# Clean up on exit
# If set to no or false the firewall configuration will not get cleaned up
-# on exit or stop of firewalld
+# on exit or stop of firewalld.
# Default: yes
CleanupOnExit=yes
+# Clean up kernel modules on exit
+# If set to yes or true the firewall related kernel modules will be
+# unloaded on exit or stop of firewalld. This might attempt to unload
+# modules not originally loaded by firewalld.
+# Default: no
+CleanupModulesOnExit=no
+
# Lockdown
# If set to enabled, firewall changes with the D-Bus interface will be limited
# to applications that are listed in the lockdown whitelist.
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
index 0bf4c2d4d011..dd6ffb214eb3 100644
--- a/doc/xml/firewalld.conf.xml
+++ b/doc/xml/firewalld.conf.xml
@@ -88,6 +88,17 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>CleanupModulesOnExit</option></term>
+ <listitem>
+ <para>
+ Setting this option to yes or true unloads all firewall-related
+ kernel modules when firewalld is stopped. The default value is no
+ or false.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>CleanupOnExit</option></term>
<listitem>
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
index d17cb8b6c1ec..466220b40b21 100644
--- a/doc/xml/firewalld.dbus.xml
+++ b/doc/xml/firewalld.dbus.xml
@@ -2798,6 +2798,15 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry id="FirewallD1.config.Properties.CleanupModulesOnExit">
+ <term>CleanupModulesOnExit - s - (rw)</term>
+ <listitem>
+ <para>
+ Setting this option to yes or true unloads all firewall-related
+ kernel modules when firewalld is stopped.
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry id="FirewallD1.config.Properties.CleanupOnExit">
<term>CleanupOnExit - s - (rw)</term>
<listitem>
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
index 0dec7913f694..5d6d769fbf15 100644
--- a/src/firewall/config/__init__.py.in
+++ b/src/firewall/config/__init__.py.in
@@ -125,6 +125,7 @@ FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
FALLBACK_ZONE = "public"
FALLBACK_MINIMAL_MARK = 100
FALLBACK_CLEANUP_ON_EXIT = True
+FALLBACK_CLEANUP_MODULES_ON_EXIT = False
FALLBACK_LOCKDOWN = False
FALLBACK_IPV6_RPFILTER = True
FALLBACK_INDIVIDUAL_CALLS = False
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index 3eb54e37ab5c..4171697bdb94 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -105,12 +105,13 @@ class Firewall(object):
self.__init_vars()
def __repr__(self):
- return '%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)' % \
+ return '%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)' % \
(self.__class__, self.ip4tables_enabled, self.ip6tables_enabled,
self.ebtables_enabled, self._state, self._panic,
self._default_zone, self._module_refcount, self._marks,
- self.cleanup_on_exit, self.ipv6_rpfilter_enabled,
- self.ipset_enabled, self._individual_calls, self._log_denied)
+ self.cleanup_on_exit, self.cleanup_modules_on_exit,
+ self.ipv6_rpfilter_enabled, self.ipset_enabled,
+ self._individual_calls, self._log_denied)
def __init_vars(self):
self._state = "INIT"
@@ -120,6 +121,7 @@ class Firewall(object):
self._marks = [ ]
# fallback settings will be overloaded by firewalld.conf
self.cleanup_on_exit = config.FALLBACK_CLEANUP_ON_EXIT
+ self.cleanup_modules_on_exit = config.FALLBACK_CLEANUP_MODULES_ON_EXIT
self.ipv6_rpfilter_enabled = config.FALLBACK_IPV6_RPFILTER
self._individual_calls = config.FALLBACK_INDIVIDUAL_CALLS
self._log_denied = config.FALLBACK_LOG_DENIED
@@ -232,6 +234,13 @@ class Firewall(object):
log.debug1("CleanupOnExit is set to '%s'",
self.cleanup_on_exit)
+ if self._firewalld_conf.get("CleanupModulesOnExit"):
+ value = self._firewalld_conf.get("CleanupModulesOnExit")
+ if value is not None and value.lower() in [ "yes", "true" ]:
+ self.cleanup_modules_on_exit = True
+ log.debug1("CleanupModulesOnExit is set to '%s'",
+ self.cleanup_modules_on_exit)
+
if self._firewalld_conf.get("Lockdown"):
value = self._firewalld_conf.get("Lockdown")
if value is not None and value.lower() in [ "yes", "true" ]:
@@ -667,11 +676,15 @@ class Firewall(object):
self.__init_vars()
def stop(self):
- if self.cleanup_on_exit and not self._offline:
- self.flush()
- self.ipset.flush()
- self.set_policy("ACCEPT")
- self.modules_backend.unload_firewall_modules()
+ if not self._offline:
+ if self.cleanup_on_exit:
+ self.flush()
+ self.ipset.flush()
+ self.set_policy("ACCEPT")
+
+ if self.cleanup_modules_on_exit:
+ log.debug1('Unloading firewall kernel modules')
+ self.modules_backend.unload_firewall_modules()
self.cleanup()
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
index 7c7092120676..70258400ef06 100644
--- a/src/firewall/core/io/firewalld_conf.py
+++ b/src/firewall/core/io/firewalld_conf.py
@@ -28,10 +28,11 @@ from firewall import config
from firewall.core.logger import log
from firewall.functions import b2u, u2b, PY2
-valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit", "Lockdown",
- "IPv6_rpfilter", "IndividualCalls", "LogDenied",
- "AutomaticHelpers", "FirewallBackend", "FlushAllOnReload",
- "RFC3964_IPv4", "AllowZoneDrifting" ]
+valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit",
+ "CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
+ "IndividualCalls", "LogDenied", "AutomaticHelpers",
+ "FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
+ "AllowZoneDrifting" ]
class firewalld_conf(object):
def __init__(self, filename):
@@ -75,6 +76,7 @@ class firewalld_conf(object):
self.set("DefaultZone", config.FALLBACK_ZONE)
self.set("MinimalMark", str(config.FALLBACK_MINIMAL_MARK))
self.set("CleanupOnExit", "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no")
+ self.set("CleanupModulesOnExit", "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no")
self.set("Lockdown", "yes" if config.FALLBACK_LOCKDOWN else "no")
self.set("IPv6_rpfilter","yes" if config.FALLBACK_IPV6_RPFILTER else "no")
self.set("IndividualCalls", "yes" if config.FALLBACK_INDIVIDUAL_CALLS else "no")
@@ -135,6 +137,15 @@ class firewalld_conf(object):
config.FALLBACK_CLEANUP_ON_EXIT)
self.set("CleanupOnExit", "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no")
+ # check module cleanup on exit
+ value = self.get("CleanupModulesOnExit")
+ if not value or value.lower() not in [ "no", "false", "yes", "true" ]:
+ if value is not None:
+ log.warning("CleanupModulesOnExit '%s' is not valid, using default "
+ "value %s", value if value else '',
+ config.FALLBACK_CLEANUP_MODULES_ON_EXIT)
+ self.set("CleanupModulesOnExit", "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no")
+
# check lockdown
value = self.get("Lockdown")
if not value or value.lower() not in [ "yes", "true", "no", "false" ]:
diff --git a/src/firewall/server/config.py b/src/firewall/server/config.py
index 031ef5d1afaa..8815920c6893 100644
--- a/src/firewall/server/config.py
+++ b/src/firewall/server/config.py
@@ -100,6 +100,7 @@ class FirewallDConfig(slip.dbus.service.Object):
dbus_introspection_prepare_properties(self,
config.dbus.DBUS_INTERFACE_CONFIG,
{ "CleanupOnExit": "readwrite",
+ "CleanupModulesOnExit": "readwrite",
"IPv6_rpfilter": "readwrite",
"Lockdown": "readwrite",
"MinimalMark": "readwrite",
@@ -554,9 +555,9 @@ class FirewallDConfig(slip.dbus.service.Object):
@dbus_handle_exceptions
def _get_property(self, prop):
if prop not in [ "DefaultZone", "MinimalMark", "CleanupOnExit",
- "Lockdown", "IPv6_rpfilter", "IndividualCalls",
- "LogDenied", "AutomaticHelpers", "FirewallBackend",
- "FlushAllOnReload", "RFC3964_IPv4",
+ "CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
+ "IndividualCalls", "LogDenied", "AutomaticHelpers",
+ "FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
"AllowZoneDrifting" ]:
raise dbus.exceptions.DBusException(
"org.freedesktop.DBus.Error.InvalidArgs: "
@@ -578,6 +579,10 @@ class FirewallDConfig(slip.dbus.service.Object):
if value is None:
value = "yes" if config.FALLBACK_CLEANUP_ON_EXIT else "no"
return dbus.String(value)
+ elif prop == "CleanupModulesOnExit":
+ if value is None:
+ value = "yes" if config.FALLBACK_CLEANUP_MODULES_ON_EXIT else "no"
+ return dbus.String(value)
elif prop == "Lockdown":
if value is None:
value = "yes" if config.FALLBACK_LOCKDOWN else "no"
@@ -623,6 +628,8 @@ class FirewallDConfig(slip.dbus.service.Object):
return dbus.Int32(self._get_property(prop))
elif prop == "CleanupOnExit":
return dbus.String(self._get_property(prop))
+ elif prop == "CleanupModulesOnExit":
+ return dbus.String(self._get_property(prop))
elif prop == "Lockdown":
return dbus.String(self._get_property(prop))
elif prop == "IPv6_rpfilter":
@@ -679,9 +686,9 @@ class FirewallDConfig(slip.dbus.service.Object):
ret = { }
if interface_name == config.dbus.DBUS_INTERFACE_CONFIG:
for x in [ "DefaultZone", "MinimalMark", "CleanupOnExit",
- "Lockdown", "IPv6_rpfilter", "IndividualCalls",
- "LogDenied", "AutomaticHelpers", "FirewallBackend",
- "FlushAllOnReload", "RFC3964_IPv4",
+ "CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
+ "IndividualCalls", "LogDenied", "AutomaticHelpers",
+ "FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
"AllowZoneDrifting" ]:
ret[x] = self._get_property(x)
elif interface_name in [ config.dbus.DBUS_INTERFACE_CONFIG_DIRECT,
@@ -706,12 +713,12 @@ class FirewallDConfig(slip.dbus.service.Object):
self.accessCheck(sender)
if interface_name == config.dbus.DBUS_INTERFACE_CONFIG:
- if property_name in [ "CleanupOnExit", "Lockdown",
+ if property_name in [ "CleanupOnExit", "Lockdown", "CleanupModulesOnExit",
"IPv6_rpfilter", "IndividualCalls",
"LogDenied",
"FirewallBackend", "FlushAllOnReload",
"RFC3964_IPv4", "AllowZoneDrifting" ]:
- if property_name in [ "CleanupOnExit", "Lockdown",
+ if property_name in [ "CleanupOnExit", "Lockdown", "CleanupModulesOnExit",
"IPv6_rpfilter", "IndividualCalls" ]:
if new_value.lower() not in [ "yes", "no",
"true", "false" ]:
diff --git a/src/tests/dbus/firewalld.conf.at b/src/tests/dbus/firewalld.conf.at
index 9fc5502a8d0b..9a04a3bd491c 100644
--- a/src/tests/dbus/firewalld.conf.at
+++ b/src/tests/dbus/firewalld.conf.at
@@ -17,6 +17,7 @@ dnl Verify defaults over dbus. Should be inline with default firewalld.conf.
DBUS_GETALL([config], [config], 0, [dnl
string "AllowZoneDrifting" : variant string "no"
string "AutomaticHelpers" : variant string "no"
+string "CleanupModulesOnExit" : variant string "no"
string "CleanupOnExit" : variant string "no"
string "DefaultZone" : variant string "public"
string "FirewallBackend" : variant string "nftables"
@@ -45,6 +46,7 @@ _helper([IPv6_rpfilter], [string:"yes"], [variant string "yes"])
_helper([IndividualCalls], [string:"yes"], [variant string "yes"])
_helper([FirewallBackend], [string:"iptables"], [variant string "iptables"])
_helper([FlushAllOnReload], [string:"no"], [variant string "no"])
+_helper([CleanupModulesOnExit], [string:"yes"], [variant string "yes"])
_helper([CleanupOnExit], [string:"yes"], [variant string "yes"])
_helper([RFC3964_IPv4], [string:"no"], [variant string "no"])
_helper([AllowZoneDrifting], [string:"yes"], [variant string "yes"])
--
2.31.1

View File

@ -0,0 +1,95 @@
From 1aef58a8ff6d232cefcc6bd19ea63c0f071bfee3 Mon Sep 17 00:00:00 2001
From: Eric Garver <egarver@redhat.com>
Date: Mon, 20 Dec 2021 13:56:55 -0500
Subject: [PATCH 48/48] RHEL only: default to CleanupModulesOnExit=yes
Resolves: rhbz1980206
---
config/firewalld.conf | 4 ++--
doc/xml/firewalld.conf.xml | 4 ++--
src/firewall/config/__init__.py.in | 2 +-
src/firewall/core/fw.py | 2 ++
src/tests/dbus/firewalld.conf.at | 4 ++--
5 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/config/firewalld.conf b/config/firewalld.conf
index 3abbc9c998c1..c387f87c28be 100644
--- a/config/firewalld.conf
+++ b/config/firewalld.conf
@@ -15,8 +15,8 @@ CleanupOnExit=yes
# If set to yes or true the firewall related kernel modules will be
# unloaded on exit or stop of firewalld. This might attempt to unload
# modules not originally loaded by firewalld.
-# Default: no
-CleanupModulesOnExit=no
+# Default: yes
+CleanupModulesOnExit=yes
# Lockdown
# If set to enabled, firewall changes with the D-Bus interface will be limited
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
index dd6ffb214eb3..12d9f5fc563e 100644
--- a/doc/xml/firewalld.conf.xml
+++ b/doc/xml/firewalld.conf.xml
@@ -93,8 +93,8 @@
<listitem>
<para>
Setting this option to yes or true unloads all firewall-related
- kernel modules when firewalld is stopped. The default value is no
- or false.
+ kernel modules when firewalld is stopped. The default value is yes
+ or true.
</para>
</listitem>
</varlistentry>
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
index 5d6d769fbf15..285e2f034b6b 100644
--- a/src/firewall/config/__init__.py.in
+++ b/src/firewall/config/__init__.py.in
@@ -125,7 +125,7 @@ FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
FALLBACK_ZONE = "public"
FALLBACK_MINIMAL_MARK = 100
FALLBACK_CLEANUP_ON_EXIT = True
-FALLBACK_CLEANUP_MODULES_ON_EXIT = False
+FALLBACK_CLEANUP_MODULES_ON_EXIT = True
FALLBACK_LOCKDOWN = False
FALLBACK_IPV6_RPFILTER = True
FALLBACK_INDIVIDUAL_CALLS = False
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index 4171697bdb94..5cef18b5f889 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -238,6 +238,8 @@ class Firewall(object):
value = self._firewalld_conf.get("CleanupModulesOnExit")
if value is not None and value.lower() in [ "yes", "true" ]:
self.cleanup_modules_on_exit = True
+ if value is not None and value.lower() in [ "no", "false" ]:
+ self.cleanup_modules_on_exit = False
log.debug1("CleanupModulesOnExit is set to '%s'",
self.cleanup_modules_on_exit)
diff --git a/src/tests/dbus/firewalld.conf.at b/src/tests/dbus/firewalld.conf.at
index 9a04a3bd491c..68832bca33bc 100644
--- a/src/tests/dbus/firewalld.conf.at
+++ b/src/tests/dbus/firewalld.conf.at
@@ -17,7 +17,7 @@ dnl Verify defaults over dbus. Should be inline with default firewalld.conf.
DBUS_GETALL([config], [config], 0, [dnl
string "AllowZoneDrifting" : variant string "no"
string "AutomaticHelpers" : variant string "no"
-string "CleanupModulesOnExit" : variant string "no"
+string "CleanupModulesOnExit" : variant string "yes"
string "CleanupOnExit" : variant string "no"
string "DefaultZone" : variant string "public"
string "FirewallBackend" : variant string "nftables"
@@ -46,7 +46,7 @@ _helper([IPv6_rpfilter], [string:"yes"], [variant string "yes"])
_helper([IndividualCalls], [string:"yes"], [variant string "yes"])
_helper([FirewallBackend], [string:"iptables"], [variant string "iptables"])
_helper([FlushAllOnReload], [string:"no"], [variant string "no"])
-_helper([CleanupModulesOnExit], [string:"yes"], [variant string "yes"])
+_helper([CleanupModulesOnExit], [string:"no"], [variant string "no"])
_helper([CleanupOnExit], [string:"yes"], [variant string "yes"])
_helper([RFC3964_IPv4], [string:"no"], [variant string "no"])
_helper([AllowZoneDrifting], [string:"yes"], [variant string "yes"])
--
2.31.1

View File

@ -0,0 +1,140 @@
From 34967402eda57d051b239c1551ecc0259881e7d4 Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Tue, 30 Nov 2021 14:54:20 -0500
Subject: [PATCH 49/51] fix(ipset): reduce cost of entry overlap detection
This increases peak memory usage to reduce the duration it takes to
apply the set entries. Building the list of IPv4Network objects up front
means we don't have to build them multiple times inside the for loop.
Fixes: #881
(cherry picked from commit 7f5b736378c0133f46470c42e0c1fb3b95087de5)
---
src/firewall/client.py | 10 ++++------
src/firewall/core/fw_ipset.py | 9 +++------
src/firewall/core/ipset.py | 27 ++++++++++++++++++++++-----
src/firewall/server/config_ipset.py | 10 ++++------
4 files changed, 33 insertions(+), 23 deletions(-)
diff --git a/src/firewall/client.py b/src/firewall/client.py
index 3715ffd29316..fdc88ac7946b 100644
--- a/src/firewall/client.py
+++ b/src/firewall/client.py
@@ -34,7 +34,8 @@ from firewall.core.base import DEFAULT_ZONE_TARGET, DEFAULT_POLICY_TARGET, DEFAU
from firewall.dbus_utils import dbus_to_python
from firewall.functions import b2u
from firewall.core.rich import Rich_Rule
-from firewall.core.ipset import normalize_ipset_entry, check_entry_overlaps_existing
+from firewall.core.ipset import normalize_ipset_entry, check_entry_overlaps_existing, \
+ check_for_overlapping_entries
from firewall import errors
from firewall.errors import FirewallError
@@ -1617,11 +1618,8 @@ class FirewallClientIPSetSettings(object):
if "timeout" in self.settings[4] and \
self.settings[4]["timeout"] != "0":
raise FirewallError(errors.IPSET_WITH_TIMEOUT)
- _entries = set()
- for _entry in dbus_to_python(entries, list):
- check_entry_overlaps_existing(_entry, _entries)
- _entries.add(normalize_ipset_entry(_entry))
- self.settings[5] = list(_entries)
+ check_for_overlapping_entries(entries)
+ self.settings[5] = entries
@handle_exceptions
def addEntry(self, entry):
if "timeout" in self.settings[4] and \
diff --git a/src/firewall/core/fw_ipset.py b/src/firewall/core/fw_ipset.py
index a285fd4a4aab..d7878c01921e 100644
--- a/src/firewall/core/fw_ipset.py
+++ b/src/firewall/core/fw_ipset.py
@@ -25,7 +25,8 @@ __all__ = [ "FirewallIPSet" ]
from firewall.core.logger import log
from firewall.core.ipset import remove_default_create_options as rm_def_cr_opts, \
- normalize_ipset_entry, check_entry_overlaps_existing
+ normalize_ipset_entry, check_entry_overlaps_existing, \
+ check_for_overlapping_entries
from firewall.core.io.ipset import IPSet
from firewall import errors
from firewall.errors import FirewallError
@@ -244,11 +245,7 @@ class FirewallIPSet(object):
def set_entries(self, name, entries):
obj = self.get_ipset(name, applied=True)
- _entries = set()
- for _entry in entries:
- check_entry_overlaps_existing(_entry, _entries)
- _entries.add(normalize_ipset_entry(_entry))
- entries = list(_entries)
+ check_for_overlapping_entries(entries)
for entry in entries:
IPSet.check_entry(entry, obj.options, obj.type)
diff --git a/src/firewall/core/ipset.py b/src/firewall/core/ipset.py
index d6defa395241..66ea4335536d 100644
--- a/src/firewall/core/ipset.py
+++ b/src/firewall/core/ipset.py
@@ -309,9 +309,26 @@ def check_entry_overlaps_existing(entry, entries):
if len(entry.split(",")) > 1:
return
+ try:
+ entry_network = ipaddress.ip_network(entry, strict=False)
+ except ValueError:
+ # could not parse the new IP address, maybe a MAC
+ return
+
for itr in entries:
- try:
- if ipaddress.ip_network(itr, strict=False).overlaps(ipaddress.ip_network(entry, strict=False)):
- raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps with existing entry '{}'".format(itr, entry))
- except ValueError:
- pass
+ if entry_network.overlaps(ipaddress.ip_network(itr, strict=False)):
+ raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps with existing entry '{}'".format(entry, itr))
+
+def check_for_overlapping_entries(entries):
+ """ Check if any entry overlaps any entry in the list of entries """
+ try:
+ entries = [ipaddress.ip_network(x, strict=False) for x in entries]
+ except ValueError:
+ # at least one entry can not be parsed
+ return
+
+ while entries:
+ entry = entries.pop()
+ for itr in entries:
+ if entry.overlaps(itr):
+ raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps entry '{}'".format(entry, itr))
diff --git a/src/firewall/server/config_ipset.py b/src/firewall/server/config_ipset.py
index f33c2a02926f..499ffcb9227a 100644
--- a/src/firewall/server/config_ipset.py
+++ b/src/firewall/server/config_ipset.py
@@ -34,7 +34,8 @@ from firewall.dbus_utils import dbus_to_python, \
dbus_introspection_add_properties
from firewall.core.io.ipset import IPSet
from firewall.core.ipset import IPSET_TYPES, normalize_ipset_entry, \
- check_entry_overlaps_existing
+ check_entry_overlaps_existing, \
+ check_for_overlapping_entries
from firewall.core.logger import log
from firewall.server.decorators import handle_exceptions, \
dbus_handle_exceptions, dbus_service_method
@@ -407,11 +408,8 @@ class FirewallDConfigIPSet(slip.dbus.service.Object):
in_signature='as')
@dbus_handle_exceptions
def setEntries(self, entries, sender=None):
- _entries = set()
- for _entry in dbus_to_python(entries, list):
- check_entry_overlaps_existing(_entry, _entries)
- _entries.add(normalize_ipset_entry(_entry))
- entries = list(_entries)
+ entries = dbus_to_python(entries, list)
+ check_for_overlapping_entries(entries)
log.debug1("%s.setEntries('[%s]')", self._log_prefix,
",".join(entries))
self.parent.accessCheck(sender)
--
2.31.1

View File

@ -0,0 +1,56 @@
From 344753267f6b40d029a3b690cce74720a355cb4d Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Tue, 30 Nov 2021 14:50:17 -0500
Subject: [PATCH 50/51] test(ipset): huge set of entries benchmark
Coverage: #881
(cherry picked from commit 114936c71ab1b12a5598d06805b7e9e13f7ee190)
---
src/tests/regression/gh881.at | 25 +++++++++++++++++++++++++
src/tests/regression/regression.at | 1 +
2 files changed, 26 insertions(+)
create mode 100644 src/tests/regression/gh881.at
diff --git a/src/tests/regression/gh881.at b/src/tests/regression/gh881.at
new file mode 100644
index 000000000000..c7326805b555
--- /dev/null
+++ b/src/tests/regression/gh881.at
@@ -0,0 +1,25 @@
+FWD_START_TEST([ipset entry overlap detect perf])
+AT_KEYWORDS(ipset gh881)
+
+dnl build a large ipset
+dnl
+AT_DATA([./deny_cidr], [])
+NS_CHECK([sh -c '
+for I in $(seq 10); do
+ for J in $(seq 250); do
+ echo "10.${I}.${J}.0/24" >> ./deny_cidr
+ done
+done
+'])
+
+dnl verify non-overlapping does not error
+dnl
+FWD_CHECK([--permanent --new-ipset=deny_set --type=hash:net --option=family=inet --option=hashsize=16384 --option=maxelem=20000], 0, [ignore])
+NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
+
+dnl verify overlap detection actually detects an overlap
+dnl
+NS_CHECK([echo "10.1.0.0/16" >> ./deny_cidr])
+NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
+
+FWD_END_TEST()
diff --git a/src/tests/regression/regression.at b/src/tests/regression/regression.at
index a20b913fbe59..4045563d0b91 100644
--- a/src/tests/regression/regression.at
+++ b/src/tests/regression/regression.at
@@ -45,3 +45,4 @@ m4_include([regression/rhbz1914935.at])
m4_include([regression/gh696.at])
m4_include([regression/rhbz1917766.at])
m4_include([regression/rhbz2014383.at])
+m4_include([regression/gh881.at])
--
2.31.1

View File

@ -0,0 +1,150 @@
From 33b10b9112f2f51df049315438204efec7a5434c Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Tue, 25 Jan 2022 09:29:32 -0500
Subject: [PATCH 51/51] fix(ipset): further reduce cost of entry overlap
detection
This makes the complexity linear by sorting the networks ahead of time.
Fixes: #881
Fixes: rhbz2043289
(cherry picked from commit 36c170db265265e838a089858be4b20dbbd582eb)
---
src/firewall/core/ipset.py | 59 ++++++++++++++++++++++++++++++++---
src/tests/regression/gh881.at | 42 ++++++++++++++++++++++---
2 files changed, 92 insertions(+), 9 deletions(-)
diff --git a/src/firewall/core/ipset.py b/src/firewall/core/ipset.py
index 66ea4335536d..b160d8345669 100644
--- a/src/firewall/core/ipset.py
+++ b/src/firewall/core/ipset.py
@@ -327,8 +327,57 @@ def check_for_overlapping_entries(entries):
# at least one entry can not be parsed
return
- while entries:
- entry = entries.pop()
- for itr in entries:
- if entry.overlaps(itr):
- raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps entry '{}'".format(entry, itr))
+ # We can take advantage of some facts of IPv4Network/IPv6Network and
+ # how Python sorts the networks to quickly detect overlaps.
+ #
+ # Facts:
+ #
+ # 1. IPv{4,6}Network are normalized to remove host bits, e.g.
+ # 10.1.1.0/16 will become 10.1.0.0/16.
+ #
+ # 2. IPv{4,6}Network objects are sorted by:
+ # a. IP address (network bits)
+ # then
+ # b. netmask (significant bits count)
+ #
+ # Because of the above we have these properties:
+ #
+ # 1. big networks (netA) are sorted before smaller networks (netB)
+ # that overlap the big network (netA)
+ # - e.g. 10.1.128.0/17 (netA) sorts before 10.1.129.0/24 (netB)
+ # 2. same value addresses (network bits) are grouped together even
+ # if the number of network bits vary. e.g. /16 vs /24
+ # - recall that address are normalized to remove host bits
+ # - e.g. 10.1.128.0/17 (netA) sorts before 10.1.128.0/24 (netC)
+ # 3. non-overlapping networks (netD, netE) are always sorted before or
+ # after networks that overlap (netB, netC) the current one (netA)
+ # - e.g. 10.1.128.0/17 (netA) sorts before 10.2.128.0/16 (netD)
+ # - e.g. 10.1.128.0/17 (netA) sorts after 9.1.128.0/17 (netE)
+ # - e.g. 9.1.128.0/17 (netE) sorts before 10.1.129.0/24 (netB)
+ #
+ # With this we know the sorted list looks like:
+ #
+ # list: [ netE, netA, netB, netC, netD ]
+ #
+ # netE = non-overlapping network
+ # netA = big network
+ # netB = smaller network that overlaps netA (subnet)
+ # netC = smaller network that overlaps netA (subnet)
+ # netD = non-overlapping network
+ #
+ # If networks netB and netC exist in the list, they overlap and are
+ # adjacent to netA.
+ #
+ # Checking for overlaps on a sorted list is thus:
+ #
+ # 1. compare adjacent elements in the list for overlaps
+ #
+ # Recall that we only need to detect a single overlap. We do not need to
+ # detect them all.
+ #
+ entries.sort()
+ prev_network = entries.pop(0)
+ for current_network in entries:
+ if prev_network.overlaps(current_network):
+ raise FirewallError(errors.INVALID_ENTRY, "Entry '{}' overlaps entry '{}'".format(prev_network, current_network))
+ prev_network = current_network
diff --git a/src/tests/regression/gh881.at b/src/tests/regression/gh881.at
index c7326805b555..a5cf7e4eb912 100644
--- a/src/tests/regression/gh881.at
+++ b/src/tests/regression/gh881.at
@@ -5,21 +5,55 @@ dnl build a large ipset
dnl
AT_DATA([./deny_cidr], [])
NS_CHECK([sh -c '
-for I in $(seq 10); do
+for I in $(seq 250); do
for J in $(seq 250); do
echo "10.${I}.${J}.0/24" >> ./deny_cidr
done
done
'])
+NS_CHECK([echo "10.254.0.0/16" >> ./deny_cidr])
dnl verify non-overlapping does not error
dnl
FWD_CHECK([--permanent --new-ipset=deny_set --type=hash:net --option=family=inet --option=hashsize=16384 --option=maxelem=20000], 0, [ignore])
-NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
+
+dnl still no overlap
+dnl
+AT_DATA([./deny_cidr], [
+9.0.0.0/8
+11.1.0.0/16
+])
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
dnl verify overlap detection actually detects an overlap
dnl
-NS_CHECK([echo "10.1.0.0/16" >> ./deny_cidr])
-NS_CHECK([time timeout 300 firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
+AT_DATA([./deny_cidr], [
+10.1.0.0/16
+10.2.0.0/16
+10.250.0.0/16
+])
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
+
+AT_DATA([./deny_cidr], [
+10.253.0.0/16
+10.253.128.0/17
+])
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
+
+AT_DATA([./deny_cidr], [
+10.1.1.1/32
+])
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
+
+AT_DATA([./deny_cidr], [
+10.0.0.0/8
+10.0.0.0/25
+])
+NS_CHECK([time firewall-cmd --permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 136, [ignore], [ignore])
+
+dnl empty file, no additions, but previous ones will remain
+AT_DATA([./deny_cidr], [])
+FWD_CHECK([--permanent --ipset=deny_set --add-entries-from-file=./deny_cidr], 0, [ignore], [ignore])
FWD_END_TEST()
--
2.31.1

View File

@ -1,7 +1,7 @@
Summary: A firewall daemon with D-Bus interface providing a dynamic firewall Summary: A firewall daemon with D-Bus interface providing a dynamic firewall
Name: firewalld Name: firewalld
Version: 0.9.3 Version: 0.9.3
Release: 11%{?dist} Release: 13%{?dist}
URL: http://www.firewalld.org URL: http://www.firewalld.org
License: GPLv2+ License: GPLv2+
Source0: https://github.com/firewalld/firewalld/releases/download/v%{version}/firewalld-%{version}.tar.gz Source0: https://github.com/firewalld/firewalld/releases/download/v%{version}/firewalld-%{version}.tar.gz
@ -51,6 +51,11 @@ Patch43: 0043-fix-fw_config-zone-on-rename-remove-then-add.patch
Patch44: 0044-fix-io-functions-check_config-against-on-disk-conf.patch Patch44: 0044-fix-io-functions-check_config-against-on-disk-conf.patch
Patch45: 0045-fix-zone-detect-same-source-interface-in-zones.patch Patch45: 0045-fix-zone-detect-same-source-interface-in-zones.patch
Patch46: 0046-test-zone-detect-same-source-interface-in-zones.patch Patch46: 0046-test-zone-detect-same-source-interface-in-zones.patch
Patch47: 0047-feat-config-add-CleanupModulesOnExit-configuration-o.patch
Patch48: 0048-RHEL-only-default-to-CleanupModulesOnExit-yes.patch
Patch49: v1.0.0-0049-fix-ipset-reduce-cost-of-entry-overlap-detection.patch
Patch50: v1.0.0-0050-test-ipset-huge-set-of-entries-benchmark.patch
Patch51: v1.0.0-0051-fix-ipset-further-reduce-cost-of-entry-overlap-detec.patch
BuildArch: noarch BuildArch: noarch
BuildRequires: autoconf BuildRequires: autoconf
@ -252,6 +257,13 @@ desktop-file-install --delete-original \
%{_mandir}/man1/firewall-config*.1* %{_mandir}/man1/firewall-config*.1*
%changelog %changelog
* Thu Feb 03 2022 Eric Garver <egarver@redhat.com> - 0.9.3-13
- change default CleanupModulesOnExit=yes
* Mon Dec 20 2021 Eric Garver <egarver@redhat.com> - 0.9.3-12
- feat(config): add CleanupModulesOnExit configuration option
- change default CleanupModulesOnExit=yes
* Tue Nov 16 2021 Eric Garver <egarver@redhat.com> - 0.9.3-11 * Tue Nov 16 2021 Eric Garver <egarver@redhat.com> - 0.9.3-11
- fix(zone): detect same source/interface in zones - fix(zone): detect same source/interface in zones