summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2025-04-25 18:34:30 +0300
committerGitHub <noreply@github.com>2025-04-25 18:34:30 +0300
commitdf2e9cbc2cbe04a6de0eedfe9154afc87bd3d45d (patch)
treea94ff8fc1770a1f2aa981c9428a414c0180bcd46
parente6909ffe8fc474e181e9b711f1b08974e152a0a7 (diff)
parent929d689cdb58f1037062c2829e1aa45190223c3f (diff)
downloadvyos-1x-df2e9cbc2cbe04a6de0eedfe9154afc87bd3d45d.tar.gz
vyos-1x-df2e9cbc2cbe04a6de0eedfe9154afc87bd3d45d.zip
Merge pull request #4457 from l0crian1/t7358-add-offload-to-global-state
firewall: T7358: add offload option to global state policy
-rwxr-xr-xdata/templates/firewall/nftables.j236
-rw-r--r--interface-definitions/include/firewall/global-options.xml.i8
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py7
-rwxr-xr-xsrc/conf_mode/firewall.py24
4 files changed, 66 insertions, 9 deletions
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 67473da8e..a78119a80 100755
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -47,7 +47,7 @@ table ip vyos_filter {
chain VYOS_FORWARD_{{ prior }} {
type filter hook forward priority {{ prior }}; policy accept;
{% if global_options.state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY
+ jump VYOS_STATE_POLICY_FORWARD
{% endif %}
{% if conf.rule is vyos_defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
@@ -180,6 +180,22 @@ table ip vyos_filter {
{% endif %}
return
}
+
+ chain VYOS_STATE_POLICY_FORWARD {
+{% if global_options.state_policy.offload is vyos_defined %}
+ counter flow add @VYOS_FLOWTABLE_{{ global_options.state_policy.offload.offload_target }}
+{% endif %}
+{% if global_options.state_policy.established is vyos_defined %}
+ {{ global_options.state_policy.established | nft_state_policy('established') }}
+{% endif %}
+{% if global_options.state_policy.invalid is vyos_defined %}
+ {{ global_options.state_policy.invalid | nft_state_policy('invalid') }}
+{% endif %}
+{% if global_options.state_policy.related is vyos_defined %}
+ {{ global_options.state_policy.related | nft_state_policy('related') }}
+{% endif %}
+ return
+ }
{% endif %}
}
@@ -200,7 +216,7 @@ table ip6 vyos_filter {
chain VYOS_IPV6_FORWARD_{{ prior }} {
type filter hook forward priority {{ prior }}; policy accept;
{% if global_options.state_policy is vyos_defined %}
- jump VYOS_STATE_POLICY6
+ jump VYOS_STATE_POLICY6_FORWARD
{% endif %}
{% if conf.rule is vyos_defined %}
{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
@@ -331,6 +347,22 @@ table ip6 vyos_filter {
{% endif %}
return
}
+
+ chain VYOS_STATE_POLICY6_FORWARD {
+{% if global_options.state_policy.offload is vyos_defined %}
+ counter flow add @VYOS_FLOWTABLE_{{ global_options.state_policy.offload.offload_target }}
+{% endif %}
+{% if global_options.state_policy.established is vyos_defined %}
+ {{ global_options.state_policy.established | nft_state_policy('established') }}
+{% endif %}
+{% if global_options.state_policy.invalid is vyos_defined %}
+ {{ global_options.state_policy.invalid | nft_state_policy('invalid') }}
+{% endif %}
+{% if global_options.state_policy.related is vyos_defined %}
+ {{ global_options.state_policy.related | nft_state_policy('related') }}
+{% endif %}
+ return
+ }
{% endif %}
}
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
index 355b41fde..7393ff5c9 100644
--- a/interface-definitions/include/firewall/global-options.xml.i
+++ b/interface-definitions/include/firewall/global-options.xml.i
@@ -217,6 +217,14 @@
<help>Global firewall state-policy</help>
</properties>
<children>
+ <node name="offload">
+ <properties>
+ <help>All stateful forward traffic is offloaded to a flowtable</help>
+ </properties>
+ <children>
+ #include <include/firewall/offload-target.xml.i>
+ </children>
+ </node>
<node name="established">
<properties>
<help>Global firewall policy for packets part of an established connection</help>
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 2829edbfb..69de0c326 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -642,6 +642,10 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
def test_ipv4_global_state(self):
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'software'])
+
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'offload', 'offload-target', 'smoketest'])
self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept'])
self.cli_set(['firewall', 'global-options', 'state-policy', 'related', 'action', 'accept'])
self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'drop'])
@@ -651,6 +655,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
nftables_search = [
['jump VYOS_STATE_POLICY'],
['chain VYOS_STATE_POLICY'],
+ ['jump VYOS_STATE_POLICY_FORWARD'],
+ ['chain VYOS_STATE_POLICY_FORWARD'],
+ ['flow add @VYOS_FLOWTABLE_smoketest'],
['ct state established', 'accept'],
['ct state invalid', 'drop'],
['ct state related', 'accept']
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 72f2d39f4..274ca2ce6 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -205,7 +205,7 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf):
if 'jump' not in rule_conf['action']:
raise ConfigError('jump-target defined, but action jump needed and it is not defined')
target = rule_conf['jump_target']
- if hook != 'name': # This is a bit clumsy, but consolidates a chunk of code.
+ if hook != 'name': # This is a bit clumsy, but consolidates a chunk of code.
verify_jump_target(firewall, hook, target, family, recursive=True)
else:
verify_jump_target(firewall, hook, target, family, recursive=False)
@@ -268,12 +268,12 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf):
if dict_search_args(rule_conf, 'gre', 'flags', 'checksum') is None:
# There is no builtin match in nftables for the GRE key, so we need to do a raw lookup.
- # The offset of the key within the packet shifts depending on the C-flag.
- # 99% of the time, nobody will have checksums enabled - it's usually a manual config option.
- # We can either assume it is unset unless otherwise directed
+ # The offset of the key within the packet shifts depending on the C-flag.
+ # 99% of the time, nobody will have checksums enabled - it's usually a manual config option.
+ # We can either assume it is unset unless otherwise directed
# (confusing, requires doco to explain why it doesn't work sometimes)
- # or, demand an explicit selection to be made for this specific match rule.
- # This check enforces the latter. The user is free to create rules for both cases.
+ # or, demand an explicit selection to be made for this specific match rule.
+ # This check enforces the latter. The user is free to create rules for both cases.
raise ConfigError('Matching GRE tunnel key requires an explicit checksum flag match. For most cases, use "gre flags checksum unset"')
if dict_search_args(rule_conf, 'gre', 'flags', 'key', 'unset') is not None:
@@ -286,7 +286,7 @@ def verify_rule(firewall, family, hook, priority, rule_id, rule_conf):
if gre_inner_value < 0 or gre_inner_value > 65535:
raise ConfigError('inner-proto outside valid ethertype range 0-65535')
except ValueError:
- pass # Symbolic constant, pre-validated before reaching here.
+ pass # Symbolic constant, pre-validated before reaching here.
tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
if tcp_flags:
@@ -437,6 +437,16 @@ def verify(firewall):
for ifname in interfaces:
verify_hardware_offload(ifname)
+ 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:
+ raise ConfigError('offload-target must be specified')
+
+ offload_target = offload_path['offload_target']
+
+ if not dict_search_args(firewall, 'flowtable', offload_target):
+ raise ConfigError(f'Invalid offload-target. Flowtable "{offload_target}" does not exist on the system')
+
if 'group' in firewall:
for group_type in nested_group_types:
if group_type in firewall['group']: