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