diff options
26 files changed, 646 insertions, 59 deletions
diff --git a/data/templates/conntrack/nftables-helpers.j2 b/data/templates/conntrack/nftables-helpers.j2 index 433931162..63a0cc855 100644 --- a/data/templates/conntrack/nftables-helpers.j2 +++ b/data/templates/conntrack/nftables-helpers.j2 @@ -31,6 +31,12 @@ } {% endif %} +{% if modules.rtsp is vyos_defined and ipv4 %} + ct helper rtsp_tcp { + type "rtsp" protocol tcp; + } +{% endif %} + {% if modules.sip is vyos_defined %} ct helper sip_tcp { type "sip" protocol tcp; diff --git a/data/templates/router-advert/radvd.conf.j2 b/data/templates/router-advert/radvd.conf.j2 index 4ef4751dd..97180d164 100644 --- a/data/templates/router-advert/radvd.conf.j2 +++ b/data/templates/router-advert/radvd.conf.j2 @@ -50,6 +50,13 @@ interface {{ iface }} { {% endfor %} }; {% endif %} +{% if iface_config.nat64prefix is vyos_defined %} +{% for nat64prefix, nat64prefix_options in iface_config.nat64prefix.items() %} + nat64prefix {{ nat64prefix }} { + AdvValidLifetime {{ nat64prefix_options.valid_lifetime }}; + }; +{% endfor %} +{% endif %} {% if iface_config.prefix is vyos_defined %} {% for prefix, prefix_options in iface_config.prefix.items() %} prefix {{ prefix }} { diff --git a/debian/control b/debian/control index dddc4e14c..c5a60f660 100644 --- a/debian/control +++ b/debian/control @@ -256,6 +256,9 @@ Depends: # For "nat64" jool, # End "nat64" +# For "system conntrack modules rtsp" + nat-rtsp, +# End "system conntrack modules rtsp" # For "system ntp" chrony, # End "system ntp" diff --git a/interface-definitions/include/firewall/conntrack-helper.xml.i b/interface-definitions/include/firewall/conntrack-helper.xml.i index ee17f2c61..3ca1a0353 100644 --- a/interface-definitions/include/firewall/conntrack-helper.xml.i +++ b/interface-definitions/include/firewall/conntrack-helper.xml.i @@ -22,6 +22,10 @@ <description>Related traffic from NFS helper</description> </valueHelp> <valueHelp> + <format>rtsp</format> + <description>Related traffic from RTSP helper</description> + </valueHelp> + <valueHelp> <format>sip</format> <description>Related traffic from SIP helper</description> </valueHelp> @@ -34,7 +38,7 @@ <description>Related traffic from SQLNet helper</description> </valueHelp> <constraint> - <regex>(ftp|h323|pptp|nfs|sip|tftp|sqlnet)</regex> + <regex>(ftp|h323|pptp|nfs|rtsp|sip|tftp|sqlnet)</regex> </constraint> <multi/> </properties> diff --git a/interface-definitions/nat64.xml.in b/interface-definitions/nat64.xml.in index dfdd295d2..4b3c157cc 100644 --- a/interface-definitions/nat64.xml.in +++ b/interface-definitions/nat64.xml.in @@ -2,7 +2,7 @@ <interfaceDefinition> <node name="nat64" owner="${vyos_conf_scripts_dir}/nat64.py"> <properties> - <help>IPv6-to-IPv4 Network Address Translation (NAT64) Settings</help> + <help>Network Address Translation (NAT64) parameters</help> <priority>501</priority> </properties> <children> diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in index 1518de8bd..32d501cce 100644 --- a/interface-definitions/nat66.xml.in +++ b/interface-definitions/nat66.xml.in @@ -2,7 +2,7 @@ <interfaceDefinition> <node name="nat66" owner="${vyos_conf_scripts_dir}/nat66.py"> <properties> - <help>IPv6-to-IPv6 Network Prefix Translation (NAT66/NPT) Settings</help> + <help>Network Prefix Translation (NAT66/NPTv6) parameters</help> <priority>500</priority> </properties> <children> diff --git a/interface-definitions/pki.xml.in b/interface-definitions/pki.xml.in index 7a0b073b4..b922771c1 100644 --- a/interface-definitions/pki.xml.in +++ b/interface-definitions/pki.xml.in @@ -2,7 +2,7 @@ <interfaceDefinition> <node name="pki" owner="${vyos_conf_scripts_dir}/pki.py"> <properties> - <help>VyOS PKI configuration</help> + <help>Public key infrastructure (PKI)</help> <priority>300</priority> </properties> <children> diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 0d82cd3f8..791fa1d87 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -1124,12 +1124,20 @@ <leafNode name="exclude"> <properties> <help>Remove/exclude from the as-path attribute</help> + <completionHelp> + <list>all</list> + </completionHelp> <valueHelp> - <format>u32</format> + <format>u32:1-4294967295</format> <description>AS number</description> </valueHelp> + <valueHelp> + <format>all</format> + <description>Exclude all AS numbers from the as-path</description> + </valueHelp> <constraint> <validator name="as-number-list"/> + <regex>(all)</regex> </constraint> </properties> </leafNode> @@ -1137,7 +1145,7 @@ <properties> <help>Prepend to the as-path</help> <valueHelp> - <format>u32</format> + <format>u32:1-4294967295</format> <description>AS number</description> </valueHelp> <constraint> diff --git a/interface-definitions/service_config-sync.xml.in b/interface-definitions/service_config-sync.xml.in index 9955acfee..9e9dcdb69 100644 --- a/interface-definitions/service_config-sync.xml.in +++ b/interface-definitions/service_config-sync.xml.in @@ -73,30 +73,382 @@ </constraint> </properties> </leafNode> - <leafNode name="section"> + <node name="section"> <properties> <help>Section for synchronization</help> - <completionHelp> - <list>nat nat66 firewall</list> - </completionHelp> - <valueHelp> - <format>nat</format> - <description>NAT</description> - </valueHelp> - <valueHelp> - <format>nat66</format> - <description>NAT66</description> - </valueHelp> - <valueHelp> - <format>firewall</format> - <description>firewall</description> - </valueHelp> - <constraint> - <regex>(nat|nat66|firewall)</regex> - </constraint> - <multi/> </properties> - </leafNode> + <children> + <leafNode name="firewall"> + <properties> + <help>Firewall</help> + <valueless/> + </properties> + </leafNode> + <node name="interfaces"> + <properties> + <help>Interfaces</help> + </properties> + <children> + <leafNode name="bonding"> + <properties> + <help>Bonding interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="bridge"> + <properties> + <help>Bridge interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dummy"> + <properties> + <help>Dummy interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ethernet"> + <properties> + <help>Ethernet interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="geneve"> + <properties> + <help>GENEVE interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="input"> + <properties> + <help>Input interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="l2tpv3"> + <properties> + <help>L2TPv3 interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="loopback"> + <properties> + <help>Loopback interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="macsec"> + <properties> + <help>MACsec interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="openvpn"> + <properties> + <help>OpenVPN interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pppoe"> + <properties> + <help>PPPoE interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pseudo-ethernet"> + <properties> + <help>Pseudo-Ethernet interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="sstpc"> + <properties> + <help>SSTP client interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="tunnel"> + <properties> + <help>Tunnel interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="virtual-ethernet"> + <properties> + <help>Virtual Ethernet interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="vti"> + <properties> + <help>Virtual tunnel interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="vxlan"> + <properties> + <help>VXLAN interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="wireguard"> + <properties> + <help>Wireguard interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="wireless"> + <properties> + <help>Wireless interface</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="wwan"> + <properties> + <help>WWAN interface</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="nat"> + <properties> + <help>NAT</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="nat66"> + <properties> + <help>NAT66</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pki"> + <properties> + <help>Public key infrastructure (PKI)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="policy"> + <properties> + <help>Routing policy</help> + <valueless/> + </properties> + </leafNode> + <node name="protocols"> + <properties> + <help>Routing protocols</help> + </properties> + <children> + <leafNode name="babel"> + <properties> + <help>Babel Routing Protocol</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="bfd"> + <properties> + <help>Bidirectional Forwarding Detection (BFD)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="bgp"> + <properties> + <help>Border Gateway Protocol (BGP)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="failover"> + <properties> + <help>Failover route</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="igmp-proxy"> + <properties> + <help>Internet Group Management Protocol (IGMP) proxy</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="isis"> + <properties> + <help>Intermediate System to Intermediate System (IS-IS)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="mpls"> + <properties> + <help>Multiprotocol Label Switching (MPLS)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="nhrp"> + <properties> + <help>Next Hop Resolution Protocol (NHRP) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ospf"> + <properties> + <help>Open Shortest Path First (OSPF)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ospfv3"> + <properties> + <help>Open Shortest Path First (OSPF) for IPv6</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pim"> + <properties> + <help>Protocol Independent Multicast (PIM) and IGMP</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="pim6"> + <properties> + <help>Protocol Independent Multicast for IPv6 (PIMv6) and MLD</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="rip"> + <properties> + <help>Routing Information Protocol (RIP) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ripng"> + <properties> + <help>Routing Information Protocol (RIPng) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="rpki"> + <properties> + <help>Resource Public Key Infrastructure (RPKI)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="segment-routing"> + <properties> + <help>Segment Routing</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="static"> + <properties> + <help>Static Routing</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <node name="service"> + <properties> + <help>System services</help> + </properties> + <children> + <leafNode name="console-server"> + <properties> + <help>Serial Console Server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcp-relay"> + <properties> + <help>Host Configuration Protocol (DHCP) relay agent</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcp-server"> + <properties> + <help>Dynamic Host Configuration Protocol (DHCP) for DHCP server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcpv6-relay"> + <properties> + <help>DHCPv6 Relay Agent parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dhcpv6-server"> + <properties> + <help>DHCP for IPv6 (DHCPv6) server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="dns"> + <properties> + <help>Domain Name System (DNS) related services</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="lldp"> + <properties> + <help>LLDP settings</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="mdns"> + <properties> + <help>Multicast DNS (mDNS) parameters</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="monitoring"> + <properties> + <help>Monitoring services</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ndp-proxy"> + <properties> + <help>Neighbor Discovery Protocol (NDP) Proxy</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="ntp"> + <properties> + <help>Network Time Protocol (NTP) configuration</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="snmp"> + <properties> + <help>Simple Network Management Protocol (SNMP)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="tftp-server"> + <properties> + <help>Trivial File Transfer Protocol (TFTP) server</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="webproxy"> + <properties> + <help>Webproxy service settings</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <leafNode name="vpn"> + <properties> + <help>Virtual Private Network (VPN)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="vrf"> + <properties> + <help>Virtual Routing and Forwarding</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> </children> </node> </children> diff --git a/interface-definitions/service_router-advert.xml.in b/interface-definitions/service_router-advert.xml.in index 16c29022d..166a4a0cf 100644 --- a/interface-definitions/service_router-advert.xml.in +++ b/interface-definitions/service_router-advert.xml.in @@ -225,6 +225,36 @@ </leafNode> </children> </tagNode> + <tagNode name="nat64prefix"> + <properties> + <help>NAT64 prefix included in the router advertisements</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix to be advertized</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + <leafNode name="valid-lifetime"> + <properties> + <help>Time in seconds that the prefix will remain valid</help> + <completionHelp> + <list>infinity</list> + </completionHelp> + <valueHelp> + <format>u32:4-65528</format> + <description>Time in seconds that the prefix will remain valid</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 4-65528"/> + </constraint> + </properties> + <defaultValue>65528</defaultValue> + </leafNode> + </children> + </tagNode> <tagNode name="prefix"> <properties> <help>IPv6 prefix to be advertised in Router Advertisements (RAs)</help> diff --git a/interface-definitions/system_conntrack.xml.in b/interface-definitions/system_conntrack.xml.in index a348097cc..219c6e28e 100644 --- a/interface-definitions/system_conntrack.xml.in +++ b/interface-definitions/system_conntrack.xml.in @@ -289,6 +289,12 @@ <valueless/> </properties> </leafNode> + <leafNode name="rtsp"> + <properties> + <help>RTSP connection tracking</help> + <valueless/> + </properties> + </leafNode> <leafNode name="sip"> <properties> <help>SIP connection tracking</help> diff --git a/op-mode-definitions/force-commit-archive.xml.in b/op-mode-definitions/force-commit-archive.xml.in new file mode 100644 index 000000000..162323c20 --- /dev/null +++ b/op-mode-definitions/force-commit-archive.xml.in @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="force"> + <children> + <leafNode name="commit-archive"> + <properties> + <help>Manually archive configuration</help> + </properties> + <command>/usr/bin/config-mgmt</command> + </leafNode> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index a81c8d4f7..a5e01bade 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -4,7 +4,7 @@ <children> <node name="pki"> <properties> - <help>Generate PKI certificates and keys</help> + <help>Generate public key infrastructure (PKI) certificates and keys</help> </properties> <children> <node name="ca"> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index a6ce04624..e13270364 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -219,7 +219,7 @@ <path>firewall ipv4 forward filter rule</path> </completionHelp> </properties> - <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-FWD-filter-$8-[ADRJC]\]"</command> + <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-FWD-filter-$8-[ADRJCO]\]"</command> </tagNode> </children> </node> @@ -322,7 +322,7 @@ <path>firewall ipv6 forward filter rule</path> </completionHelp> </properties> - <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-FWD-filter-$8-[ADRJC]\]"</command> + <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-FWD-filter-$8-[ADRJCO]\]"</command> </tagNode> </children> </node> diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py index ff078649d..28ccee769 100644 --- a/python/vyos/config_mgmt.py +++ b/python/vyos/config_mgmt.py @@ -132,6 +132,9 @@ class ConfigMgmt: {}).get('source_address', '') if config.exists(['system', 'host-name']): self.hostname = config.return_value(['system', 'host-name']) + if config.exists(['system', 'domain-name']): + tmp = config.return_value(['system', 'domain-name']) + self.hostname += f'.{tmp}' else: self.hostname = 'vyos' diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index d048901f0..423fe01ed 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -20,10 +20,22 @@ from ctypes import cdll, c_char_p, c_void_p, c_int, c_bool LIBPATH = '/usr/lib/libvyosconfig.so.0' +def replace_backslash(s, search, replace): + """Modify quoted strings containing backslashes not of escape sequences""" + def replace_method(match): + result = match.group().replace(search, replace) + return result + p = re.compile(r'("[^"]*[\\][^"]*"\n|\'[^\']*[\\][^\']*\'\n)') + return p.sub(replace_method, s) + def escape_backslash(string: str) -> str: - """Escape single backslashes in string that are not in escape sequence""" - p = re.compile(r'(?<!\\)[\\](?!b|f|n|r|t|\\[^bfnrt])') - result = p.sub(r'\\\\', string) + """Escape single backslashes in quoted strings""" + result = replace_backslash(string, '\\', '\\\\') + return result + +def unescape_backslash(string: str) -> str: + """Unescape backslashes in quoted strings""" + result = replace_backslash(string, '\\\\', '\\') return result def extract_version(s): @@ -165,11 +177,14 @@ class ConfigTree(object): def to_string(self, ordered_values=False): config_string = self.__to_string(self.__config, ordered_values).decode() + config_string = unescape_backslash(config_string) config_string = "{0}\n{1}".format(config_string, self.__version) return config_string def to_commands(self, op="set"): - return self.__to_commands(self.__config, op.encode()).decode() + commands = self.__to_commands(self.__config, op.encode()).decode() + commands = unescape_backslash(commands) + return commands def to_json(self): return self.__to_json(self.__config).decode() @@ -362,6 +377,7 @@ def show_diff(left, right, path=[], commands=False, libpath=LIBPATH): msg = __get_error().decode() raise ConfigTreeError(msg) + res = unescape_backslash(res) return res def union(left, right, libpath=LIBPATH): diff --git a/smoketest/scripts/cli/test_backslash_escape.py b/smoketest/scripts/cli/test_backslash_escape.py new file mode 100755 index 000000000..e94e9ab0a --- /dev/null +++ b/smoketest/scripts/cli/test_backslash_escape.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configtree import ConfigTree + +base_path = ['interfaces', 'ethernet', 'eth0', 'description'] + +cases_word = [r'fo\o', r'fo\\o', r'foço\o', r'foço\\o'] +# legacy CLI output quotes only if whitespace present; this is a notable +# difference that confounds the translation legacy -> modern, hence +# determines the regex used in function replace_backslash +cases_phrase = [r'some fo\o', r'some fo\\o', r'some foço\o', r'some foço\\o'] + +case_save_config = '/tmp/smoketest-case-save' + +class TestBackslashEscape(VyOSUnitTestSHIM.TestCase): + def test_backslash_escape_word(self): + for case in cases_word: + self.cli_set(base_path + [case]) + self.cli_commit() + # save_config tests translation though subsystems: + # legacy output -> config -> configtree -> file + self._session.save_config(case_save_config) + # reload to configtree and confirm: + with open(case_save_config) as f: + config_string = f.read() + ct = ConfigTree(config_string) + res = ct.return_value(base_path) + self.assertEqual(case, res, msg=res) + print(f'description: {res}') + self.cli_delete(base_path) + self.cli_commit() + + def test_backslash_escape_phrase(self): + for case in cases_phrase: + self.cli_set(base_path + [case]) + self.cli_commit() + # save_config tests translation though subsystems: + # legacy output -> config -> configtree -> file + self._session.save_config(case_save_config) + # reload to configtree and confirm: + with open(case_save_config) as f: + config_string = f.read() + ct = ConfigTree(config_string) + res = ct.return_value(base_path) + self.assertEqual(case, res, msg=res) + print(f'description: {res}') + self.cli_delete(base_path) + self.cli_commit() + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index c21d8af4e..ee4445251 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1065,6 +1065,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase): '20' : { 'action' : 'permit', 'set' : { + 'as-path-exclude' : 'all', 'evpn-gateway-ipv4' : '192.0.2.99', 'evpn-gateway-ipv6' : '2001:db8:f00::1', }, diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py index 5fc2019fd..d1ff25a58 100755 --- a/smoketest/scripts/cli/test_service_router-advert.py +++ b/smoketest/scripts/cli/test_service_router-advert.py @@ -195,6 +195,34 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): for src in ra_src: self.assertIn(f' {src};', config) + def test_nat64prefix(self): + nat64prefix = '64:ff9b::/96' + nat64prefix_invalid = '64:ff9b::/44' + + self.cli_set(base_path + ['nat64prefix', nat64prefix]) + + # and another invalid prefix + # Invalid NAT64 prefix length for "2001:db8::/34", can only be one of: + # /32, /40, /48, /56, /64, /96 + self.cli_set(base_path + ['nat64prefix', nat64prefix_invalid]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['nat64prefix', nat64prefix_invalid]) + + # NAT64 valid-lifetime must not be smaller then "interval max" + self.cli_set(base_path + ['nat64prefix', nat64prefix, 'valid-lifetime', '500']) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['nat64prefix', nat64prefix, 'valid-lifetime']) + + # commit changes + self.cli_commit() + + config = read_file(RADVD_CONF) + + tmp = f'nat64prefix {nat64prefix}' + ' {' + self.assertIn(tmp, config) + self.assertIn('AdvValidLifetime 65528;', config) # default if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py index f00626b3d..2d76da145 100755 --- a/smoketest/scripts/cli/test_system_conntrack.py +++ b/smoketest/scripts/cli/test_system_conntrack.py @@ -174,12 +174,16 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase): 'pptp': { 'driver': ['nf_nat_pptp', 'nf_conntrack_pptp'], 'nftables': ['ct helper set "pptp_tcp"'] - }, + }, + 'rtsp': { + 'driver': ['nf_nat_rtsp', 'nf_conntrack_rtsp'], + 'nftables': ['ct helper set "rtsp_tcp"'] + }, 'sip': { 'driver': ['nf_nat_sip', 'nf_conntrack_sip'], 'nftables': ['ct helper set "sip_tcp"', 'ct helper set "sip_udp"'] - }, + }, 'sqlnet': { 'nftables': ['ct helper set "tns_tcp"'] }, diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 3c27655b0..810437dda 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -268,6 +268,18 @@ def verify_rule(firewall, rule_conf, ipv6): if 'port' in side_conf and dict_search_args(side_conf, 'group', 'port_group'): raise ConfigError(f'{side} port-group and port cannot both be defined') + if 'add_address_to_group' in rule_conf: + for type in ['destination_address', 'source_address']: + if type in rule_conf['add_address_to_group']: + if 'address_group' not in rule_conf['add_address_to_group'][type]: + raise ConfigError(f'Dynamic address group must be defined.') + else: + target = rule_conf['add_address_to_group'][type]['address_group'] + fwall_group = 'ipv6_address_group' if ipv6 else 'address_group' + group_obj = dict_search_args(firewall, 'group', 'dynamic_group', fwall_group, target) + if group_obj is None: + raise ConfigError(f'Invalid dynamic address group on firewall rule') + if 'log_options' in rule_conf: if 'log' not in rule_conf: raise ConfigError('log-options defined, but log is not enable') diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py index dbb47de4e..88d767bb8 100755 --- a/src/conf_mode/service_router-advert.py +++ b/src/conf_mode/service_router-advert.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2022 VyOS maintainers and contributors +# Copyright (C) 2018-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,6 +17,8 @@ import os from sys import exit +from ipaddress import IPv6Network + from vyos.base import Warning from vyos.config import Config from vyos.template import render @@ -47,7 +49,9 @@ def verify(rtradv): return None for interface, interface_config in rtradv['interface'].items(): - if 'prefix' in interface: + interval_max = int(interface_config['interval']['max']) + + if 'prefix' in interface_config: for prefix, prefix_config in interface_config['prefix'].items(): valid_lifetime = prefix_config['valid_lifetime'] if valid_lifetime == 'infinity': @@ -60,6 +64,15 @@ def verify(rtradv): if not (int(valid_lifetime) >= int(preferred_lifetime)): raise ConfigError('Prefix valid-lifetime must be greater then or equal to preferred-lifetime') + if 'nat64prefix' in interface_config: + nat64_supported_lengths = [32, 40, 48, 56, 64, 96] + for prefix, prefix_config in interface_config['nat64prefix'].items(): + if IPv6Network(prefix).prefixlen not in nat64_supported_lengths: + raise ConfigError(f'Invalid NAT64 prefix length for "{prefix}", can only be one of: /' + ', /'.join(nat64_supported_lengths)) + + if int(prefix_config['valid_lifetime']) < interval_max: + raise ConfigError(f'NAT64 valid-lifetime must not be smaller then "interval max" which is "{interval_max}"!') + if 'name_server' in interface_config: if len(interface_config['name_server']) > 3: raise ConfigError('No more then 3 IPv6 name-servers supported!') @@ -72,7 +85,6 @@ def verify(rtradv): # ensure stale RDNSS info gets removed in a timely fashion, this # should not be greater than 2*MaxRtrAdvInterval. lifetime = int(interface_config['name_server_lifetime']) - interval_max = int(interface_config['interval']['max']) if lifetime > 0: if lifetime < int(interval_max): raise ConfigError(f'RDNSS lifetime must be at least "{interval_max}" seconds!') diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py index 2a55daed4..a1472aaaa 100755 --- a/src/conf_mode/system_conntrack.py +++ b/src/conf_mode/system_conntrack.py @@ -58,6 +58,11 @@ module_map = { 'nftables': ['tcp dport {1723} ct helper set "pptp_tcp" return'], 'ipv4': True }, + 'rtsp': { + 'ko': ['nf_nat_rtsp', 'nf_conntrack_rtsp'], + 'nftables': ['tcp dport {554} ct helper set "rtsp_tcp" return'], + 'ipv4': True + }, 'sip': { 'ko': ['nf_nat_sip', 'nf_conntrack_sip'], 'nftables': ['tcp dport {5060,5061} ct helper set "sip_tcp" return', @@ -195,7 +200,7 @@ def generate(conntrack): def apply(conntrack): # Depending on the enable/disable state of the ALG (Application Layer Gateway) # modules we need to either insmod or rmmod the helpers. - + add_modules = [] rm_modules = [] diff --git a/src/helpers/vyos_config_sync.py b/src/helpers/vyos_config_sync.py index 7cfa8fe88..572fea61f 100755 --- a/src/helpers/vyos_config_sync.py +++ b/src/helpers/vyos_config_sync.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023 VyOS maintainers and contributors +# Copyright (C) 2023-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -60,6 +60,7 @@ def post_request(url: str, return response + def retrieve_config(section: str = None) -> Optional[Dict[str, Any]]: """Retrieves the configuration from the local server. @@ -71,8 +72,6 @@ def retrieve_config(section: str = None) -> Optional[Dict[str, Any]]: """ if section is None: section = [] - else: - section = section.split() conf = Config() config = conf.get_config_dict(section, get_first_key=True) @@ -101,8 +100,6 @@ def set_remote_config( if path is None: path = [] - else: - path = path.split() headers = {'Content-Type': 'application/json'} # Disable the InsecureRequestWarning @@ -127,17 +124,16 @@ def set_remote_config( def is_section_revised(section: str) -> bool: from vyos.config_mgmt import is_node_revised - return is_node_revised([section]) + return is_node_revised(section) def config_sync(secondary_address: str, secondary_key: str, - sections: List[str], + sections: List[list], mode: str): """Retrieve a config section from primary router in JSON format and send it to secondary router """ - # Config sync only if sections changed if not any(map(is_section_revised, sections)): return @@ -188,5 +184,17 @@ if __name__ == '__main__': "Missing required configuration data for config synchronization.") exit(0) + # Generate list_sections of sections/subsections + # [ + # ['interfaces', 'pseudo-ethernet'], ['interfaces', 'virtual-ethernet'], ['nat'], ['nat66'] + # ] + list_sections = [] + for section, subsections in sections.items(): + if subsections: + for subsection in subsections: + list_sections.append([section, subsection]) + else: + list_sections.append([section]) + config_sync(secondary_address, secondary_key, - sections, mode) + list_sections, mode) diff --git a/src/migration-scripts/policy/1-to-2 b/src/migration-scripts/policy/1-to-2 index c70490ce9..c7a983bba 100755 --- a/src/migration-scripts/policy/1-to-2 +++ b/src/migration-scripts/policy/1-to-2 @@ -32,23 +32,23 @@ file_name = argv[1] with open(file_name, 'r') as f: config_file = f.read() -base = ['policy', 'ipv6-route'] +base = ['policy'] config = ConfigTree(config_file) if not config.exists(base): # Nothing to do exit(0) -config.rename(base, 'route6') -config.set_tag(['policy', 'route6']) +if config.exists(base + ['ipv6-route']): + config.rename(base + ['ipv6-route'],'route6') + config.set_tag(['policy', 'route6']) for route in ['route', 'route6']: - route_path = ['policy', route] - if config.exists(route_path): - for name in config.list_nodes(route_path): - if config.exists(route_path + [name, 'rule']): - for rule in config.list_nodes(route_path + [name, 'rule']): - rule_tcp_flags = route_path + [name, 'rule', rule, 'tcp', 'flags'] + if config.exists(base + [route]): + for name in config.list_nodes(base + [route]): + if config.exists(base + [route, name, 'rule']): + for rule in config.list_nodes(base + [route, name, 'rule']): + rule_tcp_flags = base + [route, name, 'rule', rule, 'tcp', 'flags'] if config.exists(rule_tcp_flags): tmp = config.return_value(rule_tcp_flags) diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py index cf8adf795..6ea213bec 100755 --- a/src/op_mode/conntrack.py +++ b/src/op_mode/conntrack.py @@ -112,7 +112,8 @@ def get_formatted_output(dict_data): proto = meta['layer4']['protoname'] if direction == 'independent': conn_id = meta['id'] - timeout = meta['timeout'] + # T6138 flowtable offload conntrack entries without 'timeout' + timeout = meta.get('timeout', 'n/a') orig_src = f'{orig_src}:{orig_sport}' if orig_sport else orig_src orig_dst = f'{orig_dst}:{orig_dport}' if orig_dport else orig_dst reply_src = f'{reply_src}:{reply_sport}' if reply_sport else reply_src |