diff options
25 files changed, 478 insertions, 55 deletions
diff --git a/.github/workflows/mergifyio_backport.yml b/.github/workflows/mergifyio_backport.yml new file mode 100644 index 000000000..f1f4312c4 --- /dev/null +++ b/.github/workflows/mergifyio_backport.yml @@ -0,0 +1,22 @@ +name: Mergifyio backport + +on: [issue_comment] + +jobs: + mergifyio_backport: + if: github.repository == 'vyos/vyos-1x' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: actions-ecosystem/action-regex-match@v2 + id: regex-match + with: + text: ${{ github.event.comment.body }} + regex: '[Mm]ergifyio backport ' + + - uses: actions-ecosystem/action-add-labels@v1 + if: ${{ steps.regex-match.outputs.match != '' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + labels: backport @@ -62,7 +62,6 @@ op_mode_definitions: $(op_xml_obj) rm -f $(OP_TMPL_DIR)/delete/node.def rm -f $(OP_TMPL_DIR)/generate/node.def rm -f $(OP_TMPL_DIR)/set/node.def - rm -f $(OP_TMPL_DIR)/show/tech-support/node.def # XXX: ping and traceroute must be able to recursivly call itself as the # options are provided from the script itself diff --git a/data/templates/conntrack/nftables-ct.j2 b/data/templates/conntrack/nftables-ct.j2 index 970869043..3a5b5a87c 100644 --- a/data/templates/conntrack/nftables-ct.j2 +++ b/data/templates/conntrack/nftables-ct.j2 @@ -29,7 +29,7 @@ table raw { return } -{{ group_tmpl.groups(firewall_group, False) }} +{{ group_tmpl.groups(firewall_group, False, True) }} } flush chain ip6 raw {{ nft_ct_ignore_name }} @@ -54,5 +54,5 @@ table ip6 raw { return } -{{ group_tmpl.groups(firewall_group, True) }} +{{ group_tmpl.groups(firewall_group, True, True) }} } diff --git a/data/templates/firewall/nftables-vrf-zones.j2 b/data/templates/firewall/nftables-vrf-zones.j2 index eecf47b78..3bce7312d 100644 --- a/data/templates/firewall/nftables-vrf-zones.j2 +++ b/data/templates/firewall/nftables-vrf-zones.j2 @@ -7,11 +7,11 @@ table inet vrf_zones { # Chain for inbound traffic chain vrf_zones_ct_in { type filter hook prerouting priority raw; policy accept; - counter ct zone set iifname map @ct_iface_map + counter ct original zone set iifname map @ct_iface_map } # Chain for locally-generated traffic chain vrf_zones_ct_out { type filter hook output priority raw; policy accept; - counter ct zone set oifname map @ct_iface_map + counter ct original zone set oifname map @ct_iface_map } } diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2 index 7fa974254..e1c102e16 100644 --- a/data/templates/frr/bgpd.frr.j2 +++ b/data/templates/frr/bgpd.frr.j2 @@ -170,7 +170,7 @@ {% endif %} {% endif %} {% if afi_config.remove_private_as is vyos_defined %} - neighbor {{ neighbor }} remove-private-AS + neighbor {{ neighbor }} remove-private-AS {{ 'all' if afi_config.remove_private_as.all is vyos_defined }} {% endif %} {% if afi_config.route_reflector_client is vyos_defined %} neighbor {{ neighbor }} route-reflector-client diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl index 3aad8e8dd..fe2610724 100644 --- a/data/templates/frr/daemons.frr.tmpl +++ b/data/templates/frr/daemons.frr.tmpl @@ -6,6 +6,7 @@ ripd=yes ripngd=yes isisd=yes pimd=no +pim6d=yes ldpd=yes nhrpd=no eigrpd=yes @@ -16,39 +17,41 @@ bfdd=yes staticd=yes vtysh_enable=yes -zebra_options=" -s 90000000 --daemon -A 127.0.0.1 +zebra_options=" --daemon -A 127.0.0.1 -s 90000000 {%- if irdp is defined %} -M irdp{% endif -%} {%- if snmp is defined and snmp.zebra is defined %} -M snmp{% endif -%} " -bgpd_options=" --daemon -A 127.0.0.1 +bgpd_options=" --daemon -A 127.0.0.1 -M rpki {%- if bmp is defined %} -M bmp{% endif -%} {%- if snmp is defined and snmp.bgpd is defined %} -M snmp{% endif -%} " -ospfd_options=" --daemon -A 127.0.0.1 +ospfd_options=" --daemon -A 127.0.0.1 {%- if snmp is defined and snmp.ospfd is defined %} -M snmp{% endif -%} " -ospf6d_options=" --daemon -A ::1 +ospf6d_options=" --daemon -A ::1 {%- if snmp is defined and snmp.ospf6d is defined %} -M snmp{% endif -%} " -ripd_options=" --daemon -A 127.0.0.1 +ripd_options=" --daemon -A 127.0.0.1 {%- if snmp is defined and snmp.ripd is defined %} -M snmp{% endif -%} " -ripngd_options=" --daemon -A ::1" -isisd_options=" --daemon -A 127.0.0.1 +ripngd_options=" --daemon -A ::1" +isisd_options=" --daemon -A 127.0.0.1 {%- if snmp is defined and snmp.isisd is defined %} -M snmp{% endif -%} " -pimd_options=" --daemon -A 127.0.0.1" -ldpd_options=" --daemon -A 127.0.0.1 +pimd_options=" --daemon -A 127.0.0.1" +pim6d_options=" --daemon -A ::1" +ldpd_options=" --daemon -A 127.0.0.1 {%- if snmp is defined and snmp.ldpd is defined %} -M snmp{% endif -%} " -mgmtd_options=" --daemon -A 127.0.0.1" -nhrpd_options=" --daemon -A 127.0.0.1" +mgmtd_options=" --daemon -A 127.0.0.1" +nhrpd_options=" --daemon -A 127.0.0.1" eigrpd_options=" --daemon -A 127.0.0.1" babeld_options=" --daemon -A 127.0.0.1" sharpd_options=" --daemon -A 127.0.0.1" -pbrd_options=" --daemon -A 127.0.0.1" -staticd_options=" --daemon -A 127.0.0.1" -bfdd_options=" --daemon -A 127.0.0.1" +pbrd_options=" --daemon -A 127.0.0.1" +staticd_options=" --daemon -A 127.0.0.1" +bfdd_options=" --daemon -A 127.0.0.1" watchfrr_enable=no valgrind_enable=no + diff --git a/data/templates/frr/pim6d.frr.j2 b/data/templates/frr/pim6d.frr.j2 new file mode 100644 index 000000000..8e430541d --- /dev/null +++ b/data/templates/frr/pim6d.frr.j2 @@ -0,0 +1,38 @@ +! +{% if interface is vyos_defined %} +{% for iface, iface_config in interface.items() %} +interface {{ iface }} +{% if iface_config.mld is vyos_defined and iface_config.mld.disable is not vyos_defined %} + ipv6 mld +{% if iface_config.mld.version is vyos_defined %} + ipv6 mld version {{ iface_config.mld.version }} +{% endif %} +{% if iface_config.mld.interval is vyos_defined %} + ipv6 mld query-interval {{ iface_config.mld.interval }} +{% endif %} +{% if iface_config.mld.max_response_time is vyos_defined %} + ipv6 mld query-max-response-time {{ iface_config.mld.max_response_time // 100 }} +{% endif %} +{% if iface_config.mld.last_member_query_count is vyos_defined %} + ipv6 mld last-member-query-count {{ iface_config.mld.last_member_query_count }} +{% endif %} +{% if iface_config.mld.last_member_query_interval is vyos_defined %} + ipv6 mld last-member-query-interval {{ iface_config.mld.last_member_query_interval // 100 }} +{% endif %} +{% if iface_config.mld.join is vyos_defined %} +{% for group, group_config in iface_config.mld.join.items() %} +{% if group_config.source is vyos_defined %} +{% for source in group_config.source %} + ipv6 mld join {{ group }} {{ source }} +{% endfor %} +{% else %} + ipv6 mld join {{ group }} +{% endif %} +{% endfor %} +{% endif %} +{% endif %} +exit +! +{% endfor %} +! +{% endif %} diff --git a/debian/changelog b/debian/changelog index c9d925253..d64c66818 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -vyos-1x (1.4dev0) unstable; urgency=medium +vyos-1x (1.5dev0) unstable; urgency=medium * Dummy changelog entry for vyos-1x repository This is a internal VyOS package and the VyOS package process does not use @@ -7,4 +7,4 @@ vyos-1x (1.4dev0) unstable; urgency=medium The correct verion number of this package is auto-generated by GIT on build-time - -- VyOS maintainers and contributors <maintainers@vyos.io> Mon, 11 Jan 2021 19:02:53 +0100 + -- VyOS maintainers and contributors <maintainers@vyos.io> Sun, 10 Sep 2023 15:42:53 +0200 diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst index e355ffa84..16c118cb7 100644 --- a/debian/vyos-1x.preinst +++ b/debian/vyos-1x.preinst @@ -10,3 +10,4 @@ dpkg-divert --package vyos-1x --add --no-rename /etc/skel/.profile dpkg-divert --package vyos-1x --add --no-rename /etc/sysctl.d/80-vpp.conf dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplugd.conf dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplug +dpkg-divert --package vyos-1x --add --no-rename /etc/rsyslog.d/45-frr.conf diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i index 75221a348..9ec513da9 100644 --- a/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i +++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i @@ -1,4 +1,5 @@ <!-- include start from bgp/neighbor-afi-ipv4-ipv6-common.xml.i --> + <leafNode name="addpath-tx-all"> <properties> <help>Use addpath to advertise all paths to a neighbor</help> @@ -156,12 +157,19 @@ </properties> </leafNode> #include <include/bgp/afi-nexthop-self.xml.i> -<leafNode name="remove-private-as"> +<node name="remove-private-as"> <properties> <help>Remove private AS numbers from AS path in outbound route updates</help> - <valueless/> </properties> -</leafNode> + <children> + <leafNode name="all"> + <properties> + <help>Remove private AS numbers to all AS numbers in outbound route updates</help> + <valueless/> + </properties> + </leafNode> + </children> +</node> #include <include/bgp/afi-route-map.xml.i> #include <include/bgp/afi-route-reflector-client.xml.i> #include <include/bgp/afi-route-server-client.xml.i> diff --git a/interface-definitions/protocols-pim6.xml.in b/interface-definitions/protocols-pim6.xml.in new file mode 100644 index 000000000..58ef5a1e3 --- /dev/null +++ b/interface-definitions/protocols-pim6.xml.in @@ -0,0 +1,132 @@ +<?xml version="1.0"?> +<!-- Protocol Independent Multicast for IPv6 (PIMv6) configuration --> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="pim6" owner="${vyos_conf_scripts_dir}/protocols_pim6.py"> + <properties> + <help>Protocol Independent Multicast for IPv6 (PIMv6)</help> + <priority>400</priority> + </properties> + <children> + <tagNode name="interface"> + <properties> + <help>PIMv6 interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + </properties> + <children> + <node name="mld"> + <properties> + <help>Multicast Listener Discovery (MLD)</help> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + <tagNode name="join"> + <properties> + <help>MLD join multicast group</help> + <valueHelp> + <format>ipv6</format> + <description>Multicast group address</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + </properties> + <children> + <leafNode name="source"> + <properties> + <help>Source address</help> + <valueHelp> + <format>ipv6</format> + <description>Source address</description> + </valueHelp> + <completionHelp> + <script>${vyos_completion_dir}/list_local_ips.sh --ipv6</script> + </completionHelp> + <constraint> + <validator name="ipv6-address"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> + </tagNode> + <leafNode name="version"> + <properties> + <help>MLD version</help> + <completionHelp> + <list>1 2</list> + </completionHelp> + <valueHelp> + <format>1</format> + <description>MLD version 1</description> + </valueHelp> + <valueHelp> + <format>2</format> + <description>MLD version 2</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-2"/> + </constraint> + </properties> + <defaultValue>2</defaultValue> + </leafNode> + <leafNode name="interval"> + <properties> + <help>Query interval</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Query interval in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="max-response-time"> + <properties> + <help>Max query response time</help> + <valueHelp> + <format>u32:100-6553500</format> + <description>Query response value in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 100-6553500"/> + </constraint> + </properties> + </leafNode> + <leafNode name="last-member-query-count"> + <properties> + <help>Last member query count</help> + <valueHelp> + <format>u32:1-255</format> + <description>Count</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <leafNode name="last-member-query-interval"> + <properties> + <help>Last member query interval</help> + <valueHelp> + <format>u32:100-6553500</format> + <description>Last member query interval in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 100-6553500"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/service-webproxy.xml.in b/interface-definitions/service-webproxy.xml.in index b24997816..637d57891 100644 --- a/interface-definitions/service-webproxy.xml.in +++ b/interface-definitions/service-webproxy.xml.in @@ -353,7 +353,7 @@ <description>Object size in KB</description> </valueHelp> <constraint> - <validator name="numeric" argument="--range 1-100000"/> + <validator name="numeric" argument="--range 1-1000000"/> </constraint> </properties> </leafNode> diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in index d5dbb7850..3caf1f1ea 100644 --- a/op-mode-definitions/show-ip.xml.in +++ b/op-mode-definitions/show-ip.xml.in @@ -33,6 +33,12 @@ </tagNode> </children> </node> + <leafNode name="nht"> + <properties> + <help>Show IPv4 nexthop tracking table</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> </children> </node> </children> diff --git a/op-mode-definitions/show-techsupport_report.xml.in b/op-mode-definitions/show-techsupport_report.xml.in index aa51eacd9..ef051e940 100644 --- a/op-mode-definitions/show-techsupport_report.xml.in +++ b/op-mode-definitions/show-techsupport_report.xml.in @@ -3,6 +3,9 @@ <node name="show"> <children> <node name="tech-support"> + <properties> + <help>Show tech-support report</help> + </properties> <children> <node name="report"> <properties> diff --git a/python/vyos/frr.py b/python/vyos/frr.py index 2e3c8a271..9c9e50ff7 100644 --- a/python/vyos/frr.py +++ b/python/vyos/frr.py @@ -88,7 +88,7 @@ LOG.addHandler(ch2) _frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd', 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd', - 'bfdd', 'eigrpd', 'babeld'] + 'bfdd', 'eigrpd', 'babeld' ,'pim6d'] path_vtysh = '/usr/bin/vtysh' path_frr_reload = '/usr/lib/frr/frr-reload.py' diff --git a/python/vyos/template.py b/python/vyos/template.py index c1b57b883..add4d3ce5 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -671,7 +671,8 @@ def conntrack_ignore_rule(rule_conf, rule_id, ipv6=False): if 'inbound_interface' in rule_conf: ifname = rule_conf['inbound_interface'] - output.append(f'iifname {ifname}') + if ifname != 'any': + output.append(f'iifname {ifname}') if 'protocol' in rule_conf: proto = rule_conf['protocol'] diff --git a/python/vyos/utils/process.py b/python/vyos/utils/process.py index 9ecdddf09..e09c7d86d 100644 --- a/python/vyos/utils/process.py +++ b/python/vyos/utils/process.py @@ -139,7 +139,7 @@ def cmd(command, flag='', shell=None, input=None, timeout=None, env=None, expect: a list of error codes to consider as normal """ decoded, code = popen( - command.lstrip(), flag, + command, flag, stdout=stdout, stderr=stderr, input=input, timeout=timeout, env=env, shell=shell, @@ -170,7 +170,7 @@ def rc_cmd(command, flag='', shell=None, input=None, timeout=None, env=None, (1, 'Device "eth99" does not exist.') """ out, code = popen( - command.lstrip(), flag, + command, flag, stdout=stdout, stderr=stderr, input=input, timeout=timeout, env=env, shell=shell, diff --git a/smoketest/scripts/cli/test_load_balancing_wan.py b/smoketest/scripts/cli/test_load_balancing_wan.py index 9b2cb0fac..47ca19b27 100755 --- a/smoketest/scripts/cli/test_load_balancing_wan.py +++ b/smoketest/scripts/cli/test_load_balancing_wan.py @@ -124,11 +124,12 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): self.assertEqual(tmp, original) # Delete veth interfaces and netns - for iface in [iface1, iface2, iface3, container_iface1, container_iface2, container_iface3]: + for iface in [iface1, iface2, iface3]: call(f'sudo ip link del dev {iface}') delete_netns(ns1) delete_netns(ns2) + delete_netns(ns3) def test_check_chains(self): @@ -246,11 +247,13 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): self.assertEqual(tmp, nat_vyos_pre_snat_hook) # Delete veth interfaces and netns - for iface in [iface1, iface2, iface3, container_iface1, container_iface2, container_iface3]: + for iface in [iface1, iface2, iface3]: call(f'sudo ip link del dev {iface}') delete_netns(ns1) delete_netns(ns2) + delete_netns(ns3) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_pim6.py b/smoketest/scripts/cli/test_protocols_pim6.py new file mode 100755 index 000000000..1be12836d --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_pim6.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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.configsession import ConfigSessionError +from vyos.ifconfig import Section +from vyos.utils.process import process_named_running + +PROCESS_NAME = 'pim6d' +base_path = ['protocols', 'pim6'] + + +class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase): + def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + self.cli_delete(base_path) + self.cli_commit() + + def test_pim6_01_mld_simple(self): + # commit changes + interfaces = Section.interfaces('ethernet') + + for interface in interfaces: + self.cli_set(base_path + ['interface', interface]) + + self.cli_commit() + + # Verify FRR pim6d configuration + for interface in interfaces: + config = self.getFRRconfig( + f'interface {interface}', daemon=PROCESS_NAME) + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ipv6 mld', config) + self.assertNotIn(f' ipv6 mld version 1', config) + + # Change to MLD version 1 + for interface in interfaces: + self.cli_set(base_path + ['interface', + interface, 'mld', 'version', '1']) + + self.cli_commit() + + # Verify FRR pim6d configuration + for interface in interfaces: + config = self.getFRRconfig( + f'interface {interface}', daemon=PROCESS_NAME) + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ipv6 mld', config) + self.assertIn(f' ipv6 mld version 1', config) + + def test_pim6_02_mld_join(self): + # commit changes + interfaces = Section.interfaces('ethernet') + + # Use an invalid multiple group address + for interface in interfaces: + self.cli_set(base_path + ['interface', + interface, 'mld', 'join', 'fd00::1234']) + + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface']) + + # Use a valid multiple group address + for interface in interfaces: + self.cli_set(base_path + ['interface', + interface, 'mld', 'join', 'ff18::1234']) + + self.cli_commit() + + # Verify FRR pim6d configuration + for interface in interfaces: + config = self.getFRRconfig( + f'interface {interface}', daemon=PROCESS_NAME) + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ipv6 mld join ff18::1234', config) + + # Join a source-specific multicast group + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, + 'mld', 'join', 'ff38::5678', 'source', '2001:db8::5678']) + + self.cli_commit() + + # Verify FRR pim6d configuration + for interface in interfaces: + config = self.getFRRconfig( + f'interface {interface}', daemon=PROCESS_NAME) + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ipv6 mld join ff38::5678 2001:db8::5678', config) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/conf_mode/protocols_pim6.py b/src/conf_mode/protocols_pim6.py new file mode 100755 index 000000000..6a1235ba5 --- /dev/null +++ b/src/conf_mode/protocols_pim6.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +from ipaddress import IPv6Address +from sys import exit +from typing import Optional + +from vyos import ConfigError, airbag, frr +from vyos.config import Config, ConfigDict +from vyos.configdict import node_changed +from vyos.configverify import verify_interface_exists +from vyos.template import render_to_string + +airbag.enable() + + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'pim6'] + pim6 = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True, with_recursive_defaults=True) + + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + pim6['interface_removed'] = list(interfaces_removed) + + return pim6 + + +def verify(pim6): + if pim6 is None: + return + + for interface, interface_config in pim6.get('interface', {}).items(): + verify_interface_exists(interface) + if 'mld' in interface_config: + mld = interface_config['mld'] + for group in mld.get('join', {}).keys(): + # Validate multicast group address + if not IPv6Address(group).is_multicast: + raise ConfigError(f"{group} is not a multicast group") + + +def generate(pim6): + if pim6 is None: + return + + pim6['new_frr_config'] = render_to_string('frr/pim6d.frr.j2', pim6) + + +def apply(pim6): + if pim6 is None: + return + + pim6_daemon = 'pim6d' + + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + + frr_cfg.load_configuration(pim6_daemon) + + for key in ['interface', 'interface_removed']: + if key not in pim6: + continue + for interface in pim6[key]: + frr_cfg.modify_section( + f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True) + + if 'new_frr_config' in pim6: + frr_cfg.add_before(frr.default_add_before, pim6['new_frr_config']) + frr_cfg.commit_configuration(pim6_daemon) + + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/system_frr.py b/src/conf_mode/system_frr.py index fb252238a..d8224b3c3 100755 --- a/src/conf_mode/system_frr.py +++ b/src/conf_mode/system_frr.py @@ -22,17 +22,14 @@ from vyos import airbag from vyos.config import Config from vyos.logger import syslog from vyos.template import render_to_string +from vyos.utils.boot import boot_configuration_complete from vyos.utils.file import read_file from vyos.utils.file import write_file -from vyos.utils.process import run +from vyos.utils.process import call airbag.enable() # path to daemons config and config status files config_file = '/etc/frr/daemons' -vyos_status_file = '/tmp/vyos-config-status' -# path to watchfrr for FRR control -watchfrr = '/usr/lib/frr/watchfrr.sh' - def get_config(config=None): if config: @@ -45,12 +42,10 @@ def get_config(config=None): return frr_config - def verify(frr_config): # Nothing to verify here pass - def generate(frr_config): # read daemons config file daemons_config_current = read_file(config_file) @@ -62,25 +57,21 @@ def generate(frr_config): write_file(config_file, daemons_config_new) frr_config['config_file_changed'] = True - def apply(frr_config): - # check if this is initial commit during boot or intiated by CLI - # if the file exists, this must be CLI commit - commit_type_cli = Path(vyos_status_file).exists() # display warning to user - if commit_type_cli and frr_config.get('config_file_changed'): + if boot_configuration_complete() and frr_config.get('config_file_changed'): # Since FRR restart is not safe thing, better to give # control over this to users print(''' You need to reboot a router (preferred) or restart FRR to apply changes in modules settings ''') - # restart FRR automatically. DUring the initial boot this should be - # safe in most cases - if not commit_type_cli and frr_config.get('config_file_changed'): - syslog.warning('Restarting FRR to apply changes in modules') - run(f'{watchfrr} restart') + # restart FRR automatically + # During initial boot this should be safe in most cases + if not boot_configuration_complete() and frr_config.get('config_file_changed'): + syslog.warning('Restarting FRR to apply changes in modules') + call(f'systemctl restart frr.service') if __name__ == '__main__': try: diff --git a/src/init/vyos-router b/src/init/vyos-router index a5d1a31fa..1bbb9c869 100755 --- a/src/init/vyos-router +++ b/src/init/vyos-router @@ -340,10 +340,6 @@ start () nfct helper add tns inet6 tcp nft -f /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules" - rm -f /etc/hostname - ${vyos_conf_scripts_dir}/host_name.py || log_failure_msg "could not reset host-name" - systemctl start frr.service - # As VyOS does not execute commands that are not present in the CLI we call # the script by hand to have a single source for the login banner and MOTD ${vyos_conf_scripts_dir}/system_console.py || log_failure_msg "could not reset serial console" @@ -376,6 +372,13 @@ start () && chgrp ${GROUP} ${vyatta_configdir} log_action_end_msg $? + rm -f /etc/hostname + ${vyos_conf_scripts_dir}/host_name.py || log_failure_msg "could not reset host-name" + ${vyos_conf_scripts_dir}/system_frr.py || log_failure_msg "could not reset FRR config" + # If for any reason FRR was not started by system_frr.py - start it anyways. + # This is a safety net! + systemctl start frr.service + disabled bootfile || init_bootfile cleanup_post_commit_hooks diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py index 23b4b8459..11cbd977d 100755 --- a/src/op_mode/firewall.py +++ b/src/op_mode/firewall.py @@ -300,6 +300,8 @@ def show_firewall_group(name=None): for priority, priority_conf in firewall[item][name_type].items(): if priority not in firewall[item][name_type]: continue + if 'rule' not in priority_conf: + continue for rule_id, rule_conf in priority_conf['rule'].items(): source_group = dict_search_args(rule_conf, 'source', 'group', group_type) dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type) diff --git a/src/op_mode/otp.py b/src/op_mode/otp.py index c472a1ed3..6d4298894 100755 --- a/src/op_mode/otp.py +++ b/src/op_mode/otp.py @@ -23,7 +23,7 @@ from jinja2 import Template from vyos.configquery import ConfigTreeQuery from vyos.xml import defaults from vyos.configdict import dict_merge -from vyos.util import popen +from vyos.utils.process import popen users_otp_template = Template(""" diff --git a/src/systemd/vyos-router.service b/src/systemd/vyos-router.service index 6f683cebb..7a1638f11 100644 --- a/src/systemd/vyos-router.service +++ b/src/systemd/vyos-router.service @@ -1,7 +1,6 @@ [Unit] Description=VyOS Router After=systemd-journald-dev-log.socket time-sync.target local-fs.target cloud-config.service -Requires=frr.service Conflicts=shutdown.target Before=systemd-user-sessions.service |