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