firewalld/SOURCES/0025-v2.2.0-improvement-rich-add-Rich_Limit.value_parse-a.patch

190 lines
7.3 KiB
Diff

From 2844fedea7b0c65d864f9960b513150c4468adb2 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 13 Dec 2023 19:42:37 +0100
Subject: [PATCH 25/26] v2.2.0: improvement(rich): add Rich_Limit.value_parse()
and normalize value
Instead of duplicating the parsing, add a Rich_Limit.value_parse()
function that can be used to "understand" the value string.
Note that already previously, Rich_Limit.__init__() would normalize the
value (e.g. modify "/minute" to "/m"). Go one step further with this.
Now parse and stringify the value, so that it is normalized. Invalid
values are left unnormalized, and Rich_Limit.__init__() still does not
fail the operation (like before). For that we have check().
This normalization matters. For example, the parser is (rightfully)
graceful and will accept 'limit value="1 /m"'. If we add two rules
that are identical, except for the white space, we want that the
normalize string is identical. That's useful, because the normalized
string of a rule is used as identity for the rule.
(cherry picked from commit 8d2f9502db98b349cabf76bb9c0a303fe6e3512a)
---
src/firewall-config.in | 6 +--
src/firewall/core/nftables.py | 9 ++---
src/firewall/core/rich.py | 76 ++++++++++++++++++++++-------------
3 files changed, 53 insertions(+), 38 deletions(-)
diff --git a/src/firewall-config.in b/src/firewall-config.in
index f91e945ca7de..e4fbb029ac6d 100755
--- a/src/firewall-config.in
+++ b/src/firewall-config.in
@@ -3245,7 +3245,7 @@ class FirewallConfig(object):
if old_obj.action.limit:
self.richRuleDialogActionLimitCheck.set_active(True)
- (rate, duration) = old_obj.action.limit.value.split("/")
+ (rate, duration) = old_obj.action.limit.value_parse()
self.richRuleDialogActionLimitRateEntry.set_text(rate)
combobox_select_text( \
self.richRuleDialogActionLimitDurationCombobox,
@@ -3288,7 +3288,7 @@ class FirewallConfig(object):
loglevel[log_level])
if old_obj.log.limit:
self.richRuleDialogLogLimitCheck.set_active(True)
- (rate, duration) = old_obj.log.limit.value.split("/")
+ (rate, duration) = old_obj.log.limit.value_parse()
self.richRuleDialogLogLimitRateEntry.set_text(rate)
combobox_select_text( \
self.richRuleDialogLogLimitDurationCombobox,
@@ -3299,7 +3299,7 @@ class FirewallConfig(object):
self.richRuleDialogAuditCheck.set_active(True)
if old_obj.audit.limit:
self.richRuleDialogAuditLimitCheck.set_active(True)
- (rate, duration) = old_obj.audit.limit.value.split("/")
+ (rate, duration) = old_obj.audit.limit.value_parse()
self.richRuleDialogAuditLimitRateEntry.set_text(rate)
combobox_select_text( \
self.richRuleDialogAuditLimitDurationCombobox,
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
index 67fb6457e86c..f24095ce729c 100644
--- a/src/firewall/core/nftables.py
+++ b/src/firewall/core/nftables.py
@@ -1071,13 +1071,10 @@ class nftables(object):
"d" : "day",
}
- try:
- i = limit.value.index("/")
- except ValueError:
- raise FirewallError(INVALID_RULE, "Expected '/' in limit")
+ rate, duration = limit.value_parse()
- return {"limit": {"rate": int(limit.value[0:i]),
- "per": rich_to_nft[limit.value[i+1]]}}
+ return {"limit": {"rate": rate,
+ "per": rich_to_nft[duration]}}
def _rich_rule_chain_suffix(self, rich_rule):
if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock]:
diff --git a/src/firewall/core/rich.py b/src/firewall/core/rich.py
index b150a0dca402..a77f2b4aa495 100644
--- a/src/firewall/core/rich.py
+++ b/src/firewall/core/rich.py
@@ -230,54 +230,72 @@ class Rich_Mark(object):
# value is uint32
raise FirewallError(errors.INVALID_MARK, x)
+DURATION_TO_MULT = {
+ "s": 1,
+ "m": 60,
+ "h": 60 * 60,
+ "d": 24 * 60 * 60,
+}
+
class Rich_Limit(object):
def __init__(self, value):
self.value = value
- if "/" in self.value:
- splits = self.value.split("/")
- if len(splits) == 2 and \
- splits[1] in [ "second", "minute", "hour", "day" ]:
- self.value = "%s/%s" % (splits[0], splits[1][:1])
def check(self):
+ self.value_parse()
+
+ @property
+ def value(self):
+ return self._value
+
+ @value.setter
+ def value(self, value):
+ if value is None:
+ self._value = None
+ return
+ try:
+ rate, duration = self._value_parse(value)
+ except FirewallError:
+ # The value is invalid. We cannot normalize it.
+ v = value
+ else:
+ v = f"{rate}/{duration}"
+ if getattr(self, "_value", None) != v:
+ self._value = v
+
+ @staticmethod
+ def _value_parse(value):
splits = None
- if "/" in self.value:
- splits = self.value.split("/")
+ if "/" in value:
+ splits = value.split("/")
if not splits or len(splits) != 2:
- raise FirewallError(errors.INVALID_LIMIT, self.value)
+ raise FirewallError(errors.INVALID_LIMIT, value)
(rate, duration) = splits
try:
rate = int(rate)
except:
- raise FirewallError(errors.INVALID_LIMIT, self.value)
+ raise FirewallError(errors.INVALID_LIMIT, value)
- if rate < 1 or duration not in [ "s", "m", "h", "d" ]:
- raise FirewallError(errors.INVALID_LIMIT, self.value)
+ if duration in ["second", "minute", "hour", "day"]:
+ duration = duration[:1]
- mult = 1
- if duration == "s":
- mult = 1
- elif duration == "m":
- mult = 60
- elif duration == "h":
- mult = 60*60
- elif duration == "d":
- mult = 24*60*60
+ if rate < 1 or duration not in ["s", "m", "h", "d"]:
+ raise FirewallError(errors.INVALID_LIMIT, value)
- if 10000 * mult // rate == 0:
- raise FirewallError(errors.INVALID_LIMIT,
- "%s too fast" % self.value)
+ if 10000 * DURATION_TO_MULT[duration] // rate == 0:
+ raise FirewallError(errors.INVALID_LIMIT, "%s too fast" % (value,))
if rate == 1 and duration == "d":
# iptables (v1.4.21) doesn't accept 1/d
- raise FirewallError(errors.INVALID_LIMIT,
- "%s too slow" % self.value)
+ raise FirewallError(errors.INVALID_LIMIT, "%s too slow" % (value,))
- def __str__(self):
- return 'limit value="%s"' % (self.value)
+ return rate, duration
- def command(self):
- return ''
+ def value_parse(self):
+ return self._value_parse(self._value)
+
+ def __str__(self):
+ return f'limit value="{self._value}"'
class Rich_Rule(object):
priority_min = -32768
--
2.43.0