diff options
-rw-r--r-- | .github/workflows/cla-check.yml | 19 | ||||
-rw-r--r-- | data/templates/firewall/nftables-defines.j2 | 2 | ||||
-rw-r--r-- | data/templates/firewall/nftables-zone.j2 | 36 | ||||
-rw-r--r-- | interface-definitions/qos.xml.in | 19 | ||||
-rw-r--r-- | pyproject.toml | 2 | ||||
-rwxr-xr-x | python/vyos/firewall.py | 4 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 4 | ||||
-rw-r--r-- | python/vyos/qos/cake.py | 9 | ||||
-rwxr-xr-x | python/vyos/template.py | 4 | ||||
-rwxr-xr-x | python/vyos/xml_ref/generate_op_cache.py | 23 | ||||
-rw-r--r-- | python/vyos/xml_ref/op_definition.py | 3 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_qos.py | 19 | ||||
-rwxr-xr-x | src/conf_mode/vrf.py | 4 | ||||
-rwxr-xr-x | src/init/vyos-router | 5 |
14 files changed, 125 insertions, 28 deletions
diff --git a/.github/workflows/cla-check.yml b/.github/workflows/cla-check.yml new file mode 100644 index 000000000..3c1aeee67 --- /dev/null +++ b/.github/workflows/cla-check.yml @@ -0,0 +1,19 @@ +name: "CLA Check" + +permissions: + actions: write + contents: read + pull-requests: write + statuses: write + +on: + pull_request: + types: [opened, synchronize, closed] + issue_comment: + types: [created] + +jobs: + call-cla-assistant: + uses: vyos/vyos-cla-signatures/.github/workflows/cla-reusable.yml@current + secrets: + CLA_PAT: ${{ secrets.CLA_PAT }} diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2 index a1d1fa4f6..c4b6b7eba 100644 --- a/data/templates/firewall/nftables-defines.j2 +++ b/data/templates/firewall/nftables-defines.j2 @@ -111,7 +111,7 @@ flags interval auto-merge {% if group_conf.interface is vyos_defined or includes %} - elements = { {{ group_conf.interface | nft_nested_group(includes, group.interface_group, 'interface') | join(",") }} } + elements = { {{ group_conf.interface | nft_nested_group(includes, group.interface_group, 'interface') | quoted_join(",") }} } {% endif %} } {% endfor %} diff --git a/data/templates/firewall/nftables-zone.j2 b/data/templates/firewall/nftables-zone.j2 index 645a38706..66f7e0b1c 100644 --- a/data/templates/firewall/nftables-zone.j2 +++ b/data/templates/firewall/nftables-zone.j2 @@ -9,11 +9,11 @@ {% for zone_name, zone_conf in zone.items() %} {% if 'local_zone' not in zone_conf %} {% if 'interface' in zone_conf.member %} - oifname { {{ zone_conf.member.interface | join(',') }} } counter jump VZONE_{{ zone_name }} + oifname { {{ zone_conf.member.interface | quoted_join(',') }} } counter jump VZONE_{{ zone_name }} {% endif %} {% if 'vrf' in zone_conf.member %} {% for vrf_name in zone_conf.member.vrf %} - oifname { {{ zone_conf['vrf_interfaces'][vrf_name] }} } counter jump VZONE_{{ zone_name }} + oifname { "{{ zone_conf['vrf_interfaces'][vrf_name] }}" } counter jump VZONE_{{ zone_name }} {% endfor %} {% endif %} {% endif %} @@ -49,12 +49,12 @@ {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %} {% if 'interface' in zone[from_zone].member %} - iifname { {{ zone[from_zone].member.interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} - iifname { {{ zone[from_zone].member.interface | join(",") }} } counter return + iifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} + iifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter return {% endif %} {% if 'vrf' in zone[from_zone].member %} - iifname { {{ zone[from_zone].member.vrf | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} - iifname { {{ zone[from_zone].member.vrf | join(",") }} } counter return + iifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} + iifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter return {% endif %} {% endfor %} {% endif %} @@ -65,13 +65,13 @@ {% if zone_conf.from_local is vyos_defined %} {% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall[fw_name] is vyos_defined %} {% if 'interface' in zone[from_zone].member %} - oifname { {{ zone[from_zone].member.interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} - oifname { {{ zone[from_zone].member.interface | join(",") }} } counter return + oifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} + oifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter return {% endif %} {% if 'vrf' in zone[from_zone].member %} {% for vrf_name in zone[from_zone].member.vrf %} - oifname { {{ zone[from_zone]['vrf_interfaces'][vrf_name] }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} - oifname { {{ zone[from_zone]['vrf_interfaces'][vrf_name] }} } counter return + oifname { "{{ zone[from_zone]['vrf_interfaces'][vrf_name] }}" } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} + oifname { "{{ zone[from_zone]['vrf_interfaces'][vrf_name] }}" } counter return {% endfor %} {% endif %} {% endfor %} @@ -81,29 +81,29 @@ {% else %} chain VZONE_{{ zone_name }} { {% if 'interface' in zone_conf.member %} - iifname { {{ zone_conf.member.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }} + iifname { {{ zone_conf.member.interface | quoted_join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }} {% endif %} {% if 'vrf' in zone_conf.member %} - iifname { {{ zone_conf.member.vrf | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }} + iifname { {{ zone_conf.member.vrf | quoted_join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }} {% endif %} {% if zone_conf.intra_zone_filtering is vyos_defined %} {% if 'interface' in zone_conf.member %} - iifname { {{ zone_conf.member.interface | join(",") }} } counter return + iifname { {{ zone_conf.member.interface | quoted_join(",") }} } counter return {% endif %} {% if 'vrf' in zone_conf.member %} - iifname { {{ zone_conf.member.vrf | join(",") }} } counter return + iifname { {{ zone_conf.member.vrf | quoted_join(",") }} } counter return {% endif %} {% endif %} {% if zone_conf.from is vyos_defined %} {% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %} {% if zone[from_zone].local_zone is not defined %} {% if 'interface' in zone[from_zone].member %} - iifname { {{ zone[from_zone].member.interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} - iifname { {{ zone[from_zone].member.interface | join(",") }} } counter return + iifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} + iifname { {{ zone[from_zone].member.interface | quoted_join(",") }} } counter return {% endif %} {% if 'vrf' in zone[from_zone].member %} - iifname { {{ zone[from_zone].member.vrf | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} - iifname { {{ zone[from_zone].member.vrf | join(",") }} } counter return + iifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }} + iifname { {{ zone[from_zone].member.vrf | quoted_join(",") }} } counter return {% endif %} {% endif %} {% endfor %} diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in index c6ecb742e..aad1de629 100644 --- a/interface-definitions/qos.xml.in +++ b/interface-definitions/qos.xml.in @@ -135,6 +135,25 @@ <valueless/> </properties> </leafNode> + <leafNode name="no-split-gso"> + <properties> + <help>Do not split GSO super-packets into on-the-wire components</help> + <valueless/> + </properties> + </leafNode> + <node name="ack-filter"> + <properties> + <help>Identify and filter out TCP ACK packets that do not convey significant new information</help> + </properties> + <children> + <leafNode name="aggressive"> + <properties> + <help>Enable aggressive mode which will result in more ACK packets being compresses/filtered</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> <leafNode name="rtt"> <properties> <help>Round-Trip-Time for Active Queue Management (AQM)</help> diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..76597715e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +skip-string-normalization = true
\ No newline at end of file diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 5bb7afecc..b136b6fca 100755 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -361,7 +361,7 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if iiface[0] == '!': operator = '!=' iiface = iiface[1:] - output.append(f'iifname {operator} {{{iiface}}}') + output.append(f'iifname {operator} {{"{iiface}"}}') elif 'group' in rule_conf['inbound_interface']: iiface = rule_conf['inbound_interface']['group'] if iiface[0] == '!': @@ -376,7 +376,7 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if oiface[0] == '!': operator = '!=' oiface = oiface[1:] - output.append(f'oifname {operator} {{{oiface}}}') + output.append(f'oifname {operator} {{"{oiface}"}}') elif 'group' in rule_conf['outbound_interface']: oiface = rule_conf['outbound_interface']['group'] if oiface[0] == '!': diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index ca50d6ec1..787364483 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -423,11 +423,11 @@ class Interface(Control): self._cmd(f'nft {nft_command}') def _del_interface_from_ct_iface_map(self): - nft_command = f'delete element inet vrf_zones ct_iface_map {{ "{self.ifname}" }}' + nft_command = f'delete element inet vrf_zones ct_iface_map {{ \'"{self.ifname}"\' }}' self._nft_check_and_run(nft_command) def _add_interface_to_ct_iface_map(self, vrf_table_id: int): - nft_command = f'add element inet vrf_zones ct_iface_map {{ "{self.ifname}" : {vrf_table_id} }}' + nft_command = f'add element inet vrf_zones ct_iface_map {{ \'"{self.ifname}"\' : {vrf_table_id} }}' self._nft_check_and_run(nft_command) def get_ifindex(self): diff --git a/python/vyos/qos/cake.py b/python/vyos/qos/cake.py index 626cedb8f..05a737649 100644 --- a/python/vyos/qos/cake.py +++ b/python/vyos/qos/cake.py @@ -54,7 +54,16 @@ class CAKE(QoSBase): f'Invalid flow isolation parameter: {config["flow_isolation"]}' ) + if 'ack_filter' in config: + if 'aggressive' in config['ack_filter']: + tmp += ' ack-filter-aggressive' + else: + tmp += ' ack-filter' + else: + tmp += ' no-ack-filter' + tmp += ' nat' if 'flow_isolation_nat' in config else ' nonat' + tmp += ' no-split-gso' if 'no_split_gso' in config else ' split-gso' self._cmd(tmp) diff --git a/python/vyos/template.py b/python/vyos/template.py index 9a9234490..824d42136 100755 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -582,6 +582,10 @@ def snmp_auth_oid(type): } return OIDs[type] +@register_filter('quoted_join') +def quoted_join(input_list, join_str, quote='"'): + return str(join_str).join(f'{quote}{elem}{quote}' for elem in input_list) + @register_filter('nft_action') def nft_action(vyos_action): if vyos_action == 'accept': diff --git a/python/vyos/xml_ref/generate_op_cache.py b/python/vyos/xml_ref/generate_op_cache.py index 7a6974730..266c81cd0 100755 --- a/python/vyos/xml_ref/generate_op_cache.py +++ b/python/vyos/xml_ref/generate_op_cache.py @@ -140,9 +140,16 @@ def insert_node( prop: OptElement = n.find('properties') children: OptElement = n.find('children') command: OptElement = n.find('command') - # name is not None as required by schema - name: str = n.get('name', 'schema_error') + standalone: OptElement = n.find('standalone') node_type: str = n.tag + + if node_type == 'virtualTagNode': + name = '__virtual_tag' + else: + name = n.get('name') + if not name: + raise ValueError("Node name is required for all node types except <virtualTagNode>") + if path is None: path = [] @@ -156,6 +163,16 @@ def insert_node( if command_text is not None: command_text = translate_command(command_text, path) + try: + standalone_command = translate_command(standalone.find('command').text, path) + except AttributeError: + standalone_command = None + + try: + standalone_help_text = translate_command(standalone.find('help').text, path) + except AttributeError: + standalone_help_text = None + comp_help = {} if prop is not None: che = prop.findall('completionHelp') @@ -191,6 +208,8 @@ def insert_node( cur_node_data.comp_help = comp_help cur_node_data.help_text = help_text cur_node_data.command = command_text + cur_node_data.standalone_help_text = standalone_help_text + cur_node_data.standalone_command = standalone_command cur_node_data.path = path cur_node_data.file = file diff --git a/python/vyos/xml_ref/op_definition.py b/python/vyos/xml_ref/op_definition.py index f749e0585..7b0a45a5b 100644 --- a/python/vyos/xml_ref/op_definition.py +++ b/python/vyos/xml_ref/op_definition.py @@ -15,6 +15,7 @@ from typing import TypeAlias from typing import Union +from typing import Optional from typing import Iterator from dataclasses import dataclass from dataclasses import field @@ -31,6 +32,8 @@ class NodeData: help_text: str = '' comp_help: dict[str, list] = field(default_factory=dict) command: str = '' + standalone_help_text: Optional[str] = None + standalone_command: Optional[str] = None path: list[str] = field(default_factory=list) file: str = '' children: list[tuple] = field(default_factory=list) diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py index 630e78ea8..03160ec7e 100755 --- a/smoketest/scripts/cli/test_qos.py +++ b/smoketest/scripts/cli/test_qos.py @@ -884,6 +884,8 @@ class TestQoS(VyOSUnitTestSHIM.TestCase): base_path + ['policy', 'cake', policy_name, 'bandwidth', str(bandwidth)] ) self.cli_set(base_path + ['policy', 'cake', policy_name, 'rtt', str(rtt)]) + self.cli_set(base_path + ['policy', 'cake', policy_name, 'no-split-gso']) + self.cli_set(base_path + ['policy', 'cake', policy_name, 'ack-filter', 'aggressive']) # commit changes self.cli_commit() @@ -899,6 +901,23 @@ class TestQoS(VyOSUnitTestSHIM.TestCase): self.assertFalse(tmp['options']['ingress']) self.assertFalse(tmp['options']['nat']) self.assertTrue(tmp['options']['raw']) + self.assertFalse(tmp['options']['split_gso']) + self.assertEqual(tmp['options']['ack-filter'], 'aggressive') + + self.cli_delete(base_path + ['policy', 'cake', policy_name, 'ack-filter', 'aggressive']) + self.cli_commit() + tmp = get_tc_qdisc_json(interface) + self.assertEqual(tmp['options']['ack-filter'], 'enabled') + + self.cli_delete(base_path + ['policy', 'cake', policy_name, 'ack-filter']) + self.cli_commit() + tmp = get_tc_qdisc_json(interface) + self.assertEqual(tmp['options']['ack-filter'], 'disabled') + + self.cli_delete(base_path + ['policy', 'cake', policy_name, 'no-split-gso']) + self.cli_commit() + tmp = get_tc_qdisc_json(interface) + self.assertTrue(tmp['options']['split_gso']) nat = True for flow_isolation in [ diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index 39f55e724..1eacba112 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -240,7 +240,7 @@ def apply(vrf): vrf_iface.set_dhcpv6(False) # Remove nftables conntrack zone map item - nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{tmp}" }}' + nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ \'"{tmp}"\' }}' # Check if deleting is possible first to avoid raising errors _, err = popen(f'nft --check {nft_del_element}') if not err: @@ -320,7 +320,7 @@ def apply(vrf): state = 'down' if 'disable' in config else 'up' vrf_if.set_admin_state(state) # Add nftables conntrack zone map item - nft_add_element = f'add element inet vrf_zones ct_iface_map {{ "{name}" : {table} }}' + nft_add_element = f'add element inet vrf_zones ct_iface_map {{ \'"{name}"\' : {table} }}' cmd(f'nft {nft_add_element}') # Only call into nftables as long as there is nothing setup to avoid wasting diff --git a/src/init/vyos-router b/src/init/vyos-router index f93e360da..563f12755 100755 --- a/src/init/vyos-router +++ b/src/init/vyos-router @@ -420,7 +420,10 @@ gen_duid () UUID=$(cat ${UUID_FILE} | tr -d -) fi if [ -z ${UUID} ]; then - UUID=$(uuidgen --sha1 --namespace @dns --name $(cat ${UUID_FILE_ALT}) | tr -d -) + file_alt="$(cat ${UUID_FILE_ALT})" + if [ -n "${file_alt}" ]; then + UUID=$(uuidgen --sha1 --namespace @dns --name ${file_alt} | tr -d -) + fi fi # Add DUID type4 (UUID) information DUID_TYPE="0004" |