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:
		
							parent
							
								
									9c0f94b6e4
								
							
						
					
					
						commit
						c6bfeff9a2
					
				| @ -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 | ||||||
|  | 
 | ||||||
| @ -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 | ||||||
|  | 
 | ||||||
							
								
								
									
										51
									
								
								0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								0013-v2.2.0-test-rpfilter-use-CHECK-macros.patch
									
									
									
									
									
										Normal 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 | ||||||
|  | 
 | ||||||
							
								
								
									
										41
									
								
								0014-v2.2.0-test-IPv6_rpfilter-verify-valid-values.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								0014-v2.2.0-test-IPv6_rpfilter-verify-valid-values.patch
									
									
									
									
									
										Normal 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 | ||||||
|  | 
 | ||||||
							
								
								
									
										336
									
								
								0015-v2.2.0-chore-IPv6_rpfilter-prepare-for-new-config-va.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								0015-v2.2.0-chore-IPv6_rpfilter-prepare-for-new-config-va.patch
									
									
									
									
									
										Normal 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 | ||||||
|  | 
 | ||||||
							
								
								
									
										166
									
								
								0016-v2.2.0-feat-IPv6_rpfilter-support-loose-rpfilter.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								0016-v2.2.0-feat-IPv6_rpfilter-support-loose-rpfilter.patch
									
									
									
									
									
										Normal 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 | ||||||
|  | 
 | ||||||
							
								
								
									
										229
									
								
								0017-v2.2.0-feat-IPv6_rpfilter-support-loose-forward-rpfi.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								0017-v2.2.0-feat-IPv6_rpfilter-support-loose-forward-rpfi.patch
									
									
									
									
									
										Normal 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 | ||||||
|  | 
 | ||||||
							
								
								
									
										183
									
								
								0018-v2.2.0-feat-IPv6_rpfilter-support-strict-forward-rpf.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								0018-v2.2.0-feat-IPv6_rpfilter-support-strict-forward-rpf.patch
									
									
									
									
									
										Normal 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 | ||||||
|  | 
 | ||||||
| @ -1,7 +1,7 @@ | |||||||
| Summary: A firewall daemon with D-Bus interface providing a dynamic firewall | Summary: A firewall daemon with D-Bus interface providing a dynamic firewall | ||||||
| Name: firewalld | Name: firewalld | ||||||
| Version: 1.3.4 | Version: 1.3.4 | ||||||
| Release: 5%{?dist} | Release: 6%{?dist} | ||||||
| URL:     http://www.firewalld.org | URL:     http://www.firewalld.org | ||||||
| License: GPLv2+ | License: GPLv2+ | ||||||
| Source0: https://github.com/firewalld/firewalld/releases/download/v%{version}/firewalld-%{version}.tar.bz2 | 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 | 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 | 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 | 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 | BuildArch: noarch | ||||||
| BuildRequires: autoconf | BuildRequires: autoconf | ||||||
| BuildRequires: automake | BuildRequires: automake | ||||||
| @ -238,6 +246,11 @@ rm -rf %{buildroot}%{_datadir}/firewalld/testsuite | |||||||
| %{_mandir}/man1/firewall-config*.1* | %{_mandir}/man1/firewall-config*.1* | ||||||
| 
 | 
 | ||||||
| %changelog | %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 | * Mon Jul 01 2024 Eric Garver <egarver@redhat.com> - 1.3.4-5 | ||||||
| - feat(fw): add ReloadPolicy option in firewalld.conf | - feat(fw): add ReloadPolicy option in firewalld.conf | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user