diff options
19 files changed, 335 insertions, 35 deletions
diff --git a/data/templates/dhcp-client/ipv4.j2 b/data/templates/dhcp-client/ipv4.j2 index cc5ddf09c..77905e054 100644 --- a/data/templates/dhcp-client/ipv4.j2 +++ b/data/templates/dhcp-client/ipv4.j2 @@ -9,14 +9,30 @@ interface "{{ ifname }}" { send host-name "{{ dhcp_options.host_name }}"; {% if dhcp_options.client_id is vyos_defined %} {% set client_id = dhcp_options.client_id %} -{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. If not HEX, use double quotes ASCII format #} -{% if not dhcp_options.client_id.split(':') | length >= 5 %} -{% set client_id = '"' + dhcp_options.client_id + '"' %} +{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. #} +{# If not HEX, use double quotes ASCII format #} +{% if not client_id.split(':') | length >= 3 %} +{% set client_id = '"' ~ dhcp_options.client_id ~ '"' %} {% endif %} send dhcp-client-identifier {{ client_id }}; {% endif %} {% if dhcp_options.vendor_class_id is vyos_defined %} - send vendor-class-identifier "{{ dhcp_options.vendor_class_id }}"; +{% set vendor_class_id = dhcp_options.vendor_class_id %} +{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. #} +{# If not HEX, use double quotes ASCII format #} +{% if not vendor_class_id.split(':') | length >= 3 %} +{% set vendor_class_id = '"' ~ dhcp_options.vendor_class_id ~ '"' %} +{% endif %} + send vendor-class-identifier {{ vendor_class_id }}; +{% endif %} +{% if dhcp_options.user_class is vyos_defined %} +{% set user_class = dhcp_options.user_class %} +{# Use HEX representation of client-id as it is send in MAC-address style using hex characters. #} +{# If not HEX, use double quotes ASCII format #} +{% if not user_class.split(':') | length >= 3 %} +{% set user_class = '"' ~ dhcp_options.user_class ~ '"' %} +{% endif %} + send user-class {{ user_class }}; {% endif %} # The request statement causes the client to request that any server responding to the # client send the client its values for the specified options. diff --git a/data/templates/firewall/nftables-offload.j2 b/data/templates/firewall/nftables-offload.j2 new file mode 100644 index 000000000..087fd141c --- /dev/null +++ b/data/templates/firewall/nftables-offload.j2 @@ -0,0 +1,9 @@ +{% macro flowtable(name, config) %} + flowtable VYOS_FLOWTABLE_{{ name }} { + hook ingress priority 0; devices = { {{ config.interface | join(', ') }} }; +{% if config.offload is vyos_defined('hardware') %} + flags offload; +{% endif %} + counter + } +{% endmacro %}
\ No newline at end of file diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2 index 9fcacf677..91c4d0788 100644 --- a/data/templates/firewall/nftables.j2 +++ b/data/templates/firewall/nftables.j2 @@ -2,6 +2,7 @@ {% import 'firewall/nftables-defines.j2' as group_tmpl %} {% import 'firewall/nftables-bridge.j2' as bridge_tmpl %} +{% import 'firewall/nftables-offload.j2' as offload_tmpl %} {% import 'firewall/nftables-zone.j2' as zone_tmpl %} flush chain raw FW_CONNTRACK @@ -38,6 +39,12 @@ delete table ip vyos_filter {% endif %} table ip vyos_filter { {% if ipv4 is vyos_defined %} +{% if flowtable is vyos_defined %} +{% for name, flowtable_conf in flowtable.items() %} +{{ offload_tmpl.flowtable(name, flowtable_conf) }} +{% endfor %} +{% endif %} + {% set ns = namespace(sets=[]) %} {% if ipv4.forward is vyos_defined %} {% for prior, conf in ipv4.forward.items() %} @@ -160,6 +167,12 @@ delete table ip6 vyos_filter {% endif %} table ip6 vyos_filter { {% if ipv6 is vyos_defined %} +{% if flowtable is vyos_defined %} +{% for name, flowtable_conf in flowtable.items() %} +{{ offload_tmpl.flowtable(name, flowtable_conf) }} +{% endfor %} +{% endif %} + {% set ns = namespace(sets=[]) %} {% if ipv6.forward is vyos_defined %} {% for prior, conf in ipv6.forward.items() %} diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index 4704b200e..0bb14a1b3 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -7,6 +7,46 @@ </properties> <children> #include <include/firewall/global-options.xml.i> + <tagNode name="flowtable"> + <properties> + <help>Flowtable</help> + <constraint> + <regex>[a-zA-Z0-9][\w\-\.]*</regex> + </constraint> + </properties> + <children> + #include <include/generic-description.xml.i> + <leafNode name="interface"> + <properties> + <help>Interfaces to use this flowtable</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <multi/> + </properties> + </leafNode> + <leafNode name="offload"> + <properties> + <help>Offloading method</help> + <completionHelp> + <list>hardware software</list> + </completionHelp> + <valueHelp> + <format>hardware</format> + <description>Hardware offload</description> + </valueHelp> + <valueHelp> + <format>software</format> + <description>Software offload</description> + </valueHelp> + <constraint> + <regex>(hardware|software)</regex> + </constraint> + </properties> + <defaultValue>software</defaultValue> + </leafNode> + </children> + </tagNode> <node name="group"> <properties> <help>Firewall group</help> diff --git a/interface-definitions/include/constraint/dhcp-client-string-option.xml.i b/interface-definitions/include/constraint/dhcp-client-string-option.xml.i new file mode 100644 index 000000000..76e0e5466 --- /dev/null +++ b/interface-definitions/include/constraint/dhcp-client-string-option.xml.i @@ -0,0 +1,4 @@ +<!-- include start from include/constraint/dhcp-client-string-option.xml.i --> +<regex>[-_a-zA-Z0-9\s]+</regex> +<regex>([a-fA-F0-9][a-fA-F0-9]:){2,}[a-fA-F0-9][a-fA-F0-9]</regex> +<!-- include end --> diff --git a/interface-definitions/include/firewall/action-forward.xml.i b/interface-definitions/include/firewall/action-forward.xml.i new file mode 100644 index 000000000..87da72c97 --- /dev/null +++ b/interface-definitions/include/firewall/action-forward.xml.i @@ -0,0 +1,45 @@ +<!-- include start from firewall/action-forward.xml.i --> +<leafNode name="action"> + <properties> + <help>Rule action</help> + <completionHelp> + <list>accept continue jump reject return drop queue offload</list> + </completionHelp> + <valueHelp> + <format>accept</format> + <description>Accept matching entries</description> + </valueHelp> + <valueHelp> + <format>continue</format> + <description>Continue parsing next rule</description> + </valueHelp> + <valueHelp> + <format>jump</format> + <description>Jump to another chain</description> + </valueHelp> + <valueHelp> + <format>reject</format> + <description>Reject matching entries</description> + </valueHelp> + <valueHelp> + <format>return</format> + <description>Return from the current chain and continue at the next rule of the last chain</description> + </valueHelp> + <valueHelp> + <format>drop</format> + <description>Drop matching entries</description> + </valueHelp> + <valueHelp> + <format>queue</format> + <description>Enqueue packet to userspace</description> + </valueHelp> + <valueHelp> + <format>offload</format> + <description>Offload packet via flowtable</description> + </valueHelp> + <constraint> + <regex>(accept|continue|jump|reject|return|drop|queue|offload)</regex> + </constraint> + </properties> +</leafNode> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i index 9831498c9..100f1c3d9 100644 --- a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i +++ b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i @@ -25,8 +25,10 @@ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> + #include <include/firewall/action-forward.xml.i> #include <include/firewall/common-rule-ipv4.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> </tagNode> diff --git a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i index 5c86871e5..fb38267eb 100644 --- a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i +++ b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i @@ -25,8 +25,10 @@ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> + #include <include/firewall/action-forward.xml.i> #include <include/firewall/common-rule-ipv6.xml.i> #include <include/firewall/inbound-interface.xml.i> + #include <include/firewall/offload-target.xml.i> #include <include/firewall/outbound-interface.xml.i> </children> </tagNode> diff --git a/interface-definitions/include/firewall/offload-target.xml.i b/interface-definitions/include/firewall/offload-target.xml.i new file mode 100644 index 000000000..b1ae39100 --- /dev/null +++ b/interface-definitions/include/firewall/offload-target.xml.i @@ -0,0 +1,10 @@ +<!-- include start from firewall/offload-target.xml.i --> +<leafNode name="offload-target"> + <properties> + <help>Set flowtable offload target. Action offload must be defined to use this setting</help> + <completionHelp> + <path>firewall flowtable</path> + </completionHelp> + </properties> +</leafNode> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/interface/dhcp-options.xml.i b/interface-definitions/include/interface/dhcp-options.xml.i index 8027769ff..733512a98 100644 --- a/interface-definitions/include/interface/dhcp-options.xml.i +++ b/interface-definitions/include/interface/dhcp-options.xml.i @@ -7,6 +7,13 @@ <leafNode name="client-id"> <properties> <help>Identifier used by client to identify itself to the DHCP server</help> + <valueHelp> + <format>txt</format> + <description>DHCP option string</description> + </valueHelp> + <constraint> + #include <include/constraint/dhcp-client-string-option.xml.i> + </constraint> </properties> </leafNode> <leafNode name="host-name"> @@ -27,6 +34,25 @@ <leafNode name="vendor-class-id"> <properties> <help>Identify the vendor client type to the DHCP server</help> + <valueHelp> + <format>txt</format> + <description>DHCP option string</description> + </valueHelp> + <constraint> + #include <include/constraint/dhcp-client-string-option.xml.i> + </constraint> + </properties> + </leafNode> + <leafNode name="user-class"> + <properties> + <help>Identify to the DHCP server, user configurable option</help> + <valueHelp> + <format>txt</format> + <description>DHCP option string</description> + </valueHelp> + <constraint> + #include <include/constraint/dhcp-client-string-option.xml.i> + </constraint> </properties> </leafNode> #include <include/interface/no-default-route.xml.i> 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 @@ <valueless/> </properties> </leafNode> + <leafNode name="hw-tc-offload"> + <properties> + <help>Enable Hardware Flow Offload</help> + <valueless/> + </properties> + </leafNode> <leafNode name="lro"> <properties> <help>Enable Large Receive Offload</help> 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/firewall.py b/python/vyos/firewall.py index 8ae269fed..4fc1abb15 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -421,19 +421,24 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if 'action' in rule_conf: # Change action=return to action=action # #output.append(nft_action(rule_conf['action'])) - output.append(f'{rule_conf["action"]}') - if 'jump' in rule_conf['action']: - target = rule_conf['jump_target'] - output.append(f'NAME{def_suffix}_{target}') - - if 'queue' in rule_conf['action']: - if 'queue' in rule_conf: - target = rule_conf['queue'] - output.append(f'num {target}') - - if 'queue_options' in rule_conf: - queue_opts = ','.join(rule_conf['queue_options']) - output.append(f'{queue_opts}') + if rule_conf['action'] == 'offload': + offload_target = rule_conf['offload_target'] + output.append(f'flow add @VYOS_FLOWTABLE_{offload_target}') + else: + output.append(f'{rule_conf["action"]}') + + if 'jump' in rule_conf['action']: + target = rule_conf['jump_target'] + output.append(f'NAME{def_suffix}_{target}') + + if 'queue' in rule_conf['action']: + if 'queue' in rule_conf: + target = rule_conf['queue'] + output.append(f'num {target}') + + if 'queue_options' in rule_conf: + queue_opts = ','.join(rule_conf['queue_options']) + output.append(f'{queue_opts}') else: output.append('return') diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 96e5f513b..aa1e87744 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), @@ -256,6 +260,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. @@ -392,6 +415,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/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py index 9329c5ee7..bde1d9aec 100644 --- a/python/vyos/ifconfig/macsec.py +++ b/python/vyos/ifconfig/macsec.py @@ -45,6 +45,10 @@ class MACsecIf(Interface): # create tunnel interface cmd = 'ip link add link {source_interface} {ifname} type {type}'.format(**self.config) cmd += f' cipher {self.config["security"]["cipher"]}' + + if 'encrypt' in self.config["security"]: + cmd += ' encrypt on' + self._cmd(cmd) # Check if using static keys diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 277f14067..6862207e3 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -158,14 +158,22 @@ class BasicInterfaceTest: if not self._test_dhcp or not self._test_vrf: self.skipTest('not supported') + client_id = 'VyOS-router' distance = '100' + hostname = 'vyos' + vendor_class_id = 'vyos-vendor' + user_class = 'vyos' for interface in self._interfaces: for option in self._options.get(interface, []): self.cli_set(self._base_path + [interface] + option.split()) self.cli_set(self._base_path + [interface, 'address', 'dhcp']) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'client-id', client_id]) self.cli_set(self._base_path + [interface, 'dhcp-options', 'default-route-distance', distance]) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'host-name', hostname]) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'vendor-class-id', vendor_class_id]) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'user-class', user_class]) self.cli_commit() @@ -175,8 +183,12 @@ class BasicInterfaceTest: self.assertTrue(dhclient_pid) dhclient_config = read_file(f'{dhclient_base_dir}/dhclient_{interface}.conf') - self.assertIn('request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config) - self.assertIn('require subnet-mask;', dhclient_config) + self.assertIn(f'request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config) + self.assertIn(f'require subnet-mask;', dhclient_config) + self.assertIn(f'send host-name "{hostname}";', dhclient_config) + self.assertIn(f'send dhcp-client-identifier "{client_id}";', dhclient_config) + self.assertIn(f'send vendor-class-identifier "{vendor_class_id}";', dhclient_config) + self.assertIn(f'send user-class "{user_class}";', dhclient_config) # and the commandline has the appropriate options cmdline = read_file(f'/proc/{dhclient_pid}/cmdline') diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index e6c928ad7..18940c04d 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -653,5 +653,43 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): break self.assertTrue(matched) + 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']) + self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'offload-target', 'smoketest']) + self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'protocol', 'tcp_udp']) + self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'established']) + self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'related']) + + self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'action', 'offload']) + self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'offload-target', 'smoketest']) + self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'protocol', 'tcp_udp']) + self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'state', 'established']) + self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'state', 'related']) + + self.cli_commit() + + nftables_search = [ + ['flowtable VYOS_FLOWTABLE_smoketest'], + ['hook ingress priority filter'], + ['devices = { eth0 }'], + ['ct state { established, related }', 'meta l4proto { tcp, udp }', 'flow add @VYOS_FLOWTABLE_smoketest'], + ] + + self.verify_nftables(nftables_search, 'ip vyos_filter') + self.verify_nftables(nftables_search, 'ip6 vyos_filter') + + # Check conntrack + #self.verify_nftables_chain([['accept']], 'ip vyos_conntrack', 'FW_CONNTRACK') + #self.verify_nftables_chain([['accept']], 'ip6 vyos_conntrack', 'FW_CONNTRACK') + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py index ea0f00071..d8d564792 100755 --- a/smoketest/scripts/cli/test_interfaces_macsec.py +++ b/smoketest/scripts/cli/test_interfaces_macsec.py @@ -14,7 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os import re import unittest @@ -23,9 +22,10 @@ from netifaces import interfaces from vyos.configsession import ConfigSessionError from vyos.ifconfig import Section -from vyos.utils.process import cmd from vyos.utils.file import read_file from vyos.utils.network import get_interface_config +from vyos.utils.network import interface_exists +from vyos.utils.process import cmd from vyos.utils.process import process_named_running PROCESS_NAME = 'wpa_supplicant' @@ -35,10 +35,6 @@ def get_config_value(interface, key): tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp) return tmp[0] -def get_cipher(interface): - tmp = get_interface_config(interface) - return tmp['linkinfo']['info_data']['cipher_suite'].lower() - class MACsecInterfaceTest(BasicInterfaceTest.TestCase): @classmethod def setUpClass(cls): @@ -117,6 +113,10 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): tmp = read_file(f'/sys/class/net/{interface}/mtu') self.assertEqual(tmp, '1460') + # Encryption enabled? + tmp = get_interface_config(interface) + self.assertTrue(tmp['linkinfo']['info_data']['encrypt']) + # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -138,10 +138,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): # final commit and verify self.cli_commit() - self.assertIn(interface, interfaces()) + self.assertTrue(interface_exists(interface)) # Verify proper cipher suite (T4537) - self.assertEqual(cipher, get_cipher(interface)) + tmp = get_interface_config(interface) + self.assertEqual(cipher, tmp['linkinfo']['info_data']['cipher_suite'].lower()) def test_macsec_gcm_aes_256(self): src_interface = 'eth0' @@ -161,10 +162,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): # final commit and verify self.cli_commit() - self.assertIn(interface, interfaces()) + self.assertTrue(interface_exists(interface)) # Verify proper cipher suite (T4537) - self.assertEqual(cipher, get_cipher(interface)) + tmp = get_interface_config(interface) + self.assertEqual(cipher, tmp['linkinfo']['info_data']['cipher_suite'].lower()) def test_macsec_source_interface(self): # Ensure source-interface can bot be part of any other bond or bridge @@ -191,7 +193,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): # final commit and verify self.cli_commit() - self.assertIn(interface, interfaces()) + self.assertTrue(interface_exists(interface)) def test_macsec_static_keys(self): src_interface = 'eth0' @@ -205,7 +207,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): peer_mac = '00:11:22:33:44:55' self.cli_set(self._base_path + [interface]) - # Encrypt link + # Encrypt link self.cli_set(self._base_path + [interface, 'security', 'encrypt']) # check validate() - source interface is mandatory @@ -261,9 +263,12 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase): # final commit and verify self.cli_commit() - self.assertIn(interface, interfaces()) - self.assertEqual(cipher2, get_cipher(interface)) - self.assertTrue(os.path.isdir(f'/sys/class/net/{interface}')) + + self.assertTrue(interface_exists(interface)) + tmp = get_interface_config(interface) + self.assertEqual(cipher2, tmp['linkinfo']['info_data']['cipher_suite'].lower()) + # Encryption enabled? + self.assertTrue(tmp['linkinfo']['info_data']['encrypt']) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index c4974d249..1cc146edd 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -26,7 +26,8 @@ from vyos.config import Config 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.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 @@ -160,6 +161,15 @@ def verify_rule(firewall, rule_conf, ipv6): if target not in dict_search_args(firewall, 'ipv6', 'name'): raise ConfigError(f'Invalid jump-target. Firewall ipv6 name {target} does not exist on the system') + if rule_conf['action'] == 'offload': + if 'offload_target' not in rule_conf: + raise ConfigError('Action set to offload, but no offload-target specified') + + offload_target = rule_conf['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 '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') @@ -279,7 +289,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']: |