feat(IPv6_rpfilter): support strict-forward rpfilter

feat(IPv6_rpfilter): support loose rpfilter
feat(IPv6_rpfilter): support loose-forward rpfilter

Resolves: RHEL-33330
This commit is contained in:
Eric Garver 2024-07-01 10:50:11 -04:00
parent 9c0f94b6e4
commit c6bfeff9a2
9 changed files with 1078 additions and 1 deletions

View File

@ -0,0 +1,27 @@
From 55e40954a8c596fabe03371e9a508d3518273ac1 Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Tue, 14 May 2024 16:27:54 -0400
Subject: [PATCH 11/22] v2.2.0: test(functions): add macro CHECK_NFTABLES_FIB
(cherry picked from commit 0aeebef07bc57b1f56b107632cdfdd809384398c)
---
src/tests/functions.at | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/tests/functions.at b/src/tests/functions.at
index f454ca980046..65a4ce078e05 100644
--- a/src/tests/functions.at
+++ b/src/tests/functions.at
@@ -748,3 +748,9 @@ m4_define([CHECK_NM_CAPABILITY_OVS], [
m4_define([IF_BACKEND_IS_DEFAULT], [
m4_if(nftables, FIREWALL_BACKEND, [$1], [])
])
+
+m4_define([CHECK_NFTABLES_FIB], [
+ m4_if(nftables, FIREWALL_BACKEND, [
+ IF_HOST_SUPPORTS_NFT_FIB([], [AT_SKIP_IF([:])])
+ ])
+])
--
2.43.5

View File

@ -0,0 +1,31 @@
From d368d579c78652a68273897d5f8b5099d251a9b5 Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Tue, 14 May 2024 16:21:06 -0400
Subject: [PATCH 12/22] v2.2.0: test(functions): add macro
CHECK_NFTABLES_FIB_IN_FORWARD
(cherry picked from commit b9cf7b75c7d94efa98545a3b7ad5020b1896b22a)
---
src/tests/functions.at | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/tests/functions.at b/src/tests/functions.at
index 65a4ce078e05..b2372dd4075b 100644
--- a/src/tests/functions.at
+++ b/src/tests/functions.at
@@ -754,3 +754,12 @@ m4_define([CHECK_NFTABLES_FIB], [
IF_HOST_SUPPORTS_NFT_FIB([], [AT_SKIP_IF([:])])
])
])
+
+m4_define([CHECK_NFTABLES_FIB_IN_FORWARD], [
+ m4_if(nftables, FIREWALL_BACKEND, [
+ NS_CHECK([nft add table inet firewalld_check])
+ NS_CHECK([nft add chain inet firewalld_check foobar { type filter hook forward priority 0 \; }])
+ AT_SKIP_IF([! NS_CMD([nft add rule inet firewalld_check foobar meta nfproto ipv6 fib saddr . mark . iif oif missing drop >/dev/null 2>&1])])
+ NS_CHECK([nft delete table inet firewalld_check])
+ ])
+])
--
2.43.5

View File

@ -0,0 +1,51 @@
From c1620d5ad4c151382373a138ab0c36dd7561a4bb Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Tue, 14 May 2024 16:29:50 -0400
Subject: [PATCH 13/22] v2.2.0: test(rpfilter): use CHECK macros
(cherry picked from commit 352f3fc7fc00b675178de1eff8f0197607741de7)
---
src/tests/features/rpfilter.at | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
index 01fb81ea75ef..ccc8a6cf5e80 100644
--- a/src/tests/features/rpfilter.at
+++ b/src/tests/features/rpfilter.at
@@ -1,22 +1,17 @@
-FWD_START_TEST([rpfilter])
+FWD_START_TEST([rpfilter - strict])
AT_KEYWORDS(rpfilter)
+CHECK_NFTABLES_FIB()
-IF_HOST_SUPPORTS_NFT_FIB([
- NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
- table inet firewalld {
- chain filter_PREROUTING {
- icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
- meta nfproto ipv6 fib saddr . mark . iif oif missing drop
- }
- }
- ])
-], [
- NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
- table inet firewalld {
- chain filter_PREROUTING {
- }
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=yes/' ./firewalld.conf])
+FWD_RELOAD()
+
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
+ table inet firewalld {
+ chain filter_PREROUTING {
+ icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
+ meta nfproto ipv6 fib saddr . mark . iif oif missing drop
}
- ])
+ }
])
IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
--
2.43.5

View File

@ -0,0 +1,41 @@
From 0ba1eed533e4cd1dd77771ba7c16dc0edcea841e Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Mon, 13 May 2024 13:53:55 -0400
Subject: [PATCH 14/22] v2.2.0: test(IPv6_rpfilter): verify valid values
Including the deprecated "yes" value.
(cherry picked from commit 1e91792157d36355669b4f02a82c1ee603a9467d)
---
src/tests/features/rpfilter.at | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
index ccc8a6cf5e80..755d9dfd33cc 100644
--- a/src/tests/features/rpfilter.at
+++ b/src/tests/features/rpfilter.at
@@ -22,4 +22,20 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
PREROUTING_ZONES 0 -- ::/0 ::/0
])
-FWD_END_TEST
+FWD_END_TEST()
+
+FWD_START_TEST([rpfilter - config values])
+AT_KEYWORDS(rpfilter)
+CHECK_NFTABLES_FIB()
+
+dnl Verify other/deprecated configuration values are accepted.
+dnl
+m4_foreach([VALUE], [[no], [yes], [false], [true]], [
+ AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=VALUE/' ./firewalld.conf])
+ FWD_RELOAD()
+])
+dnl And a bogus one.
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=bogus/' ./firewalld.conf])
+FWD_RELOAD()
+
+FWD_END_TEST([-e "/^WARNING: IPv6_rpfilter 'bogus' is not valid/d"])
--
2.43.5

View File

@ -0,0 +1,336 @@
From 7973ddf8d9f972f0292c8c865da9e0ebaefd77cb Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Thu, 16 May 2024 09:26:43 -0400
Subject: [PATCH 15/22] v2.2.0: chore(IPv6_rpfilter): prepare for new config
values
This is just prep work for supporting other configuration values. This
commits supports using "strict" as a value which is synonymous with
"yes" and is the current default behavior.
(cherry picked from commit cd959f21a5ceb41057b76f817b0456c281408ae0)
---
config/firewalld.conf | 15 ++++++++++-----
doc/xml/firewalld.conf.xml | 12 +++++++++---
doc/xml/firewalld.dbus.xml | 8 ++++++--
src/firewall/config/__init__.py.in | 3 ++-
src/firewall/core/fw.py | 19 ++++++++-----------
src/firewall/core/io/firewalld_conf.py | 14 ++++++++------
src/firewall/server/config.py | 22 ++++++++++++++++++----
src/firewall/server/firewalld.py | 2 +-
src/tests/dbus/firewalld.conf.at | 4 ++++
src/tests/features/rpfilter.at | 2 +-
10 files changed, 67 insertions(+), 34 deletions(-)
diff --git a/config/firewalld.conf b/config/firewalld.conf
index 7a0be1ff1b76..48e2a5a6527a 100644
--- a/config/firewalld.conf
+++ b/config/firewalld.conf
@@ -26,14 +26,19 @@ CleanupModulesOnExit=no
Lockdown=no
# IPv6_rpfilter
-# Performs a reverse path filter test on a packet for IPv6. If a reply to the
-# packet would be sent via the same interface that the packet arrived on, the
-# packet will match and be accepted, otherwise dropped.
+# Performs reverse path filtering (RPF) on IPv6 packets as per RFC 3704.
+# Possible values:
+# - strict: Performs "strict" filtering as per RFC 3704. This check
+# verifies that the in ingress interface is the same interface
+# that would be used to send a packet reply to the source. That
+# is, ingress == egress.
+# - no: RPF is completely disabled.
+#
# The rp_filter for IPv4 is controlled using sysctl.
# Note: This feature has a performance impact. See man page FIREWALLD.CONF(5)
# for details.
-# Default: yes
-IPv6_rpfilter=yes
+# Default: strict
+IPv6_rpfilter=strict
# IndividualCalls
# Do not use combined -restore calls, but individual calls. This increases the
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
index 022569ccf502..be6972aa0d8a 100644
--- a/doc/xml/firewalld.conf.xml
+++ b/doc/xml/firewalld.conf.xml
@@ -121,9 +121,15 @@
<term><option>IPv6_rpfilter</option></term>
<listitem>
<para>
- If this option is enabled (it is by default), reverse path filter test on a packet for IPv6 is performed.
- If a reply to the packet would be sent via the same interface that the packet arrived on, the packet will match and be accepted, otherwise dropped.
- For IPv4 the rp_filter is controlled using sysctl.
+ Performs reverse path filtering (RPF) on IPv6 packets as per RFC 3704.
+ Possible values:
+ - strict: Performs "strict" filtering as per RFC 3704. This check
+ verifies that the in ingress interface is the same interface
+ that would be used to send a packet reply to the source. That
+ is, ingress == egress.
+ - no: RPF is completely disabled.
+
+ The rp_filter for IPv4 is controlled using sysctl.
</para>
<para>
<emphasis role="bold">Note</emphasis>: This feature has a performance
diff --git a/doc/xml/firewalld.dbus.xml b/doc/xml/firewalld.dbus.xml
index a3196ea4af38..f04cf5ae757b 100644
--- a/doc/xml/firewalld.dbus.xml
+++ b/doc/xml/firewalld.dbus.xml
@@ -2858,8 +2858,12 @@
</listitem>
</varlistentry>
<varlistentry id="FirewallD1.config.Properties.IPv6_rpfilter">
- <term><parameter>IPv6_rpfilter</parameter> - s - (rw)</term>
- <listitem><para>Indicates whether the reverse path filter test on a packet for IPv6 is enabled. If a reply to the packet would be sent via the same interface that the packet arrived on, the packet will match and be accepted, otherwise dropped.</para></listitem>
+ <term><parameter>IPv6_rpfilter</parameter> - b - (rw)</term>
+ <listitem><para>Deprecated. See <link linkend="FirewallD1.config.Properties.IPv6_rpfilter2">org.fedoraproject.FirewallD1.config.Properties.IPv6_rpfilter2</link>.</para></listitem>
+ </varlistentry>
+ <varlistentry id="FirewallD1.config.Properties.IPv6_rpfilter2">
+ <term><parameter>IPv6_rpfilter2</parameter> - s - (rw)</term>
+ <listitem><para>Indicates whether the reverse path filter (RFE 3704) test on a packet for IPv6 is enabled.</para></listitem>
</varlistentry>
<varlistentry id="FirewallD1.config.Properties.IndividualCalls">
<term><parameter>IndividualCalls</parameter> - s - (ro)</term>
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
index da1e31e10e58..68e9bddce5a8 100644
--- a/src/firewall/config/__init__.py.in
+++ b/src/firewall/config/__init__.py.in
@@ -120,6 +120,7 @@ COMMANDS = {
LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
+IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict"]
# fallbacks: will be overloaded by firewalld.conf
FALLBACK_ZONE = "public"
@@ -127,7 +128,7 @@ FALLBACK_MINIMAL_MARK = 100
FALLBACK_CLEANUP_ON_EXIT = True
FALLBACK_CLEANUP_MODULES_ON_EXIT = False
FALLBACK_LOCKDOWN = False
-FALLBACK_IPV6_RPFILTER = True
+FALLBACK_IPV6_RPFILTER = "strict"
FALLBACK_INDIVIDUAL_CALLS = False
FALLBACK_LOG_DENIED = "off"
FALLBACK_AUTOMATIC_HELPERS = "no"
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index ac13be122b66..b2e150077958 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -98,7 +98,7 @@ class Firewall(object):
self.ebtables_enabled, self._state, self._panic,
self._default_zone, self._module_refcount, self._marks,
self.cleanup_on_exit, self.cleanup_modules_on_exit,
- self.ipv6_rpfilter_enabled, self.ipset_enabled,
+ self._ipv6_rpfilter, self.ipset_enabled,
self._individual_calls, self._log_denied)
def __init_vars(self):
@@ -112,7 +112,7 @@ class Firewall(object):
# 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._ipv6_rpfilter = config.FALLBACK_IPV6_RPFILTER
self._individual_calls = config.FALLBACK_INDIVIDUAL_CALLS
self._log_denied = config.FALLBACK_LOG_DENIED
self._firewall_backend = config.FALLBACK_FIREWALL_BACKEND
@@ -329,14 +329,11 @@ class Firewall(object):
if self._firewalld_conf.get("IPv6_rpfilter"):
value = self._firewalld_conf.get("IPv6_rpfilter")
if value is not None:
- if value.lower() in [ "no", "false" ]:
- self.ipv6_rpfilter_enabled = False
- if value.lower() in [ "yes", "true" ]:
- self.ipv6_rpfilter_enabled = True
- if self.ipv6_rpfilter_enabled:
- log.debug1("IPv6 rpfilter is enabled")
- else:
- log.debug1("IPV6 rpfilter is disabled")
+ if value.lower() in ["no", "false"]:
+ self._ipv6_rpfilter = "no"
+ elif value.lower() in ["yes", "true", "strict"]:
+ self._ipv6_rpfilter = "strict"
+ log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
if self._firewalld_conf.get("IndividualCalls"):
value = self._firewalld_conf.get("IndividualCalls")
@@ -933,7 +930,7 @@ class Firewall(object):
if self.is_ipv_enabled("ipv6"):
ipv6_backend = self.get_backend_by_ipv("ipv6")
if "raw" in ipv6_backend.get_available_tables():
- if self.ipv6_rpfilter_enabled:
+ if self._ipv6_rpfilter != "no":
rules = ipv6_backend.build_rpfilter_rules(self._log_denied)
transaction.add_rules(ipv6_backend, rules)
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
index d2879b319d1f..9ad64883b656 100644
--- a/src/firewall/core/io/firewalld_conf.py
+++ b/src/firewall/core/io/firewalld_conf.py
@@ -71,7 +71,7 @@ class firewalld_conf(object):
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("IPv6_rpfilter", config.FALLBACK_IPV6_RPFILTER)
self.set("IndividualCalls", "yes" if config.FALLBACK_INDIVIDUAL_CALLS else "no")
self.set("LogDenied", config.FALLBACK_LOG_DENIED)
self.set("AutomaticHelpers", config.FALLBACK_AUTOMATIC_HELPERS)
@@ -160,12 +160,14 @@ class firewalld_conf(object):
# check ipv6_rpfilter
value = self.get("IPv6_rpfilter")
- if not value or value.lower() not in [ "yes", "true", "no", "false" ]:
+ if not value or value.lower() not in config.IPV6_RPFILTER_VALUES:
if value is not None:
- log.warning("IPv6_rpfilter '%s' is not valid, using default "
- "value %s", value if value else '',
- config.FALLBACK_IPV6_RPFILTER)
- self.set("IPv6_rpfilter","yes" if config.FALLBACK_IPV6_RPFILTER else "no")
+ log.warning(
+ "IPv6_rpfilter '%s' is not valid, using default " "value %s",
+ value if value else "",
+ config.FALLBACK_IPV6_RPFILTER,
+ )
+ self.set("IPv6_rpfilter", config.FALLBACK_IPV6_RPFILTER)
# check individual calls
value = self.get("IndividualCalls")
diff --git a/src/firewall/server/config.py b/src/firewall/server/config.py
index dfbbb889b520..b805e497bb05 100644
--- a/src/firewall/server/config.py
+++ b/src/firewall/server/config.py
@@ -100,6 +100,7 @@ class FirewallDConfig(DbusServiceObject):
"CleanupOnExit": "readwrite",
"CleanupModulesOnExit": "readwrite",
"IPv6_rpfilter": "readwrite",
+ "IPv6_rpfilter2": "readwrite",
"Lockdown": "readwrite",
"MinimalMark": "readwrite",
"IndividualCalls": "readwrite",
@@ -564,7 +565,7 @@ class FirewallDConfig(DbusServiceObject):
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
"IndividualCalls", "LogDenied", "AutomaticHelpers",
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
- "AllowZoneDrifting" ]:
+ "AllowZoneDrifting", "IPv6_rpfilter2" ]:
raise dbus.exceptions.DBusException(
"org.freedesktop.DBus.Error.InvalidArgs: "
"Property '%s' does not exist" % prop)
@@ -594,8 +595,13 @@ class FirewallDConfig(DbusServiceObject):
value = "yes" if config.FALLBACK_LOCKDOWN else "no"
return dbus.String(value)
elif prop == "IPv6_rpfilter":
+ if value is None or value != "no":
+ return dbus.String("yes")
+ else:
+ return dbus.String("no")
+ elif prop == "IPv6_rpfilter2":
if value is None:
- value = "yes" if config.FALLBACK_IPV6_RPFILTER else "no"
+ value = config.FALLBACK_IPV6_RPFILTER
return dbus.String(value)
elif prop == "IndividualCalls":
if value is None:
@@ -640,6 +646,8 @@ class FirewallDConfig(DbusServiceObject):
return dbus.String(self._get_property(prop))
elif prop == "IPv6_rpfilter":
return dbus.String(self._get_property(prop))
+ elif prop == "IPv6_rpfilter2":
+ return dbus.String(self._get_property(prop))
elif prop == "IndividualCalls":
return dbus.String(self._get_property(prop))
elif prop == "LogDenied":
@@ -693,7 +701,7 @@ class FirewallDConfig(DbusServiceObject):
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
"IndividualCalls", "LogDenied", "AutomaticHelpers",
"FirewallBackend", "FlushAllOnReload", "RFC3964_IPv4",
- "AllowZoneDrifting" ]:
+ "AllowZoneDrifting", "IPv6_rpfilter2" ]:
ret[x] = self._get_property(x)
elif interface_name in [ config.dbus.DBUS_INTERFACE_CONFIG_DIRECT,
config.dbus.DBUS_INTERFACE_CONFIG_POLICIES ]:
@@ -722,7 +730,7 @@ class FirewallDConfig(DbusServiceObject):
"IPv6_rpfilter", "IndividualCalls",
"LogDenied",
"FirewallBackend", "FlushAllOnReload",
- "RFC3964_IPv4"]:
+ "RFC3964_IPv4", "IPv6_rpfilter2"]:
if property_name in [ "CleanupOnExit", "CleanupModulesOnExit",
"Lockdown", "IPv6_rpfilter",
"IndividualCalls", "FlushAllOnReload",
@@ -742,6 +750,12 @@ class FirewallDConfig(DbusServiceObject):
raise FirewallError(errors.INVALID_VALUE,
"'%s' for %s" % \
(new_value, property_name))
+ elif property_name == "IPv6_rpfilter2":
+ if new_value not in config.IPV6_RPFILTER_VALUES:
+ raise FirewallError(
+ errors.INVALID_VALUE,
+ "'%s' for %s" % (new_value, property_name),
+ )
else:
raise dbus.exceptions.DBusException(
"org.freedesktop.DBus.Error.InvalidArgs: "
diff --git a/src/firewall/server/firewalld.py b/src/firewall/server/firewalld.py
index e43ddb959f41..8b9593a22fd8 100644
--- a/src/firewall/server/firewalld.py
+++ b/src/firewall/server/firewalld.py
@@ -165,7 +165,7 @@ class FirewallD(DbusServiceObject):
return dbus.Boolean(self.fw.is_ipv_enabled("ipv6"))
elif prop == "IPv6_rpfilter":
- return dbus.Boolean(self.fw.ipv6_rpfilter_enabled)
+ return dbus.Boolean(False if self.fw._ipv6_rpfilter == "no" else True)
elif prop == "IPv6ICMPTypes":
return dbus.Array(self.fw.ipv6_supported_icmp_types, "s")
diff --git a/src/tests/dbus/firewalld.conf.at b/src/tests/dbus/firewalld.conf.at
index 10b16cd9e06f..61c220f3e59c 100644
--- a/src/tests/dbus/firewalld.conf.at
+++ b/src/tests/dbus/firewalld.conf.at
@@ -3,8 +3,10 @@ AT_KEYWORDS(dbus)
IF_HOST_SUPPORTS_NFT_FIB([
EXPECTED_IPV6_RPFILTER_VALUE=yes
+ EXPECTED_IPV6_RPFILTER2_VALUE=strict
], [
EXPECTED_IPV6_RPFILTER_VALUE=no
+ EXPECTED_IPV6_RPFILTER2_VALUE=no
])
IF_HOST_SUPPORTS_NFT_RULE_INDEX([
@@ -23,6 +25,7 @@ string "DefaultZone" : variant string "public"
string "FirewallBackend" : variant string "nftables"
string "FlushAllOnReload" : variant string "yes"
string "IPv6_rpfilter" : variant string m4_escape(["${EXPECTED_IPV6_RPFILTER_VALUE}"])
+string "IPv6_rpfilter2" : variant string m4_escape(["${EXPECTED_IPV6_RPFILTER2_VALUE}"])
string "IndividualCalls" : variant string m4_escape(["${EXPECTED_INDIVIDUAL_CALLS_VALUE}"])
string "Lockdown" : variant string "no"
string "LogDenied" : variant string "off"
@@ -43,6 +46,7 @@ _helper([AutomaticHelpers], [string:"yes"], [variant string "no"])
_helper([Lockdown], [string:"yes"], [variant string "yes"])
_helper([LogDenied], [string:"all"], [variant string "all"])
_helper([IPv6_rpfilter], [string:"yes"], [variant string "yes"])
+_helper([IPv6_rpfilter2], [string:"no"], [variant string "no"])
_helper([IndividualCalls], [string:"yes"], [variant string "yes"])
_helper([FirewallBackend], [string:"iptables"], [variant string "iptables"])
_helper([FlushAllOnReload], [string:"no"], [variant string "no"])
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
index 755d9dfd33cc..58a4b4500330 100644
--- a/src/tests/features/rpfilter.at
+++ b/src/tests/features/rpfilter.at
@@ -2,7 +2,7 @@ FWD_START_TEST([rpfilter - strict])
AT_KEYWORDS(rpfilter)
CHECK_NFTABLES_FIB()
-AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=yes/' ./firewalld.conf])
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=strict/' ./firewalld.conf])
FWD_RELOAD()
NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
--
2.43.5

View File

@ -0,0 +1,166 @@
From 41828e0723b1ad195a33c535918a2fb6fabaf88f Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Mon, 6 May 2024 10:22:09 -0400
Subject: [PATCH 16/22] v2.2.0: feat(IPv6_rpfilter): support loose rpfilter
Support "loose" mode as per RFC 3704. This guarantees only that there is
a patch back to the source, but does NOT guarantee that ingress ==
egress.
(cherry picked from commit 669524a10658761f614e1f199970844db7259960)
---
config/firewalld.conf | 4 ++++
doc/xml/firewalld.conf.xml | 4 ++++
src/firewall/config/__init__.py.in | 2 +-
src/firewall/core/fw.py | 2 ++
src/firewall/core/ipXtables.py | 16 ++++++++++------
src/firewall/core/nftables.py | 8 +++++++-
src/tests/features/rpfilter.at | 26 ++++++++++++++++++++++++++
7 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/config/firewalld.conf b/config/firewalld.conf
index 48e2a5a6527a..c35caf9f152b 100644
--- a/config/firewalld.conf
+++ b/config/firewalld.conf
@@ -32,6 +32,10 @@ Lockdown=no
# verifies that the in ingress interface is the same interface
# that would be used to send a packet reply to the source. That
# is, ingress == egress.
+# - loose: Performs "loose" filtering as per RFC 3704. This check only
+# verifies that there is a route back to the source through any
+# interface; even if it's not the same one on which the packet
+# arrived.
# - no: RPF is completely disabled.
#
# The rp_filter for IPv4 is controlled using sysctl.
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
index be6972aa0d8a..279a149ab2a1 100644
--- a/doc/xml/firewalld.conf.xml
+++ b/doc/xml/firewalld.conf.xml
@@ -127,6 +127,10 @@
verifies that the in ingress interface is the same interface
that would be used to send a packet reply to the source. That
is, ingress == egress.
+ - loose: Performs "loose" filtering as per RFC 3704. This check only
+ verifies that there is a route back to the source through any
+ interface; even if it's not the same one on which the packet
+ arrived.
- no: RPF is completely disabled.
The rp_filter for IPv4 is controlled using sysctl.
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
index 68e9bddce5a8..6bfe96be35a6 100644
--- a/src/firewall/config/__init__.py.in
+++ b/src/firewall/config/__init__.py.in
@@ -120,7 +120,7 @@ COMMANDS = {
LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
-IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict"]
+IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose"]
# fallbacks: will be overloaded by firewalld.conf
FALLBACK_ZONE = "public"
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index b2e150077958..d9724e9c8534 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -333,6 +333,8 @@ class Firewall(object):
self._ipv6_rpfilter = "no"
elif value.lower() in ["yes", "true", "strict"]:
self._ipv6_rpfilter = "strict"
+ elif value.lower() in ["loose"]:
+ self._ipv6_rpfilter = "loose"
log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
if self._firewalld_conf.get("IndividualCalls"):
diff --git a/src/firewall/core/ipXtables.py b/src/firewall/core/ipXtables.py
index 1a0cea7a3b4e..339840c37ba4 100644
--- a/src/firewall/core/ipXtables.py
+++ b/src/firewall/core/ipXtables.py
@@ -1456,13 +1456,17 @@ class ip6tables(ip4tables):
def build_rpfilter_rules(self, log_denied=False):
rules = []
- rules.append([ "-I", "PREROUTING", "-t", "mangle",
- "-m", "rpfilter", "--invert", "--validmark",
- "-j", "DROP" ])
+ rpfilter_fragment = ["-m", "rpfilter", "--invert", "--validmark"]
+ if self._fw._ipv6_rpfilter == "loose":
+ rpfilter_fragment += ["--loose"]
+
+ rules.append([ "-I", "PREROUTING", "-t", "mangle" ]
+ + rpfilter_fragment +
+ [ "-j", "DROP" ])
if log_denied != "off":
- rules.append([ "-I", "PREROUTING", "-t", "mangle",
- "-m", "rpfilter", "--invert", "--validmark",
- "-j", "LOG",
+ rules.append([ "-I", "PREROUTING", "-t", "mangle" ]
+ + rpfilter_fragment +
+ [ "-j", "LOG",
"--log-prefix", "rpfilter_DROP: " ])
rules.append([ "-I", "PREROUTING", "-t", "mangle",
"-p", "ipv6-icmp",
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
index e9816147ef8e..9827e84042ef 100644
--- a/src/firewall/core/nftables.py
+++ b/src/firewall/core/nftables.py
@@ -1614,10 +1614,16 @@ class nftables(object):
def build_rpfilter_rules(self, log_denied=False):
rules = []
+
+ if self._fw._ipv6_rpfilter == "loose":
+ fib_flags = ["saddr", "mark"]
+ else:
+ fib_flags = ["saddr", "mark", "iif"]
+
expr_fragments = [{"match": {"left": {"meta": {"key": "nfproto"}},
"op": "==",
"right": "ipv6"}},
- {"match": {"left": {"fib": {"flags": ["saddr", "iif", "mark"],
+ {"match": {"left": {"fib": {"flags": fib_flags,
"result": "oif"}},
"op": "==",
"right": False}}]
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
index 58a4b4500330..23cd9e0e8d7f 100644
--- a/src/tests/features/rpfilter.at
+++ b/src/tests/features/rpfilter.at
@@ -24,6 +24,32 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
FWD_END_TEST()
+FWD_START_TEST([rpfilter - loose])
+AT_KEYWORDS(rpfilter)
+CHECK_NFTABLES_FIB()
+
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=loose/' ./firewalld.conf])
+FWD_RELOAD()
+
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
+ table inet firewalld {
+ chain filter_PREROUTING {
+ icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
+ meta nfproto ipv6 fib saddr . mark oif missing drop
+ }
+ }
+])
+
+IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
+ ACCEPT 58 -- ::/0 ::/0 ipv6-icmptype 134
+ ACCEPT 58 -- ::/0 ::/0 ipv6-icmptype 135
+ DROP 0 -- ::/0 ::/0 rpfilter loose validmark invert
+ PREROUTING_direct 0 -- ::/0 ::/0
+ PREROUTING_ZONES 0 -- ::/0 ::/0
+])
+
+FWD_END_TEST()
+
FWD_START_TEST([rpfilter - config values])
AT_KEYWORDS(rpfilter)
CHECK_NFTABLES_FIB()
--
2.43.5

View File

@ -0,0 +1,229 @@
From a965defecfad03530f3374271fb4889ff895ab1c Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Tue, 14 May 2024 12:16:44 -0400
Subject: [PATCH 17/22] v2.2.0: feat(IPv6_rpfilter): support loose-forward
rpfilter
This adds a new config value for IPv6_rpfilter, loose-forward. In this
mode the rpfilter occurs only for forwarded packets using the "loose"
algorithm from RFC 3704. This is a significant performance improvement
for end-stations that have a single default route because the RPF check
is completely absent on INPUT.
This new value is NOT compatible with the iptables backend. This is
enforced by configuration checks.
(cherry picked from commit fb692d2e560253c97c7fdd1e9fdbfcc8c61d0eba)
---
config/firewalld.conf | 2 ++
doc/xml/firewalld.conf.xml | 2 ++
src/firewall/config/__init__.py.in | 3 ++-
src/firewall/core/fw.py | 2 ++
src/firewall/core/io/firewalld_conf.py | 15 +++++++++++
src/firewall/core/nftables.py | 30 ++++++++++++++-------
src/tests/features/rpfilter.at | 36 ++++++++++++++++++++++++++
7 files changed, 79 insertions(+), 11 deletions(-)
diff --git a/config/firewalld.conf b/config/firewalld.conf
index c35caf9f152b..5d0777d54b15 100644
--- a/config/firewalld.conf
+++ b/config/firewalld.conf
@@ -36,6 +36,8 @@ Lockdown=no
# verifies that there is a route back to the source through any
# interface; even if it's not the same one on which the packet
# arrived.
+# - loose-forward: This is almost identical to "loose", but does not perform
+# RPF for packets targeted to the host (INPUT).
# - no: RPF is completely disabled.
#
# The rp_filter for IPv4 is controlled using sysctl.
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
index 279a149ab2a1..1303758347e2 100644
--- a/doc/xml/firewalld.conf.xml
+++ b/doc/xml/firewalld.conf.xml
@@ -131,6 +131,8 @@
verifies that there is a route back to the source through any
interface; even if it's not the same one on which the packet
arrived.
+ - loose-forward: This is almost identical to "loose", but does not perform
+ RPF for packets targeted to the host (INPUT).
- no: RPF is completely disabled.
The rp_filter for IPv4 is controlled using sysctl.
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
index 6bfe96be35a6..f2c4c9f5afa1 100644
--- a/src/firewall/config/__init__.py.in
+++ b/src/firewall/config/__init__.py.in
@@ -120,7 +120,8 @@ COMMANDS = {
LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
-IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose"]
+IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose",
+ "loose-forward"]
# fallbacks: will be overloaded by firewalld.conf
FALLBACK_ZONE = "public"
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index d9724e9c8534..8c20a4a606e2 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -335,6 +335,8 @@ class Firewall(object):
self._ipv6_rpfilter = "strict"
elif value.lower() in ["loose"]:
self._ipv6_rpfilter = "loose"
+ elif value.lower() in ["loose-forward"]:
+ self._ipv6_rpfilter = "loose-forward"
log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
if self._firewalld_conf.get("IndividualCalls"):
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
index 9ad64883b656..159715df7ede 100644
--- a/src/firewall/core/io/firewalld_conf.py
+++ b/src/firewall/core/io/firewalld_conf.py
@@ -26,6 +26,7 @@ import shutil
from firewall import config
from firewall.core.logger import log
+from firewall import errors
valid_keys = [ "DefaultZone", "MinimalMark", "CleanupOnExit",
"CleanupModulesOnExit", "Lockdown", "IPv6_rpfilter",
@@ -81,6 +82,18 @@ class firewalld_conf(object):
self.set("RFC3964_IPv4", "yes" if config.FALLBACK_RFC3964_IPV4 else "no")
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
+ def sanity_check(self):
+ if (
+ self.get("FirewallBackend") == "iptables"
+ and self.get("IPv6_rpfilter") == "loose-forward"
+ ):
+ raise errors.FirewallError(
+ errors.INVALID_VALUE,
+ "IPv6_rpfilter=loose-forward is incompatible "
+ "with FirewallBackend=iptables. This is a limitation "
+ "of the iptables backend.",
+ )
+
# load self.filename
def read(self):
self.clear()
@@ -238,6 +251,8 @@ class firewalld_conf(object):
config.FALLBACK_ALLOW_ZONE_DRIFTING)
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
+ self.sanity_check()
+
# save to self.filename if there are key/value changes
def write(self):
if len(self._config) < 1:
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
index 9827e84042ef..da2d8eb8ec29 100644
--- a/src/firewall/core/nftables.py
+++ b/src/firewall/core/nftables.py
@@ -1614,9 +1614,13 @@ class nftables(object):
def build_rpfilter_rules(self, log_denied=False):
rules = []
+ rpfilter_chain = "filter_PREROUTING"
if self._fw._ipv6_rpfilter == "loose":
fib_flags = ["saddr", "mark"]
+ elif self._fw._ipv6_rpfilter == "loose-forward":
+ fib_flags = ["saddr", "mark"]
+ rpfilter_chain = "filter_FORWARD"
else:
fib_flags = ["saddr", "mark", "iif"]
@@ -1633,17 +1637,19 @@ class nftables(object):
rules.append({"insert": {"rule": {"family": "inet",
"table": TABLE_NAME,
- "chain": "filter_PREROUTING",
+ "chain": rpfilter_chain,
"expr": expr_fragments}}})
# RHBZ#1058505, RHBZ#1575431 (bug in kernel 4.16-4.17)
- rules.append({"insert": {"rule": {"family": "inet",
- "table": TABLE_NAME,
- "chain": "filter_PREROUTING",
- "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6",
- "field": "type"}},
- "op": "==",
- "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}},
- {"accept": None}]}}})
+ if self._fw._ipv6_rpfilter != "loose-forward":
+ # this rule doesn't make sense for forwarded packets
+ rules.append({"insert": {"rule": {"family": "inet",
+ "table": TABLE_NAME,
+ "chain": rpfilter_chain,
+ "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6",
+ "field": "type"}},
+ "op": "==",
+ "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}},
+ {"accept": None}]}}})
return rules
def build_rfc3964_ipv4_rules(self):
@@ -1674,7 +1680,11 @@ class nftables(object):
"chain": "filter_OUTPUT",
"index": 1,
"expr": expr_fragments}}})
- forward_index = 4 if self._fw.get_log_denied() != "off" else 3
+ forward_index = 3
+ if self._fw.get_log_denied() != "off":
+ forward_index += 1
+ if self._fw._ipv6_rpfilter == "loose-forward":
+ forward_index += 1
rules.append({"add": {"rule": {"family": "inet",
"table": TABLE_NAME,
"chain": "filter_FORWARD",
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
index 23cd9e0e8d7f..5c25ed7e16f0 100644
--- a/src/tests/features/rpfilter.at
+++ b/src/tests/features/rpfilter.at
@@ -50,6 +50,42 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
FWD_END_TEST()
+FWD_START_TEST([rpfilter - loose-forward])
+AT_KEYWORDS(rpfilter)
+CHECK_NFTABLES_FIB()
+CHECK_NFTABLES_FIB_IN_FORWARD()
+
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=loose-forward/' ./firewalld.conf])
+m4_if(iptables, FIREWALL_BACKEND, [
+FWD_RELOAD(114, [ignore], [ignore])
+], [
+FWD_RELOAD()
+])
+
+NFT_LIST_RULES([inet], [filter_FORWARD], 0, [dnl
+ table inet firewalld {
+ chain filter_FORWARD {
+ meta nfproto ipv6 fib saddr . mark oif missing drop
+ ct state established,related accept
+ ct status dnat accept
+ iifname "lo" accept
+ ct state invalid drop
+ ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable
+ jump filter_FORWARD_ZONES
+ reject with icmpx admin-prohibited
+ }
+ }
+])
+
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
+ table inet firewalld {
+ chain filter_PREROUTING {
+ }
+ }
+])
+
+FWD_END_TEST([-e "/^ERROR: INVALID_VALUE:/d"])
+
FWD_START_TEST([rpfilter - config values])
AT_KEYWORDS(rpfilter)
CHECK_NFTABLES_FIB()
--
2.43.5

View File

@ -0,0 +1,183 @@
From 7629e1c24b73d8c1e56d275c07076bc3bf99caad Mon Sep 17 00:00:00 2001
From: Eric Garver <eric@garver.life>
Date: Fri, 17 May 2024 10:05:04 -0400
Subject: [PATCH 18/22] v2.2.0: feat(IPv6_rpfilter): support strict-forward
rpfilter
(cherry picked from commit 98e5cbb862885e00981611a79f7a3186da5d1050)
---
config/firewalld.conf | 2 ++
doc/xml/firewalld.conf.xml | 5 +++-
src/firewall/config/__init__.py.in | 2 +-
src/firewall/core/fw.py | 2 ++
src/firewall/core/io/firewalld_conf.py | 8 +++---
src/firewall/core/nftables.py | 7 +++--
src/tests/features/rpfilter.at | 36 ++++++++++++++++++++++++++
7 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/config/firewalld.conf b/config/firewalld.conf
index 5d0777d54b15..cf95af3eea8e 100644
--- a/config/firewalld.conf
+++ b/config/firewalld.conf
@@ -36,6 +36,8 @@ Lockdown=no
# verifies that there is a route back to the source through any
# interface; even if it's not the same one on which the packet
# arrived.
+# - strict-forward: This is almost identical to "strict", but does not perform
+# RPF for packets targeted to the host (INPUT).
# - loose-forward: This is almost identical to "loose", but does not perform
# RPF for packets targeted to the host (INPUT).
# - no: RPF is completely disabled.
diff --git a/doc/xml/firewalld.conf.xml b/doc/xml/firewalld.conf.xml
index 1303758347e2..4b9bfdad15be 100644
--- a/doc/xml/firewalld.conf.xml
+++ b/doc/xml/firewalld.conf.xml
@@ -131,6 +131,8 @@
verifies that there is a route back to the source through any
interface; even if it's not the same one on which the packet
arrived.
+ - strict-forward: This is almost identical to "loose", but does not perform
+ RPF for packets targeted to the host (INPUT).
- loose-forward: This is almost identical to "loose", but does not perform
RPF for packets targeted to the host (INPUT).
- no: RPF is completely disabled.
@@ -144,7 +146,8 @@
the established connections fast path. As such it can have a
significant performance impact if there is a lot of traffic. It's
enabled by default for security, but can be disabled if performance is
- a concern.
+ a concern. Alternatively one of the variants that only does RPF on
+ forwarded packets may be used.
</para>
</listitem>
</varlistentry>
diff --git a/src/firewall/config/__init__.py.in b/src/firewall/config/__init__.py.in
index f2c4c9f5afa1..2eda337e8180 100644
--- a/src/firewall/config/__init__.py.in
+++ b/src/firewall/config/__init__.py.in
@@ -121,7 +121,7 @@ LOG_DENIED_VALUES = [ "all", "unicast", "broadcast", "multicast", "off" ]
AUTOMATIC_HELPERS_VALUES = [ "yes", "no", "system" ]
FIREWALL_BACKEND_VALUES = [ "nftables", "iptables" ]
IPV6_RPFILTER_VALUES = ["yes", "true", "no", "false", "strict", "loose",
- "loose-forward"]
+ "loose-forward", "strict-forward"]
# fallbacks: will be overloaded by firewalld.conf
FALLBACK_ZONE = "public"
diff --git a/src/firewall/core/fw.py b/src/firewall/core/fw.py
index 8c20a4a606e2..f91ff53fc37a 100644
--- a/src/firewall/core/fw.py
+++ b/src/firewall/core/fw.py
@@ -337,6 +337,8 @@ class Firewall(object):
self._ipv6_rpfilter = "loose"
elif value.lower() in ["loose-forward"]:
self._ipv6_rpfilter = "loose-forward"
+ elif value.lower() in ["strict-forward"]:
+ self._ipv6_rpfilter = "strict-forward"
log.debug1(f"IPv6_rpfilter is set to '{self._ipv6_rpfilter}'")
if self._firewalld_conf.get("IndividualCalls"):
diff --git a/src/firewall/core/io/firewalld_conf.py b/src/firewall/core/io/firewalld_conf.py
index 159715df7ede..20072e26cfcd 100644
--- a/src/firewall/core/io/firewalld_conf.py
+++ b/src/firewall/core/io/firewalld_conf.py
@@ -83,13 +83,13 @@ class firewalld_conf(object):
self.set("AllowZoneDrifting", "yes" if config.FALLBACK_ALLOW_ZONE_DRIFTING else "no")
def sanity_check(self):
- if (
- self.get("FirewallBackend") == "iptables"
- and self.get("IPv6_rpfilter") == "loose-forward"
+ if self.get("FirewallBackend") == "iptables" and self.get("IPv6_rpfilter") in (
+ "loose-forward",
+ "strict-forward",
):
raise errors.FirewallError(
errors.INVALID_VALUE,
- "IPv6_rpfilter=loose-forward is incompatible "
+ f"IPv6_rpfilter={self.get('IPv6_rpfilter')} is incompatible "
"with FirewallBackend=iptables. This is a limitation "
"of the iptables backend.",
)
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
index da2d8eb8ec29..5a49b34e3a4f 100644
--- a/src/firewall/core/nftables.py
+++ b/src/firewall/core/nftables.py
@@ -1621,6 +1621,9 @@ class nftables(object):
elif self._fw._ipv6_rpfilter == "loose-forward":
fib_flags = ["saddr", "mark"]
rpfilter_chain = "filter_FORWARD"
+ elif self._fw._ipv6_rpfilter == "strict-forward":
+ fib_flags = ["saddr", "mark", "iif"]
+ rpfilter_chain = "filter_FORWARD"
else:
fib_flags = ["saddr", "mark", "iif"]
@@ -1640,7 +1643,7 @@ class nftables(object):
"chain": rpfilter_chain,
"expr": expr_fragments}}})
# RHBZ#1058505, RHBZ#1575431 (bug in kernel 4.16-4.17)
- if self._fw._ipv6_rpfilter != "loose-forward":
+ if self._fw._ipv6_rpfilter not in ("loose-forward", "strict-forward"):
# this rule doesn't make sense for forwarded packets
rules.append({"insert": {"rule": {"family": "inet",
"table": TABLE_NAME,
@@ -1683,7 +1686,7 @@ class nftables(object):
forward_index = 3
if self._fw.get_log_denied() != "off":
forward_index += 1
- if self._fw._ipv6_rpfilter == "loose-forward":
+ if self._fw._ipv6_rpfilter in ("loose-forward", "strict-forward"):
forward_index += 1
rules.append({"add": {"rule": {"family": "inet",
"table": TABLE_NAME,
diff --git a/src/tests/features/rpfilter.at b/src/tests/features/rpfilter.at
index 5c25ed7e16f0..9ad50c993ba0 100644
--- a/src/tests/features/rpfilter.at
+++ b/src/tests/features/rpfilter.at
@@ -50,6 +50,42 @@ IP6TABLES_LIST_RULES([mangle], [PREROUTING], 0, [dnl
FWD_END_TEST()
+FWD_START_TEST([rpfilter - strict-forward])
+AT_KEYWORDS(rpfilter)
+CHECK_NFTABLES_FIB()
+CHECK_NFTABLES_FIB_IN_FORWARD()
+
+AT_CHECK([sed -i 's/^IPv6_rpfilter.*/IPv6_rpfilter=strict-forward/' ./firewalld.conf])
+m4_if(iptables, FIREWALL_BACKEND, [
+FWD_RELOAD(114, [ignore], [ignore])
+], [
+FWD_RELOAD()
+])
+
+NFT_LIST_RULES([inet], [filter_FORWARD], 0, [dnl
+ table inet firewalld {
+ chain filter_FORWARD {
+ meta nfproto ipv6 fib saddr . mark . iif oif missing drop
+ ct state established,related accept
+ ct status dnat accept
+ iifname "lo" accept
+ ct state invalid drop
+ ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable
+ jump filter_FORWARD_ZONES
+ reject with icmpx admin-prohibited
+ }
+ }
+])
+
+NFT_LIST_RULES([inet], [filter_PREROUTING], 0, [dnl
+ table inet firewalld {
+ chain filter_PREROUTING {
+ }
+ }
+])
+
+FWD_END_TEST([-e "/^ERROR: INVALID_VALUE:/d"])
+
FWD_START_TEST([rpfilter - loose-forward])
AT_KEYWORDS(rpfilter)
CHECK_NFTABLES_FIB()
--
2.43.5

View File

@ -1,7 +1,7 @@
Summary: A firewall daemon with D-Bus interface providing a dynamic firewall
Name: firewalld
Version: 1.3.4
Release: 5%{?dist}
Release: 6%{?dist}
URL: http://www.firewalld.org
License: GPLv2+
Source0: https://github.com/firewalld/firewalld/releases/download/v%{version}/firewalld-%{version}.tar.bz2
@ -15,6 +15,14 @@ Patch7: 0007-v2.1.0-fix-rich-validate-service-name-of-rich-rule.patch
Patch8: 0008-v2.1.0-improvement-nftables-do-not-track-rule-handle.patch
Patch9: 0009-v2.1.0-improvement-fw-make-set_policy-DROP-more-flex.patch
Patch10: 0010-v2.1.0-feat-fw-add-ReloadPolicy-option-in-firewalld..patch
Patch11: 0011-v2.2.0-test-functions-add-macro-CHECK_NFTABLES_FIB.patch
Patch12: 0012-v2.2.0-test-functions-add-macro-CHECK_NFTABLES_FIB_I.patch
Patch13: 0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
Patch14: 0014-v2.2.0-test-IPv6_rpfilter-verify-valid-values.patch
Patch15: 0015-v2.2.0-chore-IPv6_rpfilter-prepare-for-new-config-va.patch
Patch16: 0016-v2.2.0-feat-IPv6_rpfilter-support-loose-rpfilter.patch
Patch17: 0017-v2.2.0-feat-IPv6_rpfilter-support-loose-forward-rpfi.patch
Patch18: 0018-v2.2.0-feat-IPv6_rpfilter-support-strict-forward-rpf.patch
BuildArch: noarch
BuildRequires: autoconf
BuildRequires: automake
@ -238,6 +246,11 @@ rm -rf %{buildroot}%{_datadir}/firewalld/testsuite
%{_mandir}/man1/firewall-config*.1*
%changelog
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-6
- feat(IPv6_rpfilter): support loose rpfilter
- feat(IPv6_rpfilter): support loose-forward rpfilter
- feat(IPv6_rpfilter): support strict-forward rpfilter
* Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-5
- feat(fw): add ReloadPolicy option in firewalld.conf