From bbe32749e66c208312dffadbbd076fdc34ceeb5b Mon Sep 17 00:00:00 2001
From: sarthurdev <965089+sarthurdev@users.noreply.github.com>
Date: Sun, 17 Sep 2023 20:51:44 +0200
Subject: firewall: ethernet: T4502: Add interface offload node and verify
interface supports HW flowtable offload
- Add required offload setting for interfaces + flowtable offload (hw-tc-offload)
- Verification of interface support for hardware offloaded flowtables
---
interface-definitions/interfaces-ethernet.xml.in | 6 +++++
python/vyos/ethtool.py | 3 +++
python/vyos/ifconfig/ethernet.py | 26 +++++++++++++++++++
smoketest/scripts/cli/test_firewall.py | 8 +++++-
src/conf_mode/firewall.py | 33 ++++++++++++++++++------
5 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 3669336fd..5aaa7095c 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -80,6 +80,12 @@
+
+
+ Enable Hardware Flow Offload
+
+
+
Enable Large Receive Offload
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index ca3bcfc3d..f19632719 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -172,6 +172,9 @@ class Ethtool:
def get_generic_segmentation_offload(self):
return self._get_generic('generic-segmentation-offload')
+ def get_hw_tc_offload(self):
+ return self._get_generic('hw-tc-offload')
+
def get_large_receive_offload(self):
return self._get_generic('large-receive-offload')
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 24ce3a803..285542057 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -57,6 +57,10 @@ class EthernetIf(Interface):
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'gso', v),
},
+ 'hw-tc-offload': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'hw-tc-offload', v),
+ },
'lro': {
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'lro', v),
@@ -222,6 +226,25 @@ class EthernetIf(Interface):
print('Adapter does not support changing generic-segmentation-offload settings!')
return False
+ def set_hw_tc_offload(self, state):
+ """
+ Enable hardware TC flow offload. State can be either True or False.
+ Example:
+ >>> from vyos.ifconfig import EthernetIf
+ >>> i = EthernetIf('eth0')
+ >>> i.set_hw_tc_offload(True)
+ """
+ if not isinstance(state, bool):
+ raise ValueError('Value out of range')
+
+ enabled, fixed = self.ethtool.get_hw_tc_offload()
+ if enabled != state:
+ if not fixed:
+ return self.set_interface('hw-tc-offload', 'on' if state else 'off')
+ else:
+ print('Adapter does not support changing hw-tc-offload settings!')
+ return False
+
def set_lro(self, state):
"""
Enable Large Receive offload. State can be either True or False.
@@ -358,6 +381,9 @@ class EthernetIf(Interface):
# GSO (generic segmentation offload)
self.set_gso(dict_search('offload.gso', config) != None)
+ # GSO (generic segmentation offload)
+ self.set_hw_tc_offload(dict_search('offload.hw-tc-offload', config) != None)
+
# LRO (large receive offload)
self.set_lro(dict_search('offload.lro', config) != None)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 75fdec207..72e04847a 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -603,8 +603,14 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with open(path, 'r') as f:
self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
- def test_flow_offload_software(self):
+ def test_flow_offload(self):
self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'hardware'])
+
+ # QEMU virtual NIC does not support hw-tc-offload
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'software'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'offload'])
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index eba3ebf59..3d799318e 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -27,6 +27,7 @@ from vyos.configdict import node_changed
from vyos.configdiff import get_config_diff, Diff
from vyos.configdep import set_dependents, call_dependents
from vyos.configverify import verify_interface_exists
+from vyos.ethtool import Ethtool
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
from vyos.template import render
@@ -291,7 +292,31 @@ def verify_nested_group(group_name, group, groups, seen):
if 'include' in groups[g]:
verify_nested_group(g, groups[g], groups, seen)
+def verify_hardware_offload(ifname):
+ ethtool = Ethtool(ifname)
+ enabled, fixed = ethtool.get_hw_tc_offload()
+
+ if not enabled and fixed:
+ raise ConfigError(f'Interface "{ifname}" does not support hardware offload')
+
+ if not enabled:
+ raise ConfigError(f'Interface "{ifname}" requires "offload hw-tc-offload"')
+
def verify(firewall):
+ if 'flowtable' in firewall:
+ for flowtable, flowtable_conf in firewall['flowtable'].items():
+ if 'interface' not in flowtable_conf:
+ raise ConfigError(f'Flowtable "{flowtable}" requires at least one interface')
+
+ for ifname in flowtable_conf['interface']:
+ verify_interface_exists(ifname)
+
+ if dict_search_args(flowtable_conf, 'offload') == 'hardware':
+ interfaces = flowtable_conf['interface']
+
+ for ifname in interfaces:
+ verify_hardware_offload(ifname)
+
if 'group' in firewall:
for group_type in nested_group_types:
if group_type in firewall['group']:
@@ -339,14 +364,6 @@ def verify(firewall):
for rule_id, rule_conf in name_conf['rule'].items():
verify_rule(firewall, rule_conf, True)
- # Verify flow offload options
- flow_offload = dict_search_args(firewall, 'global_options', 'flow_offload')
- for offload_type in ('software', 'hardware'):
- interfaces = dict_search_args(flow_offload, offload_type, 'interface') or []
- for interface in interfaces:
- # nft will raise an error when adding a non-existent interface to a flowtable
- verify_interface_exists(interface)
-
return None
def generate(firewall):
--
cgit v1.2.3