summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@vyos.io>2025-09-22 13:55:15 +0100
committerGitHub <noreply@github.com>2025-09-22 13:55:15 +0100
commitc035c4d627ed53c9744d956b7a4d9878e36020cb (patch)
treeb574a9c1c7d029d82696f55c431caa23008edaa4
parentcd04de9af0a98b86937d2055505284bc4448691c (diff)
parent61e234a74a95f68c665a21437c9ffaa8747bbc3a (diff)
downloadvyos-1x-c035c4d627ed53c9744d956b7a4d9878e36020cb.tar.gz
vyos-1x-c035c4d627ed53c9744d956b7a4d9878e36020cb.zip
Merge pull request #4698 from l0crian1/fw-disable-conntrack
firewall: T7475: Add an option to disable conntrack for individual firewall chaisn
-rwxr-xr-xdata/templates/firewall/nftables.j238
-rw-r--r--interface-definitions/include/firewall/disable-conntrack.xml.i8
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-forward.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-input.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-output.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-forward.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-input.xml.i1
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-output.xml.i1
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py50
-rwxr-xr-xsrc/conf_mode/firewall.py32
10 files changed, 134 insertions, 0 deletions
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 39ef72059..f810496f9 100755
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -140,6 +140,25 @@ table ip vyos_filter {
{% endfor %}
{% endif %}
+{% if ipv4.forward.filter.disable_conntrack is vyos_defined or ipv4.input.filter.disable_conntrack is vyos_defined %}
+ chain VYOS_DISABLE_CONNTRACK_INP_FWD {
+ type filter hook prerouting priority -320; policy accept;
+{% if ipv4.forward.filter.disable_conntrack is vyos_defined %}
+ fib daddr . iif type unicast notrack counter comment "DISABLE-CT-FWD"
+{% endif %}
+{% if ipv4.input.filter.disable_conntrack is vyos_defined %}
+ fib daddr . iif type local notrack counter comment "DISABLE-CT-INP"
+{% endif %}
+ }
+{% endif %}
+
+{% if ipv4.output.filter.disable_conntrack is vyos_defined %}
+ chain VYOS_DISABLE_CONNTRACK_OUT {
+ type filter hook output priority -320; policy accept;
+ notrack counter comment "DISABLE-CT-OUT"
+ }
+{% endif %}
+
{% for set_name in ns.sets %}
set RECENT_{{ set_name }} {
type ipv4_addr
@@ -308,6 +327,25 @@ table ip6 vyos_filter {
{% endfor %}
{% endif %}
+{% if ipv6.forward.filter.disable_conntrack is vyos_defined or ipv6.input.filter.disable_conntrack is vyos_defined %}
+ chain VYOS_DISABLE_CONNTRACK_INP_FWD_V6 {
+ type filter hook prerouting priority -320; policy accept;
+{% if ipv6.forward.filter.disable_conntrack is vyos_defined %}
+ fib daddr . iif type unicast notrack counter comment "DISABLE-CT-FWD-V6"
+{% endif %}
+{% if ipv6.input.filter.disable_conntrack is vyos_defined %}
+ fib daddr . iif type local notrack counter comment "DISABLE-CT-INP-V6"
+{% endif %}
+ }
+{% endif %}
+
+{% if ipv6.output.filter.disable_conntrack is vyos_defined %}
+ chain VYOS_DISABLE_CONNTRACK_OUT_V6 {
+ type filter hook output priority -320; policy accept;
+ notrack counter comment "DISABLE-CT-OUT-V6"
+ }
+{% endif %}
+
{% for set_name in ns.sets %}
set RECENT6_{{ set_name }} {
type ipv6_addr
diff --git a/interface-definitions/include/firewall/disable-conntrack.xml.i b/interface-definitions/include/firewall/disable-conntrack.xml.i
new file mode 100644
index 000000000..96a692166
--- /dev/null
+++ b/interface-definitions/include/firewall/disable-conntrack.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from firewall/disable-conntrack.xml.i -->
+<leafNode name="disable-conntrack">
+ <properties>
+ <help>Disable conntrack within this chain</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
index a2da4b701..8f04d6739 100644
--- a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
+++ b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
@@ -9,6 +9,7 @@
<help>IPv4 firewall forward filter</help>
</properties>
<children>
+ #include <include/firewall/disable-conntrack.xml.i>
#include <include/firewall/default-action-base-chains.xml.i>
#include <include/firewall/default-log.xml.i>
#include <include/generic-description.xml.i>
diff --git a/interface-definitions/include/firewall/ipv4-hook-input.xml.i b/interface-definitions/include/firewall/ipv4-hook-input.xml.i
index 491d1a9f3..64366eac0 100644
--- a/interface-definitions/include/firewall/ipv4-hook-input.xml.i
+++ b/interface-definitions/include/firewall/ipv4-hook-input.xml.i
@@ -9,6 +9,7 @@
<help>IPv4 firewall input filter</help>
</properties>
<children>
+ #include <include/firewall/disable-conntrack.xml.i>
#include <include/firewall/default-action-base-chains.xml.i>
#include <include/firewall/default-log.xml.i>
#include <include/generic-description.xml.i>
diff --git a/interface-definitions/include/firewall/ipv4-hook-output.xml.i b/interface-definitions/include/firewall/ipv4-hook-output.xml.i
index f68136557..14ea1073b 100644
--- a/interface-definitions/include/firewall/ipv4-hook-output.xml.i
+++ b/interface-definitions/include/firewall/ipv4-hook-output.xml.i
@@ -9,6 +9,7 @@
<help>IPv4 firewall output filter</help>
</properties>
<children>
+ #include <include/firewall/disable-conntrack.xml.i>
#include <include/firewall/default-action-base-chains.xml.i>
#include <include/firewall/default-log.xml.i>
#include <include/generic-description.xml.i>
diff --git a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
index 79898d691..0319c915a 100644
--- a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
+++ b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
@@ -9,6 +9,7 @@
<help>IPv6 firewall forward filter</help>
</properties>
<children>
+ #include <include/firewall/disable-conntrack.xml.i>
#include <include/firewall/default-action-base-chains.xml.i>
#include <include/firewall/default-log.xml.i>
#include <include/generic-description.xml.i>
diff --git a/interface-definitions/include/firewall/ipv6-hook-input.xml.i b/interface-definitions/include/firewall/ipv6-hook-input.xml.i
index 154b10259..ae78f8910 100644
--- a/interface-definitions/include/firewall/ipv6-hook-input.xml.i
+++ b/interface-definitions/include/firewall/ipv6-hook-input.xml.i
@@ -9,6 +9,7 @@
<help>IPv6 firewall input filter</help>
</properties>
<children>
+ #include <include/firewall/disable-conntrack.xml.i>
#include <include/firewall/default-action-base-chains.xml.i>
#include <include/firewall/default-log.xml.i>
#include <include/generic-description.xml.i>
diff --git a/interface-definitions/include/firewall/ipv6-hook-output.xml.i b/interface-definitions/include/firewall/ipv6-hook-output.xml.i
index 9a6d0bb77..96976b4a8 100644
--- a/interface-definitions/include/firewall/ipv6-hook-output.xml.i
+++ b/interface-definitions/include/firewall/ipv6-hook-output.xml.i
@@ -9,6 +9,7 @@
<help>IPv6 firewall output filter</help>
</properties>
<children>
+ #include <include/firewall/disable-conntrack.xml.i>
#include <include/firewall/default-action-base-chains.xml.i>
#include <include/firewall/default-log.xml.i>
#include <include/generic-description.xml.i>
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index bec0efe5e..89449c938 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -1404,5 +1404,55 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_v6_search, 'ip6 vyos_filter')
+ def test_disable_conntrack_per_chain(self):
+ # If conntrack is disabled in either the input or output chain,
+ # state cannot be matched in either the input or outchain
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'disable-conntrack'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '1', 'state', 'established'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_discard()
+
+ # If conntrack is disabled in the forward chain,
+ # state cannot be matched in the forward chain
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'disable-conntrack'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'established'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_discard()
+
+ # Disable conntrack in all chains for both ipv4 and ipv6
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'disable-conntrack'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'disable-conntrack'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'disable-conntrack'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'disable-conntrack'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'disable-conntrack'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'disable-conntrack'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['VYOS_DISABLE_CONNTRACK_INP_FWD'],
+ ['VYOS_DISABLE_CONNTRACK_OUT'],
+ ['fib daddr . iif type unicast notrack counter'],
+ ['fib daddr . iif type local notrack counter ']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+
+ nftables_search = [
+ ['VYOS_DISABLE_CONNTRACK_INP_FWD_V6'],
+ ['VYOS_DISABLE_CONNTRACK_OUT_V6'],
+ ['fib daddr . iif type unicast notrack counter'],
+ ['fib daddr . iif type local notrack counter ']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index cfccc88da..6630b811d 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -30,6 +30,7 @@ from vyos.ethtool import Ethtool
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
from vyos.template import render
+from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_search_args
from vyos.utils.dict import dict_search_recursive
from vyos.utils.file import write_file
@@ -267,6 +268,24 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf):
if rule_conf.get('protocol', {}) != 'tcp':
raise ConfigError('For action "synproxy" the protocol must be set to TCP')
+ if 'state' in rule_conf:
+ disable_conntrack = dict_search(f'{family}.{hook}.{priority}.disable_conntrack', firewall)
+ conntrack_disabled_list = []
+
+ # Check if conntrack is disabled in the input or output chain
+ for nft_chain in ['input', 'output']:
+ if dict_search(f'{family}.{nft_chain}.filter.disable_conntrack', firewall) == {}:
+ conntrack_disabled_list.append(nft_chain)
+
+ # If conntrack is disabled in the input or output chain,
+ # state cannot be matched in the input or output chain
+ if hook in ['input', 'output'] and conntrack_disabled_list:
+ raise ConfigError(f'state cannot be matched in {hook} when conntrack is disabled in input or output chains')
+ # If conntrack is disabled in the forward chain,
+ # state cannot be matched in the forward chain
+ if hook == 'forward' and disable_conntrack == {}:
+ raise ConfigError(f'state cannot be matched in {hook} when conntrack is disabled in {hook} chain')
+
if 'queue_options' in rule_conf:
if 'queue' not in rule_conf['action']:
raise ConfigError('queue-options defined, but action queue needed and it is not defined')
@@ -480,6 +499,19 @@ def verify(firewall):
for ifname in interfaces:
verify_hardware_offload(ifname)
+ if dict_search('global_options.state_policy', firewall) is not None:
+ # Generate list of chains where conntrack is disabled
+ conntrack_disabled_list = []
+ for inet_family in ['ipv4', 'ipv6']:
+ for nft_chain in ['input', 'forward', 'output']:
+ if dict_search(f'{inet_family}.{nft_chain}.filter.disable_conntrack', firewall) == {}:
+ conntrack_disabled_list.append(f'{inet_family}-{nft_chain}')
+
+ # If conntrack is disabled in any chain,
+ # print a warning message
+ if conntrack_disabled_list:
+ Warning(f'global-state: conntrack is disabled in the following chains: {", ".join(conntrack_disabled_list)}')
+
if 'offload' in firewall.get('global_options', {}).get('state_policy', {}):
offload_path = firewall['global_options']['state_policy']['offload']
if 'offload_target' not in offload_path: