|
|
21c891 |
From 9f998164b87cd00dfb7a5750b898a2e17359c31c Mon Sep 17 00:00:00 2001
|
|
|
21c891 |
From: Eric Garver <e@erig.me>
|
|
|
21c891 |
Date: Fri, 19 Oct 2018 09:22:04 -0400
|
|
|
21c891 |
Subject: [PATCH 24/34] nftables: support rich rule priorities
|
|
|
21c891 |
|
|
|
21c891 |
Fixes: #149
|
|
|
21c891 |
Fixes: #224
|
|
|
21c891 |
(cherry picked from commit 25e9a62532d6395ff13665db02aabaa010d6fca5)
|
|
|
21c891 |
---
|
|
|
21c891 |
src/firewall/core/nftables.py | 214 +++++++++++++++++++++++++++-------
|
|
|
21c891 |
1 file changed, 174 insertions(+), 40 deletions(-)
|
|
|
21c891 |
|
|
|
21c891 |
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
|
|
|
21c891 |
index a763ed3ec103..d59bc55bf1a5 100644
|
|
|
21c891 |
--- a/src/firewall/core/nftables.py
|
|
|
21c891 |
+++ b/src/firewall/core/nftables.py
|
|
|
21c891 |
@@ -20,6 +20,7 @@
|
|
|
21c891 |
#
|
|
|
21c891 |
|
|
|
21c891 |
import os.path
|
|
|
21c891 |
+import copy
|
|
|
21c891 |
|
|
|
21c891 |
from firewall.core.base import SHORTCUTS, DEFAULT_ZONE_TARGET
|
|
|
21c891 |
from firewall.core.prog import runProg
|
|
|
21c891 |
@@ -29,7 +30,8 @@ from firewall.functions import splitArgs, check_mac, portStr, \
|
|
|
21c891 |
from firewall import config
|
|
|
21c891 |
from firewall.errors import FirewallError, UNKNOWN_ERROR, INVALID_RULE, \
|
|
|
21c891 |
INVALID_ICMPTYPE, INVALID_TYPE, INVALID_ENTRY
|
|
|
21c891 |
-from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark
|
|
|
21c891 |
+from firewall.core.rich import Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark, \
|
|
|
21c891 |
+ Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock
|
|
|
21c891 |
|
|
|
21c891 |
TABLE_NAME = "firewalld"
|
|
|
21c891 |
|
|
|
21c891 |
@@ -160,6 +162,7 @@ class nftables(object):
|
|
|
21c891 |
self.available_tables = []
|
|
|
21c891 |
self.rule_to_handle = {}
|
|
|
21c891 |
self.rule_ref_count = {}
|
|
|
21c891 |
+ self.rich_rule_priority_counts = {}
|
|
|
21c891 |
|
|
|
21c891 |
def fill_exists(self):
|
|
|
21c891 |
self.command_exists = os.path.exists(self._command)
|
|
|
21c891 |
@@ -171,18 +174,11 @@ class nftables(object):
|
|
|
21c891 |
|
|
|
21c891 |
def rule_key_from_rule(rule):
|
|
|
21c891 |
rule_key = rule[2:]
|
|
|
21c891 |
- if rule_key[3] in ["position", "handle", "index"]:
|
|
|
21c891 |
- # strip "position #"
|
|
|
21c891 |
- # "insert rule family table chain position <num>"
|
|
|
21c891 |
- # ^^ rule_key starts here
|
|
|
21c891 |
- try:
|
|
|
21c891 |
- int(rule_key[4])
|
|
|
21c891 |
- except Exception:
|
|
|
21c891 |
- raise FirewallError(INVALID_RULE, "position without a number")
|
|
|
21c891 |
- else:
|
|
|
21c891 |
- rule_key.pop(3)
|
|
|
21c891 |
- rule_key.pop(3)
|
|
|
21c891 |
- return " ".join(rule_key)
|
|
|
21c891 |
+ # "insert rule family table chain index <num>"
|
|
|
21c891 |
+ # ^^ rule_key starts here
|
|
|
21c891 |
+ if rule_key[3] in ["position", "handle"]:
|
|
|
21c891 |
+ raise FirewallError(INVALID_RULE, "position/handle not allowed in rule")
|
|
|
21c891 |
+ return " ".join([str(x) for x in rule_key])
|
|
|
21c891 |
|
|
|
21c891 |
# If we're deleting a table (i.e. build_flush_rules())
|
|
|
21c891 |
# then check if its exist first to avoid nft throwing an error
|
|
|
21c891 |
@@ -200,11 +196,6 @@ class nftables(object):
|
|
|
21c891 |
elif _args[0] in ["delete"] and _args[1] == "rule":
|
|
|
21c891 |
rule_add = False
|
|
|
21c891 |
rule_key = rule_key_from_rule(_args)
|
|
|
21c891 |
- # delete using rule handle
|
|
|
21c891 |
- _args = ["delete", "rule"] + _args[2:5] + \
|
|
|
21c891 |
- ["handle", self.rule_to_handle[rule_key]]
|
|
|
21c891 |
-
|
|
|
21c891 |
- _args_str = " ".join(_args)
|
|
|
21c891 |
|
|
|
21c891 |
# rule deduplication
|
|
|
21c891 |
if rule_key in self.rule_ref_count:
|
|
|
21c891 |
@@ -220,22 +211,82 @@ class nftables(object):
|
|
|
21c891 |
raise FirewallError(UNKNOWN_ERROR, "rule ref count bug: rule_key '%s', cnt %d"
|
|
|
21c891 |
% (rule_key, self.rule_ref_count[rule_key]))
|
|
|
21c891 |
log.debug2("%s: rule ref cnt %d, %s %s", self.__class__,
|
|
|
21c891 |
- self.rule_ref_count[rule_key], self._command, _args_str)
|
|
|
21c891 |
+ self.rule_ref_count[rule_key], self._command,
|
|
|
21c891 |
+ " ".join([str(x) for x in _args]))
|
|
|
21c891 |
+
|
|
|
21c891 |
+ # replace %%RICH_RULE_PRIORITY%%
|
|
|
21c891 |
+ if rule_key:
|
|
|
21c891 |
+ rich_rule_priority_counts = self.rich_rule_priority_counts
|
|
|
21c891 |
+ try:
|
|
|
21c891 |
+ i = _args.index("%%RICH_RULE_PRIORITY%%")
|
|
|
21c891 |
+ except ValueError:
|
|
|
21c891 |
+ pass
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ rich_rule_priority_counts = copy.deepcopy(self.rich_rule_priority_counts)
|
|
|
21c891 |
+ _args.pop(i)
|
|
|
21c891 |
+ priority = _args.pop(i)
|
|
|
21c891 |
+ if type(priority) != int:
|
|
|
21c891 |
+ raise FirewallError(INVALID_RULE, "rich rule priority must be followed by a number")
|
|
|
21c891 |
+ chain = (_args[2], _args[4]) # family, chain
|
|
|
21c891 |
+ # Add the rule to the priority counts. We don't need to store the
|
|
|
21c891 |
+ # rule, just bump the ref count for the priority value.
|
|
|
21c891 |
+ if not rule_add:
|
|
|
21c891 |
+ if chain not in rich_rule_priority_counts or \
|
|
|
21c891 |
+ priority not in rich_rule_priority_counts[chain] or \
|
|
|
21c891 |
+ rich_rule_priority_counts[chain][priority] <= 0:
|
|
|
21c891 |
+ raise FirewallError(UNKNOWN_ERROR, "nonexistent or underflow of rich rule priority count")
|
|
|
21c891 |
+
|
|
|
21c891 |
+ rich_rule_priority_counts[chain][priority] -= 1
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ if chain not in rich_rule_priority_counts:
|
|
|
21c891 |
+ rich_rule_priority_counts[chain] = {}
|
|
|
21c891 |
+ if priority not in rich_rule_priority_counts[chain]:
|
|
|
21c891 |
+ rich_rule_priority_counts[chain][priority] = 0
|
|
|
21c891 |
+
|
|
|
21c891 |
+ # calculate index of new rule
|
|
|
21c891 |
+ index = 0
|
|
|
21c891 |
+ for p in sorted(rich_rule_priority_counts[chain].keys()):
|
|
|
21c891 |
+ if p == priority and _args[0] == "insert":
|
|
|
21c891 |
+ break
|
|
|
21c891 |
+ index += rich_rule_priority_counts[chain][p]
|
|
|
21c891 |
+ if p == priority and _args[0] == "add":
|
|
|
21c891 |
+ break
|
|
|
21c891 |
+
|
|
|
21c891 |
+ rich_rule_priority_counts[chain][priority] += 1
|
|
|
21c891 |
+
|
|
|
21c891 |
+ if index == 0:
|
|
|
21c891 |
+ _args[0] = "insert"
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ index -= 1 # point to the rule before insertion point
|
|
|
21c891 |
+ _args[0] = "add"
|
|
|
21c891 |
+ _args.insert(i, "index")
|
|
|
21c891 |
+ _args.insert(i+1, "%d" % index)
|
|
|
21c891 |
|
|
|
21c891 |
if not rule_key or (not rule_add and self.rule_ref_count[rule_key] == 0) \
|
|
|
21c891 |
or ( rule_add and rule_key not in self.rule_ref_count):
|
|
|
21c891 |
+
|
|
|
21c891 |
+ # delete using rule handle
|
|
|
21c891 |
+ if rule_key and not rule_add:
|
|
|
21c891 |
+ _args = ["delete", "rule"] + _args[2:5] + \
|
|
|
21c891 |
+ ["handle", self.rule_to_handle[rule_key]]
|
|
|
21c891 |
+
|
|
|
21c891 |
+ _args_str = " ".join(_args)
|
|
|
21c891 |
log.debug2("%s: %s %s", self.__class__, self._command, _args_str)
|
|
|
21c891 |
(status, output) = runProg(self._command, nft_opts + _args)
|
|
|
21c891 |
if status != 0:
|
|
|
21c891 |
raise ValueError("'%s %s' failed: %s" % (self._command,
|
|
|
21c891 |
_args_str, output))
|
|
|
21c891 |
+
|
|
|
21c891 |
+ if rule_key:
|
|
|
21c891 |
+ self.rich_rule_priority_counts = rich_rule_priority_counts
|
|
|
21c891 |
+
|
|
|
21c891 |
# nft requires deleting rules by handle. So we must cache the rule
|
|
|
21c891 |
# handle when adding/inserting rules.
|
|
|
21c891 |
#
|
|
|
21c891 |
if rule_key:
|
|
|
21c891 |
if rule_add:
|
|
|
21c891 |
- str = "# handle "
|
|
|
21c891 |
- offset = output.index(str) + len(str)
|
|
|
21c891 |
+ handle_str = "# handle "
|
|
|
21c891 |
+ offset = output.index(handle_str) + len(handle_str)
|
|
|
21c891 |
self.rule_to_handle[rule_key] = output[offset:].strip()
|
|
|
21c891 |
self.rule_ref_count[rule_key] = 1
|
|
|
21c891 |
else:
|
|
|
21c891 |
@@ -305,6 +356,7 @@ class nftables(object):
|
|
|
21c891 |
def build_flush_rules(self):
|
|
|
21c891 |
self.rule_to_handle = {}
|
|
|
21c891 |
self.rule_ref_count = {}
|
|
|
21c891 |
+ self.rich_rule_priority_counts = {}
|
|
|
21c891 |
|
|
|
21c891 |
rules = []
|
|
|
21c891 |
for family in OUR_CHAINS.keys():
|
|
|
21c891 |
@@ -557,18 +609,27 @@ class nftables(object):
|
|
|
21c891 |
OUR_CHAINS[family][table].update(set([_zone,
|
|
|
21c891 |
"%s_log" % _zone,
|
|
|
21c891 |
"%s_deny" % _zone,
|
|
|
21c891 |
+ "%s_rich_rule_pre" % _zone,
|
|
|
21c891 |
+ "%s_rich_rule_post" % _zone,
|
|
|
21c891 |
"%s_allow" % _zone]))
|
|
|
21c891 |
|
|
|
21c891 |
rules = []
|
|
|
21c891 |
rules.append(["add", "chain", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
"%s_%s" % (table, _zone)])
|
|
|
21c891 |
+ rules.append(["add", "chain", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
+ "%s_%s_rich_rule_pre" % (table, _zone)])
|
|
|
21c891 |
rules.append(["add", "chain", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
"%s_%s_log" % (table, _zone)])
|
|
|
21c891 |
rules.append(["add", "chain", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
"%s_%s_deny" % (table, _zone)])
|
|
|
21c891 |
rules.append(["add", "chain", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
"%s_%s_allow" % (table, _zone)])
|
|
|
21c891 |
+ rules.append(["add", "chain", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
+ "%s_%s_rich_rule_post" % (table, _zone)])
|
|
|
21c891 |
|
|
|
21c891 |
+ rules.append(["add", "rule", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
+ "%s_%s" % (table, _zone),
|
|
|
21c891 |
+ "jump", "%s_%s_rich_rule_pre" % (table, _zone)])
|
|
|
21c891 |
rules.append(["add", "rule", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
"%s_%s" % (table, _zone),
|
|
|
21c891 |
"jump", "%s_%s_log" % (table, _zone)])
|
|
|
21c891 |
@@ -578,6 +639,9 @@ class nftables(object):
|
|
|
21c891 |
rules.append(["add", "rule", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
"%s_%s" % (table, _zone),
|
|
|
21c891 |
"jump", "%s_%s_allow" % (table, _zone)])
|
|
|
21c891 |
+ rules.append(["add", "rule", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
+ "%s_%s" % (table, _zone),
|
|
|
21c891 |
+ "jump", "%s_%s_rich_rule_post" % (table, _zone)])
|
|
|
21c891 |
|
|
|
21c891 |
target = self._fw.zone._zones[zone].target
|
|
|
21c891 |
|
|
|
21c891 |
@@ -659,14 +723,54 @@ class nftables(object):
|
|
|
21c891 |
return ["limit", "rate", limit.value[0:i], "/",
|
|
|
21c891 |
rich_to_nft[limit.value[i+1]]]
|
|
|
21c891 |
|
|
|
21c891 |
+ def _rich_rule_chain_suffix(self, rich_rule):
|
|
|
21c891 |
+ if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort, Rich_IcmpBlock]:
|
|
|
21c891 |
+ # These are special and don't have an explicit action
|
|
|
21c891 |
+ pass
|
|
|
21c891 |
+ elif rich_rule.action:
|
|
|
21c891 |
+ if type(rich_rule.action) not in [Rich_Accept, Rich_Reject, Rich_Drop, Rich_Mark]:
|
|
|
21c891 |
+ raise FirewallError(INVALID_RULE, "Unknown action %s" % type(rich_rule.action))
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ raise FirewallError(INVALID_RULE, "No rule action specified.")
|
|
|
21c891 |
+
|
|
|
21c891 |
+ if rich_rule.priority == 0:
|
|
|
21c891 |
+ if type(rich_rule.element) in [Rich_Masquerade, Rich_ForwardPort] or \
|
|
|
21c891 |
+ type(rich_rule.action) in [Rich_Accept, Rich_Mark]:
|
|
|
21c891 |
+ return "allow"
|
|
|
21c891 |
+ elif type(rich_rule.element) in [Rich_IcmpBlock] or \
|
|
|
21c891 |
+ type(rich_rule.action) in [Rich_Reject, Rich_Drop]:
|
|
|
21c891 |
+ return "deny"
|
|
|
21c891 |
+ elif rich_rule.priority < 0:
|
|
|
21c891 |
+ return "rich_rule_pre"
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ return "rich_rule_post"
|
|
|
21c891 |
+
|
|
|
21c891 |
+ def _rich_rule_chain_suffix_from_log(self, rich_rule):
|
|
|
21c891 |
+ if not rich_rule.log and not rich_rule.audit:
|
|
|
21c891 |
+ raise FirewallError(INVALID_RULE, "Not log or audit")
|
|
|
21c891 |
+
|
|
|
21c891 |
+ if rich_rule.priority == 0:
|
|
|
21c891 |
+ return "log"
|
|
|
21c891 |
+ elif rich_rule.priority < 0:
|
|
|
21c891 |
+ return "rich_rule_pre"
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ return "rich_rule_post"
|
|
|
21c891 |
+
|
|
|
21c891 |
+ def _rich_rule_priority_fragment(self, rich_rule):
|
|
|
21c891 |
+ if rich_rule.priority == 0:
|
|
|
21c891 |
+ return []
|
|
|
21c891 |
+ return ["%%RICH_RULE_PRIORITY%%", rich_rule.priority]
|
|
|
21c891 |
+
|
|
|
21c891 |
def _rich_rule_log(self, rich_rule, enable, table, target, rule_fragment):
|
|
|
21c891 |
if not rich_rule.log:
|
|
|
21c891 |
return []
|
|
|
21c891 |
|
|
|
21c891 |
add_del = { True: "add", False: "delete" }[enable]
|
|
|
21c891 |
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule)
|
|
|
21c891 |
rule = [add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "%s_%s_log" % (table, target)]
|
|
|
21c891 |
+ "%s_%s_%s" % (table, target, chain_suffix)]
|
|
|
21c891 |
+ rule += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
rule += rule_fragment + ["log"]
|
|
|
21c891 |
if rich_rule.log.prefix:
|
|
|
21c891 |
rule += ["prefix", "\"%s\"" % rich_rule.log.prefix]
|
|
|
21c891 |
@@ -682,8 +786,10 @@ class nftables(object):
|
|
|
21c891 |
|
|
|
21c891 |
add_del = { True: "add", False: "delete" }[enable]
|
|
|
21c891 |
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix_from_log(rich_rule)
|
|
|
21c891 |
rule = [add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "%s_%s_log" % (table, target)]
|
|
|
21c891 |
+ "%s_%s_%s" % (table, target, chain_suffix)]
|
|
|
21c891 |
+ rule += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
rule += rule_fragment + ["log", "level", "audit"]
|
|
|
21c891 |
rule += self._rich_rule_limit_fragment(rich_rule.audit.limit)
|
|
|
21c891 |
|
|
|
21c891 |
@@ -695,28 +801,28 @@ class nftables(object):
|
|
|
21c891 |
|
|
|
21c891 |
add_del = { True: "add", False: "delete" }[enable]
|
|
|
21c891 |
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
|
21c891 |
+ chain = "%s_%s_%s" % (table, target, chain_suffix)
|
|
|
21c891 |
if type(rich_rule.action) == Rich_Accept:
|
|
|
21c891 |
- chain = "%s_%s_allow" % (table, target)
|
|
|
21c891 |
rule_action = ["accept"]
|
|
|
21c891 |
elif type(rich_rule.action) == Rich_Reject:
|
|
|
21c891 |
- chain = "%s_%s_deny" % (table, target)
|
|
|
21c891 |
rule_action = ["reject"]
|
|
|
21c891 |
if rich_rule.action.type:
|
|
|
21c891 |
rule_action += self._reject_types_fragment(rich_rule.action.type)
|
|
|
21c891 |
elif type(rich_rule.action) == Rich_Drop:
|
|
|
21c891 |
- chain = "%s_%s_deny" % (table, target)
|
|
|
21c891 |
rule_action = ["drop"]
|
|
|
21c891 |
elif type(rich_rule.action) == Rich_Mark:
|
|
|
21c891 |
target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
|
|
|
21c891 |
zone=zone)
|
|
|
21c891 |
table = "mangle"
|
|
|
21c891 |
- chain = "%s_%s_allow" % (table, target)
|
|
|
21c891 |
+ chain = "%s_%s_%s" % (table, target, chain_suffix)
|
|
|
21c891 |
rule_action = ["meta", "mark", "set", rich_rule.action.set]
|
|
|
21c891 |
else:
|
|
|
21c891 |
raise FirewallError(INVALID_RULE,
|
|
|
21c891 |
"Unknown action %s" % type(rich_rule.action))
|
|
|
21c891 |
|
|
|
21c891 |
rule = [add_del, "rule", "inet", "%s" % TABLE_NAME, chain]
|
|
|
21c891 |
+ rule += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
rule += rule_fragment
|
|
|
21c891 |
rule += self._rich_rule_limit_fragment(rich_rule.action.limit)
|
|
|
21c891 |
rule += rule_action
|
|
|
21c891 |
@@ -902,11 +1008,15 @@ class nftables(object):
|
|
|
21c891 |
|
|
|
21c891 |
rule_fragment = []
|
|
|
21c891 |
if rich_rule:
|
|
|
21c891 |
+ rule_fragment += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
|
|
|
21c891 |
rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ chain_suffix = "allow"
|
|
|
21c891 |
|
|
|
21c891 |
return [[add_del, "rule", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
- "nat_%s_allow" % (target)]
|
|
|
21c891 |
+ "nat_%s_%s" % (target, chain_suffix)]
|
|
|
21c891 |
+ rule_fragment + ["oifname", "!=", "lo", "masquerade"]]
|
|
|
21c891 |
|
|
|
21c891 |
def build_zone_masquerade_rules(self, enable, zone, rich_rule=None):
|
|
|
21c891 |
@@ -928,18 +1038,22 @@ class nftables(object):
|
|
|
21c891 |
|
|
|
21c891 |
rule_fragment = []
|
|
|
21c891 |
if rich_rule:
|
|
|
21c891 |
+ rule_fragment += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
|
|
|
21c891 |
rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ chain_suffix = "allow"
|
|
|
21c891 |
|
|
|
21c891 |
rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "filter_%s_allow" % (target)]
|
|
|
21c891 |
+ "filter_%s_%s" % (target, chain_suffix)]
|
|
|
21c891 |
+ rule_fragment + ["ct", "state", "new,untracked", "accept"])
|
|
|
21c891 |
|
|
|
21c891 |
return rules
|
|
|
21c891 |
|
|
|
21c891 |
def _build_zone_forward_port_nat_rules(self, enable, zone, protocol,
|
|
|
21c891 |
mark_fragment, toaddr, toport,
|
|
|
21c891 |
- family):
|
|
|
21c891 |
+ family, rich_rule=None):
|
|
|
21c891 |
add_del = { True: "add", False: "delete" }[enable]
|
|
|
21c891 |
target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS["PREROUTING"],
|
|
|
21c891 |
zone=zone)
|
|
|
21c891 |
@@ -953,8 +1067,17 @@ class nftables(object):
|
|
|
21c891 |
if toport and toport != "":
|
|
|
21c891 |
dnat_fragment += [":%s" % portStr(toport, "-")]
|
|
|
21c891 |
|
|
|
21c891 |
+ rich_rule_priority_fragment = []
|
|
|
21c891 |
+ if rich_rule:
|
|
|
21c891 |
+ rich_rule_priority_fragment += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ chain_suffix = "allow"
|
|
|
21c891 |
+
|
|
|
21c891 |
return [[add_del, "rule", family, "%s" % TABLE_NAME,
|
|
|
21c891 |
- "nat_%s_allow" % (target), "meta", "l4proto", protocol]
|
|
|
21c891 |
+ "nat_%s_%s" % (target, chain_suffix)]
|
|
|
21c891 |
+ + rich_rule_priority_fragment +
|
|
|
21c891 |
+ ["meta", "l4proto", protocol]
|
|
|
21c891 |
+ mark_fragment + dnat_fragment]
|
|
|
21c891 |
|
|
|
21c891 |
def build_zone_forward_port_rules(self, enable, zone, filter_chain, port,
|
|
|
21c891 |
@@ -968,36 +1091,45 @@ class nftables(object):
|
|
|
21c891 |
zone=zone)
|
|
|
21c891 |
rule_fragment = []
|
|
|
21c891 |
if rich_rule:
|
|
|
21c891 |
+ rule_fragment += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
rule_fragment += self._rich_rule_family_fragment(rich_rule.family)
|
|
|
21c891 |
rule_fragment += self._rich_rule_destination_fragment(rich_rule.destination)
|
|
|
21c891 |
rule_fragment += self._rich_rule_source_fragment(rich_rule.source)
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
|
21c891 |
+ else:
|
|
|
21c891 |
+ chain_suffix = "allow"
|
|
|
21c891 |
|
|
|
21c891 |
rules = []
|
|
|
21c891 |
rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "mangle_%s_allow" % (target)]
|
|
|
21c891 |
+ "mangle_%s_%s" % (target, chain_suffix)]
|
|
|
21c891 |
+ rule_fragment +
|
|
|
21c891 |
[protocol, "dport", port, "meta", "mark", "set", mark_str])
|
|
|
21c891 |
|
|
|
21c891 |
if rich_rule and (rich_rule.family and rich_rule.family == "ipv6"
|
|
|
21c891 |
or toaddr and check_single_address("ipv6", toaddr)):
|
|
|
21c891 |
rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
|
21c891 |
- protocol, mark_fragment, toaddr, toport, "ip6"))
|
|
|
21c891 |
+ protocol, mark_fragment, toaddr, toport, "ip6", rich_rule))
|
|
|
21c891 |
elif rich_rule and (rich_rule.family and rich_rule.family == "ipv4"
|
|
|
21c891 |
or toaddr and check_single_address("ipv4", toaddr)):
|
|
|
21c891 |
rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
|
21c891 |
- protocol, mark_fragment, toaddr, toport, "ip"))
|
|
|
21c891 |
+ protocol, mark_fragment, toaddr, toport, "ip", rich_rule))
|
|
|
21c891 |
else:
|
|
|
21c891 |
if not toaddr or check_single_address("ipv6", toaddr):
|
|
|
21c891 |
rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
|
21c891 |
- protocol, mark_fragment, toaddr, toport, "ip6"))
|
|
|
21c891 |
+ protocol, mark_fragment, toaddr, toport, "ip6", rich_rule))
|
|
|
21c891 |
if not toaddr or check_single_address("ipv4", toaddr):
|
|
|
21c891 |
rules.extend(self._build_zone_forward_port_nat_rules(enable, zone,
|
|
|
21c891 |
- protocol, mark_fragment, toaddr, toport, "ip"))
|
|
|
21c891 |
+ protocol, mark_fragment, toaddr, toport, "ip", rich_rule))
|
|
|
21c891 |
|
|
|
21c891 |
target = DEFAULT_ZONE_TARGET.format(chain=SHORTCUTS[filter_chain],
|
|
|
21c891 |
zone=zone)
|
|
|
21c891 |
+ rule_fragment = []
|
|
|
21c891 |
+ if rich_rule:
|
|
|
21c891 |
+ rule_fragment += self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "filter_%s_allow" % (target), "ct", "state", "new,untracked"]
|
|
|
21c891 |
+ "filter_%s_%s" % (target, chain_suffix)]
|
|
|
21c891 |
+ + rule_fragment +
|
|
|
21c891 |
+ ["ct", "state", "new,untracked"]
|
|
|
21c891 |
+ mark_fragment + ["accept"])
|
|
|
21c891 |
|
|
|
21c891 |
return rules
|
|
|
21c891 |
@@ -1049,8 +1181,10 @@ class nftables(object):
|
|
|
21c891 |
if rich_rule.action:
|
|
|
21c891 |
rules.append(self._rich_rule_action(zone, rich_rule, enable, table, target, rule_fragment))
|
|
|
21c891 |
else:
|
|
|
21c891 |
+ chain_suffix = self._rich_rule_chain_suffix(rich_rule)
|
|
|
21c891 |
rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "%s_%s_deny" % (table, target)]
|
|
|
21c891 |
+ "%s_%s_%s" % (table, target, chain_suffix)]
|
|
|
21c891 |
+ + self._rich_rule_priority_fragment(rich_rule)
|
|
|
21c891 |
+ rule_fragment + ["%%REJECT%%"])
|
|
|
21c891 |
else:
|
|
|
21c891 |
if self._fw.get_log_denied() != "off" and final_target != "accept":
|
|
|
21c891 |
@@ -1079,14 +1213,14 @@ class nftables(object):
|
|
|
21c891 |
|
|
|
21c891 |
# WARN: index must be kept in sync with build_zone_chain_rules()
|
|
|
21c891 |
rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "%s_%s" % (table, _zone), "index", "2",
|
|
|
21c891 |
+ "%s_%s" % (table, _zone), "index", "4",
|
|
|
21c891 |
"%%ICMP%%", ibi_target])
|
|
|
21c891 |
|
|
|
21c891 |
if self._fw.zone.query_icmp_block_inversion(zone):
|
|
|
21c891 |
if self._fw.get_log_denied() != "off":
|
|
|
21c891 |
# WARN: index must be kept in sync with build_zone_chain_rules()
|
|
|
21c891 |
rules.append([add_del, "rule", "inet", "%s" % TABLE_NAME,
|
|
|
21c891 |
- "%s_%s" % (table, _zone), "index", "2",
|
|
|
21c891 |
+ "%s_%s" % (table, _zone), "index", "4",
|
|
|
21c891 |
"%%ICMP%%", "%%LOGTYPE%%", "log", "prefix",
|
|
|
21c891 |
"\"%s_%s_ICMP_BLOCK: \"" % (table, _zone)])
|
|
|
21c891 |
|
|
|
21c891 |
--
|
|
|
21c891 |
2.18.0
|
|
|
21c891 |
|