diff options
| author | Daniil Baturin <daniil@vyos.io> | 2025-09-22 13:55:15 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-22 13:55:15 +0100 |
| commit | c035c4d627ed53c9744d956b7a4d9878e36020cb (patch) | |
| tree | b574a9c1c7d029d82696f55c431caa23008edaa4 | |
| parent | cd04de9af0a98b86937d2055505284bc4448691c (diff) | |
| parent | 61e234a74a95f68c665a21437c9ffaa8747bbc3a (diff) | |
| download | vyos-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
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: |
