diff options
67 files changed, 981 insertions, 343 deletions
| diff --git a/.gitignore b/.gitignore index e766a2c27..d781beead 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,7 @@ ENV/  templates-cfg/*  templates-op/*  tests/templates/* +xml_cache/*  # Debian packaging  debian/files @@ -144,3 +145,4 @@ debian/*.substvars  data/component-versions.json  # vyos-1x XML cache  python/vyos/xml_ref/cache.py +python/vyos/xml_ref/pkg_cache/*_cache.py @@ -3,6 +3,7 @@ OP_TMPL_DIR := templates-op  BUILD_DIR := build  DATA_DIR := data  SHIM_DIR := src/shim +CACHE_DIR := xml_cache  LIBS := -lzmq  CFLAGS :=  BUILD_ARCH := $(shell dpkg-architecture -q DEB_BUILD_ARCH) @@ -23,10 +24,11 @@ op_xml_obj = $(op_xml_src:.xml.in=.xml)  .ONESHELL:  interface_definitions: $(config_xml_obj)  	mkdir -p $(TMPL_DIR) +	mkdir -p $(CACHE_DIR)  	$(CURDIR)/scripts/override-default $(BUILD_DIR)/interface-definitions -	$(CURDIR)/python/vyos/xml_ref/generate_cache.py --xml-dir $(BUILD_DIR)/interface-definitions +	$(CURDIR)/python/vyos/xml_ref/generate_cache.py --xml-dir $(BUILD_DIR)/interface-definitions --package-name vyos-1x --output-path $(CACHE_DIR)  	find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1 @@ -61,10 +63,7 @@ op_mode_definitions: $(op_xml_obj)  	rm -f $(OP_TMPL_DIR)/clear/node.def  	rm -f $(OP_TMPL_DIR)/delete/node.def  	rm -f $(OP_TMPL_DIR)/generate/node.def -	rm -f $(OP_TMPL_DIR)/monitor/node.def  	rm -f $(OP_TMPL_DIR)/set/node.def -	rm -f $(OP_TMPL_DIR)/show/node.def -	rm -f $(OP_TMPL_DIR)/show/system/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 @@ -99,6 +98,7 @@ clean:  	rm -rf $(BUILD_DIR)  	rm -rf $(TMPL_DIR)  	rm -rf $(OP_TMPL_DIR) +	rm -rf $(CACHE_DIR)  	$(MAKE) -C $(SHIM_DIR) clean  .PHONY: test diff --git a/data/templates/chrony/chrony.conf.j2 b/data/templates/chrony/chrony.conf.j2 index 7a36fe69d..0daec8fb8 100644 --- a/data/templates/chrony/chrony.conf.j2 +++ b/data/templates/chrony/chrony.conf.j2 @@ -53,8 +53,6 @@ bindaddress {{ address }}  {%         endfor %}  {%     endif %}  {%     if interface is vyos_defined %} -{%         for ifname in interface %} -binddevice {{ ifname }} -{%         endfor %} +binddevice {{ interface }}  {%     endif %}  {% endif %} diff --git a/data/templates/dhcp-client/dhcp6c_daemon-options.j2 b/data/templates/dhcp-client/dhcp6c_daemon-options.j2 new file mode 100644 index 000000000..d33d418fc --- /dev/null +++ b/data/templates/dhcp-client/dhcp6c_daemon-options.j2 @@ -0,0 +1,2 @@ +{% set no_release = '-n' if dhcpv6_options.no_release is vyos_defined else '' %} +DHCP6C_OPTS="-D -k /run/dhcp6c/dhcp6c.{{ ifname }}.sock -c /run/dhcp6c/dhcp6c.{{ ifname }}.conf -p /run/dhcp6c/dhcp6c.{{ ifname }}.pid {{ no_release }} {{ ifname }}" diff --git a/data/templates/frr/ospf6d.frr.j2 b/data/templates/frr/ospf6d.frr.j2 index 84394ed1a..b0b5663dd 100644 --- a/data/templates/frr/ospf6d.frr.j2 +++ b/data/templates/frr/ospf6d.frr.j2 @@ -80,6 +80,27 @@ router ospf6 {{ 'vrf ' ~ vrf if vrf is vyos_defined }}  {% if distance.ospfv3 is vyos_defined %}   distance ospf6 {{ 'intra-area ' ~ distance.ospfv3.intra_area if distance.ospfv3.intra_area is vyos_defined }} {{ 'inter-area ' ~ distance.ospfv3.inter_area if distance.ospfv3.inter_area is vyos_defined }} {{ 'external ' ~ distance.ospfv3.external if distance.ospfv3.external is vyos_defined }}  {% endif %} +{% if graceful_restart is vyos_defined %} +{%     if graceful_restart.grace_period is vyos_defined %} + graceful-restart grace-period {{ graceful_restart.grace_period }} +{%     endif %} +{%     if graceful_restart.helper.enable.router_id is vyos_defined %} +{%         for router_id in graceful_restart.helper.enable.router_id %} + graceful-restart helper enable {{ router_id }} +{%         endfor %} +{%     elif graceful_restart.helper.enable is vyos_defined %} + graceful-restart helper enable +{%     endif %} +{%     if graceful_restart.helper.planned_only is vyos_defined %} + graceful-restart helper planned-only +{%     endif %} +{%     if graceful_restart.helper.lsa_check_disable is vyos_defined %} + graceful-restart helper lsa-check-disable +{%     endif %} +{%     if graceful_restart.helper.supported_grace_time is vyos_defined %} + graceful-restart helper supported-grace-time {{ graceful_restart.helper.supported_grace_time }} +{%     endif %} +{% endif %}  {% if log_adjacency_changes is vyos_defined %}   log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is vyos_defined }}  {% endif %} diff --git a/data/templates/frr/ospfd.frr.j2 b/data/templates/frr/ospfd.frr.j2 index 1ee8d8752..040628e82 100644 --- a/data/templates/frr/ospfd.frr.j2 +++ b/data/templates/frr/ospfd.frr.j2 @@ -133,6 +133,9 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}  {% if auto_cost.reference_bandwidth is vyos_defined %}   auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }}  {% endif %} +{% if capability.opaque is vyos_defined %} + capability opaque +{% endif %}  {% if default_information.originate is vyos_defined %}   default-information originate {{ 'always' if default_information.originate.always is vyos_defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is vyos_defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is vyos_defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is vyos_defined }}  {% endif %} @@ -153,6 +156,27 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}  {% if distance.ospf is vyos_defined %}   distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is vyos_defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is vyos_defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is vyos_defined }}  {% endif %} +{% if graceful_restart is vyos_defined %} +{%     if graceful_restart.grace_period is vyos_defined %} + graceful-restart grace-period {{ graceful_restart.grace_period }} +{%     endif %} +{%     if graceful_restart.helper.enable.router_id is vyos_defined %} +{%         for router_id in graceful_restart.helper.enable.router_id %} + graceful-restart helper enable {{ router_id }} +{%         endfor %} +{%     elif graceful_restart.helper.enable is vyos_defined %} + graceful-restart helper enable +{%     endif %} +{%     if graceful_restart.helper.planned_only is vyos_defined %} + graceful-restart helper planned-only +{%     endif %} +{%     if graceful_restart.helper.no_strict_lsa_checking is vyos_defined %} + no graceful-restart helper strict-lsa-checking +{%     endif %} +{%     if graceful_restart.helper.supported_grace_time is vyos_defined %} + graceful-restart helper supported-grace-time {{ graceful_restart.helper.supported_grace_time }} +{%     endif %} +{% endif %}  {% if log_adjacency_changes is vyos_defined %}   log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is vyos_defined }}  {% endif %} diff --git a/data/templates/ids/fastnetmon.j2 b/data/templates/ids/fastnetmon.j2 index 0340d3c92..f6f03d0db 100644 --- a/data/templates/ids/fastnetmon.j2 +++ b/data/templates/ids/fastnetmon.j2 @@ -29,10 +29,19 @@ unban_only_if_attack_finished = on  # For each subnet, list track speed in bps and pps for both directions  enable_subnet_counters = off -{% if mode.mirror is vyos_defined %} +{% if mode is vyos_defined('mirror') %}  mirror_afpacket = on +{% elif mode is vyos_defined('sflow') %} +sflow = on +{%     if sflow.port is vyos_defined %} +sflow_port = {{ sflow.port }} +{%     endif %} +{%     if sflow.listen_address is vyos_defined %} +sflow_host = {{ sflow.listen_address }} +{%     endif %}  {% endif %} +  process_incoming_traffic = {{ 'on' if direction is vyos_defined and 'in' in direction else 'off' }}  process_outgoing_traffic = {{ 'on' if direction is vyos_defined and 'out' in direction else 'off' }} diff --git a/data/templates/ids/fastnetmon_networks_list.j2 b/data/templates/ids/fastnetmon_networks_list.j2 index 5f1b3ba4d..0a0576d2a 100644 --- a/data/templates/ids/fastnetmon_networks_list.j2 +++ b/data/templates/ids/fastnetmon_networks_list.j2 @@ -1,4 +1,4 @@ -{% if network is vyos_defined() %} +{% if network is vyos_defined %}  {%     for net in network %}  {{ net }}  {%     endfor %} diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2 index 6332ed9c2..d144529f3 100644 --- a/data/templates/openvpn/server.conf.j2 +++ b/data/templates/openvpn/server.conf.j2 @@ -48,6 +48,9 @@ push "redirect-gateway def1"  {% if use_lzo_compression is vyos_defined %}  compress lzo  {% endif %} +{% if offload.dco is not vyos_defined %} +disable-dco +{% endif %}  {% if mode is vyos_defined('client') %}  # diff --git a/debian/control b/debian/control index 7880bd317..772edb540 100644 --- a/debian/control +++ b/debian/control @@ -91,7 +91,7 @@ Depends:    libqmi-utils,    libstrongswan-extra-plugins (>=5.9),    libstrongswan-standard-plugins (>=5.9), -  libvppinfra, +  libvppinfra [amd64],    libvyosconfig0,    linux-cpupower,    lldpd, @@ -116,7 +116,6 @@ Depends:    openvpn,    openvpn-auth-ldap,    openvpn-auth-radius, -  openvpn-dco,    openvpn-otp,    owamp-client,    owamp-server, @@ -146,7 +145,7 @@ Depends:    python3-tabulate,    python3-vici (>= 5.7.2),    python3-voluptuous, -  python3-vpp-api, +  python3-vpp-api [amd64],    python3-xmltodict,    python3-zmq,    qrencode, @@ -181,9 +180,9 @@ Depends:    uidmap,    usb-modeswitch,    usbutils, -  vpp, -  vpp-plugin-core, -  vpp-plugin-dpdk, +  vpp [amd64], +  vpp-plugin-core [amd64], +  vpp-plugin-dpdk [amd64],    vyatta-bash,    vyatta-cfg,    vyos-http-api-tools, diff --git a/debian/rules b/debian/rules index 9ada2bf87..39185f1e6 100755 --- a/debian/rules +++ b/debian/rules @@ -9,6 +9,7 @@ VYOS_CFG_TMPL_DIR := opt/vyatta/share/vyatta-cfg/templates  VYOS_OP_TMPL_DIR := opt/vyatta/share/vyatta-op/templates  VYOS_MIBS_DIR := usr/share/snmp/mibs  VYOS_LOCALUI_DIR := srv/localui +VYOS_XML_CACHE_DIR := python/vyos/xml_ref/pkg_cache  MIGRATION_SCRIPTS_DIR := opt/vyatta/etc/config-migrate/migrate  SYSTEM_SCRIPTS_DIR := usr/libexec/vyos/system @@ -35,6 +36,8 @@ override_dh_auto_install:  	# convert the XML to dictionaries  	env PYTHONPATH=python python3 python/vyos/xml/generate.py +	cp xml_cache/vyos_1x_cache.py python/vyos/xml_ref/pkg_cache +  	cd python; python3 setup.py install --install-layout=deb --root ../$(DIR); cd ..  	# Install scripts diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst index 93e7ced9b..b1bd23ff2 100644 --- a/debian/vyos-1x.postinst +++ b/debian/vyos-1x.postinst @@ -180,6 +180,9 @@ systemctl enable vyos-config-cloud-init.service  # Generate API GraphQL schema  /usr/libexec/vyos/services/api/graphql/generate/generate_schema.py +# Update XML cache +python3 /usr/lib/python3/dist-packages/vyos/xml_ref/update_cache.py +  # T1797: disable VPP support for rolling release, should be used by developers  # only (in the initial phase). If you wan't to enable VPP use the below command  # on your VyOS installation: diff --git a/interface-definitions/include/interface/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i index 609af1a2b..5ca1d525f 100644 --- a/interface-definitions/include/interface/dhcpv6-options.xml.i +++ b/interface-definitions/include/interface/dhcpv6-options.xml.i @@ -95,6 +95,12 @@          <valueless/>        </properties>      </leafNode> +    <leafNode name="no-release"> +      <properties> +        <help>Do not send a release message on client exit</help> +        <valueless/> +      </properties> +    </leafNode>    </children>  </node>  <!-- include end --> diff --git a/interface-definitions/include/ospf/graceful-restart.xml.i b/interface-definitions/include/ospf/graceful-restart.xml.i new file mode 100644 index 000000000..37d9a7f13 --- /dev/null +++ b/interface-definitions/include/ospf/graceful-restart.xml.i @@ -0,0 +1,67 @@ +<!-- include start from ospf/graceful-restart.xml.i --> +<node name="graceful-restart"> +  <properties> +    <help>Graceful Restart</help> +  </properties> +  <children> +    <leafNode name="grace-period"> +      <properties> +        <help>Maximum length of the grace period</help> +        <valueHelp> +          <format>u32:1-1800</format> +          <description>Maximum length of the grace period in seconds</description> +        </valueHelp> +        <constraint> +          <validator name="numeric" argument="--range 5-1800"/> +        </constraint> +      </properties> +      <defaultValue>120</defaultValue> +    </leafNode> +    <node name="helper"> +      <properties> +        <help>OSPF graceful-restart helpers</help> +      </properties> +      <children> +        <node name="enable"> +          <properties> +            <help>Enable helper support</help> +          </properties> +          <children> +            <leafNode name="router-id"> +              <properties> +                <help>Advertising Router-ID</help> +                <valueHelp> +                  <format>ipv4</format> +                  <description>Router-ID in IP address format</description> +                </valueHelp> +                <constraint> +                  <validator name="ipv4-address"/> +                </constraint> +                <multi/> +              </properties> +            </leafNode> +          </children> +        </node> +        <leafNode name="planned-only"> +          <properties> +            <help>Supported only planned restart</help> +            <valueless/> +          </properties> +        </leafNode> +        <leafNode name="supported-grace-time"> +          <properties> +            <help>Supported grace timer</help> +            <valueHelp> +              <format>u32:10-1800</format> +              <description>Grace interval in seconds</description> +            </valueHelp> +            <constraint> +              <validator name="numeric" argument="--range 10-1800"/> +            </constraint> +          </properties> +        </leafNode> +      </children> +    </node> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index 3492b873f..c4778e126 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -326,6 +326,19 @@    </children>  </tagNode>  #include <include/ospf/auto-cost.xml.i> +<node name="capability"> +  <properties> +    <help>Enable specific OSPF features</help> +  </properties> +  <children> +    <leafNode name="opaque"> +      <properties> +        <help>Opaque LSA</help> +        <valueless/> +      </properties> +    </leafNode> +  </children> +</node>  #include <include/ospf/default-information.xml.i>  <leafNode name="default-metric">    <properties> @@ -339,6 +352,21 @@      </constraint>    </properties>  </leafNode> +#include <include/ospf/graceful-restart.xml.i> +<node name="graceful-restart"> +  <children> +    <node name="helper"> +      <children> +        <leafNode name="no-strict-lsa-checking"> +          <properties> +            <help>Disable strict LSA check</help> +            <valueless/> +          </properties> +        </leafNode> +      </children> +    </node> +  </children> +</node>  <leafNode name="maximum-paths">    <properties>      <help>Maximum multiple paths (ECMP)</help> @@ -928,4 +956,4 @@      </node>    </children>  </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i index a7de50638..4c3ca68e1 100644 --- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i +++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i @@ -107,6 +107,21 @@      </node>    </children>  </node> +#include <include/ospf/graceful-restart.xml.i> +<node name="graceful-restart"> +  <children> +    <node name="helper"> +      <children> +        <leafNode name="lsa-check-disable"> +          <properties> +            <help>Disable strict LSA check</help> +            <valueless/> +          </properties> +        </leafNode> +      </children> +    </node> +  </children> +</node>  <tagNode name="interface">    <properties>      <help>Enable routing on an IPv6 interface</help> diff --git a/interface-definitions/include/version/ntp-version.xml.i b/interface-definitions/include/version/ntp-version.xml.i index 9eafbf7f0..155c824dc 100644 --- a/interface-definitions/include/version/ntp-version.xml.i +++ b/interface-definitions/include/version/ntp-version.xml.i @@ -1,3 +1,3 @@  <!-- include start from include/version/ntp-version.xml.i --> -<syntaxVersion component='ntp' version='2'></syntaxVersion> +<syntaxVersion component='ntp' version='3'></syntaxVersion>  <!-- include end --> diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 4e061c3e6..127a8179b 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -285,6 +285,19 @@                </constraint>              </properties>            </leafNode> +          <node name="offload"> +            <properties> +              <help>Configurable offload options</help> +            </properties> +            <children> +              <leafNode name="dco"> +                <properties> +                  <help>Enable data channel offload on this interface</help> +                  <valueless/> +                </properties> +              </leafNode> +            </children> +          </node>            <leafNode name="openvpn-option">              <properties>                <help>Additional OpenVPN options. You must use the syntax of openvpn.conf in this text-field. Using this without proper knowledge may result in a crashed OpenVPN server. Check system log to look for errors.</help> diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in index 501ff05d3..a06ceefb6 100644 --- a/interface-definitions/nat.xml.in +++ b/interface-definitions/nat.xml.in @@ -44,6 +44,14 @@                    </leafNode>                    #include <include/nat-translation-port.xml.i>                    #include <include/nat-translation-options.xml.i> +                  <node name="redirect"> +                    <properties> +                      <help>Redirect to local host</help> +                    </properties> +                    <children> +                      #include <include/nat-translation-port.xml.i> +                    </children> +                  </node>                  </children>                </node>              </children> diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in index 2275dd61c..4e874434b 100644 --- a/interface-definitions/ntp.xml.in +++ b/interface-definitions/ntp.xml.in @@ -57,7 +57,7 @@              </children>            </tagNode>            #include <include/allow-client.xml.i> -          #include <include/generic-interface-multi.xml.i> +          #include <include/generic-interface.xml.i>            #include <include/listen-address.xml.i>            #include <include/interface/vrf.xml.i>          </children> diff --git a/interface-definitions/service-ids-ddos-protection.xml.in b/interface-definitions/service-ids-ddos-protection.xml.in index bb06189bc..78463136b 100644 --- a/interface-definitions/service-ids-ddos-protection.xml.in +++ b/interface-definitions/service-ids-ddos-protection.xml.in @@ -70,17 +70,34 @@                    <multi/>                  </properties>                </leafNode> -              <node name="mode"> +              <leafNode name="mode">                  <properties> -                  <help>Traffic capture modes</help> +                  <help>Traffic capture mode</help> +                  <completionHelp> +                    <list>mirror sflow</list> +                  </completionHelp> +                  <valueHelp> +                    <format>mirror</format> +                    <description>Listen to mirrored traffic</description> +                  </valueHelp> +                  <valueHelp> +                    <format>sflow</format> +                    <description>Capture sFlow flows</description> +                  </valueHelp> +                  <constraint> +                    <regex>(mirror|sflow)</regex> +                  </constraint> +                </properties> +              </leafNode> +              <node name="sflow"> +                <properties> +                  <help>Sflow settings</help>                  </properties>                  <children> -                  <!-- Future modes "mirror" "netflow" "combine (both)" --> -                  <leafNode name="mirror"> -                    <properties> -                      <help>Listen mirrored traffic mode</help> -                      <valueless/> -                    </properties> +                  #include <include/listen-address-ipv4-single.xml.i> +                  #include <include/port-number.xml.i> +                  <leafNode name="port"> +                    <defaultValue>6343</defaultValue>                    </leafNode>                  </children>                </node> diff --git a/op-mode-definitions/dhcp.xml.in b/op-mode-definitions/dhcp.xml.in index 66584efc3..6855fe447 100644 --- a/op-mode-definitions/dhcp.xml.in +++ b/op-mode-definitions/dhcp.xml.in @@ -7,6 +7,30 @@            <help>Show DHCP (Dynamic Host Configuration Protocol) information</help>          </properties>          <children> +          <node name="client"> +            <properties> +              <help>Show DHCP client information</help> +            </properties> +            <children> +              <node name="leases"> +                <properties> +                  <help>Show DHCP client leases</help> +                </properties> +                <children> +                  <tagNode name="interface"> +                    <properties> +                      <help> Show DHCP client information for a given interface</help> +                      <completionHelp> +                        <script>${vyos_completion_dir}/list_interfaces --broadcast</script> +                      </completionHelp> +                    </properties> +                    <command>${vyos_op_scripts_dir}/dhcp.py show_client_leases --family inet --interface $6</command> +                  </tagNode> +                </children> +                <command>${vyos_op_scripts_dir}/dhcp.py show_client_leases --family inet</command> +              </node> +             </children> +          </node>            <node name="server">              <properties>                <help>Show DHCP server information</help> diff --git a/op-mode-definitions/include/monitor-background.xml.i b/op-mode-definitions/include/monitor-background.xml.i deleted file mode 100644 index 9931127e3..000000000 --- a/op-mode-definitions/include/monitor-background.xml.i +++ /dev/null @@ -1,21 +0,0 @@ -<!-- included start from monitor-background.xml.i --> -<node name="background"> -  <properties> -    <help>Monitor in background</help> -  </properties> -  <children> -    <node name="start"> -      <properties> -        <help>Start background monitoring</help> -      </properties> -      <command>${vyatta_bindir}/vyatta-monitor-background ${3^^} ${3}</command> -    </node> -    <node name="stop"> -      <properties> -        <help>Stop background monitoring</help> -      </properties> -      <command>${vyatta_bindir}/vyatta-monitor-background-stop ${3^^}</command> -    </node> -  </children> -</node> -<!-- included end --> diff --git a/op-mode-definitions/include/ospf-common.xml.i b/op-mode-definitions/include/ospf/common.xml.i index 979ffb07e..c8341bd3e 100644 --- a/op-mode-definitions/include/ospf-common.xml.i +++ b/op-mode-definitions/include/ospf/common.xml.i @@ -502,6 +502,7 @@      </tagNode>    </children>  </node> +#include <include/ospf/graceful-restart.xml.i>  <node name="interface">    <properties>      <help>Show IPv4 OSPF interface information</help> @@ -556,4 +557,3 @@    </children>  </node>  <!-- included end --> - diff --git a/op-mode-definitions/include/ospf/graceful-restart.xml.i b/op-mode-definitions/include/ospf/graceful-restart.xml.i new file mode 100644 index 000000000..736d8f951 --- /dev/null +++ b/op-mode-definitions/include/ospf/graceful-restart.xml.i @@ -0,0 +1,13 @@ +<node name="graceful-restart"> +    <properties> +      <help>Show IPv4 OSPF Graceful Restart</help> +    </properties> +    <children> +      <leafNode name="helper"> +        <properties> +          <help>OSPF Graceful Restart helper details</help> +        </properties> +        <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +      </leafNode> +    </children> +  </node> diff --git a/op-mode-definitions/monitor-bridge.xml.in b/op-mode-definitions/monitor-bridge.xml.in index 712a924f1..a43fa6dd9 100644 --- a/op-mode-definitions/monitor-bridge.xml.in +++ b/op-mode-definitions/monitor-bridge.xml.in @@ -4,14 +4,14 @@      <children>        <node name="bridge">          <properties> -          <help>Monitoring bridge database generated objects and address changes</help> +          <help>Monitor bridge database changes</help>          </properties>          <command>sudo bridge monitor all</command>          <children>            <node name="link">              <command>sudo bridge monitor link</command>              <properties> -              <help>Monitoring bridge database generated connection interface changes</help> +              <help>Monitor bridge database generated connection interface changes</help>              </properties>            </node>            <node name="fdb"> diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in index 8f3f73478..ee52a7eb8 100644 --- a/op-mode-definitions/monitor-log.xml.in +++ b/op-mode-definitions/monitor-log.xml.in @@ -1,6 +1,9 @@  <?xml version="1.0"?>  <interfaceDefinition>    <node name="monitor"> +    <properties> +      <help>Monitor system information</help> +    </properties>      <children>        <node name="log">          <properties> @@ -27,6 +30,12 @@                </leafNode>              </children>            </node> +          <leafNode name="conntrack-sync"> +            <properties> +              <help>Monitor last lines of conntrack-sync log</help> +            </properties> +            <command>journalctl --no-hostname --follow --boot --unit conntrackd.service</command> +          </leafNode>            <node name="dhcp">              <properties>                <help>Monitor last lines of Dynamic Host Control Protocol log</help> @@ -117,6 +126,23 @@              </properties>              <command>journalctl --no-hostname --boot --follow --unit chrony.service</command>            </leafNode> +          <node name="openvpn"> +            <properties> +              <help>Monitor last lines of OpenVPN log</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit openvpn@*.service</command> +            <children> +              <tagNode name="interface"> +                <properties> +                  <help>Monitor last lines of specific OpenVPN interface log</help> +                  <completionHelp> +                    <path>interfaces openvpn</path> +                  </completionHelp> +                </properties> +                <command>journalctl --no-hostname --boot --unit openvpn@$5.service</command> +              </tagNode> +            </children> +          </node>            <node name="pppoe">              <properties>                <help>Monitor last lines of PPPoE interface log</help> @@ -248,9 +274,45 @@              </properties>              <command>journalctl --no-hostname --boot --follow --unit ssh.service</command>            </leafNode> +          <leafNode name="vpn"> +            <properties> +              <help>Monitor last lines of ALL Virtual Private Network services</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command> +          </leafNode> +          <leafNode name="ipsec"> +            <properties> +              <help>Monitor last lines of IPsec log</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit strongswan.service</command> +          </leafNode> +          <leafNode name="l2tp"> +            <properties> +              <help>Monitor last lines of L2TP log</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit accel-ppp@l2tp.service</command> +          </leafNode> +          <leafNode name="openconnect"> +            <properties> +              <help>Monitor last lines of OpenConnect log</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit ocserv.service</command> +          </leafNode> +          <leafNode name="pptp"> +            <properties> +              <help>Monitor last lines of PPTP log</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit accel-ppp@pptp.service</command> +          </leafNode> +          <leafNode name="sstp"> +            <properties> +              <help>Monitor last lines of Secure Socket Tunneling Protocol server</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit accel-ppp@sstp.service</command> +          </leafNode>            <node name="sstpc">              <properties> -              <help>Monitor last lines of Secure Socket Tunneling Protocol log</help> +              <help>Monitor last lines of Secure Socket Tunneling Protocol client</help>              </properties>              <command>journalctl --no-hostname --boot --follow --unit "ppp@sstpc*.service"</command>              <children> @@ -265,49 +327,6 @@                </tagNode>              </children>            </node> -          <node name="vpn"> -            <properties> -              <help>Monitor Virtual Private Network services</help> -            </properties> -            <children> -              <leafNode name="all"> -                <properties> -                  <help>Monitor last lines of ALL VPNs</help> -                </properties> -                <command>journalctl --no-hostname --boot --follow --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command> -              </leafNode> -              <leafNode name="ipsec"> -                <properties> -                  <help>Monitor last lines of IPsec log</help> -                </properties> -                <command>journalctl --no-hostname --boot --follow --unit strongswan.service</command> -              </leafNode> -              <leafNode name="l2tp"> -                <properties> -                  <help>Monitor last lines of L2TP log</help> -                </properties> -                <command>journalctl --no-hostname --boot --follow --unit accel-ppp@l2tp.service</command> -              </leafNode> -              <leafNode name="openconnect"> -                <properties> -                  <help>Monitor last lines of OpenConnect log</help> -                </properties> -                <command>journalctl --no-hostname --boot --follow --unit ocserv.service</command> -              </leafNode> -              <leafNode name="pptp"> -                <properties> -                  <help>Monitor last lines of PPTP log</help> -                </properties> -                <command>journalctl --no-hostname --boot --follow --unit accel-ppp@pptp.service</command> -              </leafNode> -              <leafNode name="sstp"> -                <properties> -                  <help>Monitor last lines of SSTP log</help> -                </properties> -                <command>journalctl --no-hostname --boot --follow --unit accel-ppp@sstp.service</command> -              </leafNode> -            </children> -          </node>            <leafNode name="vpp">              <properties>                <help>Monitor last lines of Vector Packet Processor log</help> @@ -320,12 +339,6 @@              </properties>              <command>journalctl --no-hostname --boot --follow --unit keepalived.service</command>            </leafNode> -          <leafNode name="webproxy"> -            <properties> -              <help>Monitor last lines of Webproxy log</help> -            </properties> -            <command>journalctl --no-hostname --boot --follow --unit squid.service</command> -          </leafNode>          </children>        </node>      </children> diff --git a/op-mode-definitions/monitor-ndp.xml.in b/op-mode-definitions/monitor-ndp.xml.in index 26d881f1a..3b08f3d73 100644 --- a/op-mode-definitions/monitor-ndp.xml.in +++ b/op-mode-definitions/monitor-ndp.xml.in @@ -4,14 +4,14 @@      <children>        <node name="ndp">          <properties> -          <help>Monitor the NDP information received by the router through the device</help> +          <help>Monitor Neighbor Discovery Protocol (NDP) information</help>          </properties>          <command>sudo ndptool monitor</command>          <children>            <tagNode name="interface">              <command>sudo ndptool monitor --ifname=$4</command>              <properties> -              <help>Monitor ndp protocol on specified interface</help> +              <help>Monitor Neighbor Discovery Protocol on specified interface</help>                <completionHelp>                  <script>${vyos_completion_dir}/list_interfaces</script>                </completionHelp> @@ -20,7 +20,7 @@                <tagNode name="type">                  <command>sudo ndptool monitor --ifname=$4 --msg-type=$6</command>                  <properties> -                  <help>Monitor specific types of NDP protocols</help> +                  <help>Monitor specific Neighbor Discovery Protocol type</help>                    <completionHelp>                      <list>rs ra ns na</list>                    </completionHelp> @@ -31,7 +31,7 @@            <tagNode name="type">              <command>sudo ndptool monitor --msg-type=$4</command>              <properties> -              <help>Monitor specific types of NDP protocols</help> +              <help>Monitor specific Neighbor Discovery Protocol type</help>                <completionHelp>                  <list>rs ra ns na</list>                </completionHelp> diff --git a/op-mode-definitions/monitor-protocol.xml.in b/op-mode-definitions/monitor-protocol.xml.in index f3af3575c..f05a1945f 100644 --- a/op-mode-definitions/monitor-protocol.xml.in +++ b/op-mode-definitions/monitor-protocol.xml.in @@ -12,7 +12,6 @@                <help>Monitor the Border Gateway Protocol (BGP)</help>              </properties>              <children> -              #include <include/monitor-background.xml.i>                <node name="disable">                  <properties>                    <help>Disable Border Gateway Protocol (BGP) debugging</help> @@ -266,7 +265,6 @@                <help>Monitor Open Shortest Path First (OSPF) protocol</help>              </properties>              <children> -              #include <include/monitor-background.xml.i>                <node name="disable">                  <properties>                    <help>Disable Open Shortest Path First (OSPF) debugging</help> @@ -650,25 +648,6 @@                <help>Monitor the IPv6 Open Shortest Path First (OSPFv3) protocol</help>              </properties>              <children> -              <node name="background"> -                <properties> -                  <help>Monitor in background</help> -                </properties> -                <children> -                  <node name="start"> -                    <properties> -                      <help>Start background monitoring</help> -                    </properties> -                    <command>${vyatta_bindir}/vyatta-monitor-background OSPFv3 ospf6</command> -                  </node> -                  <node name="stop"> -                    <properties> -                      <help>Stop background monitoring</help> -                    </properties> -                    <command>${vyatta_bindir}/vyatta-monitor-background-stop OSPFv3</command> -                  </node> -                </children> -              </node>                <node name="disable">                  <properties>                    <help>Disable IPv6 Open Shortest Path First (OSPFv3) protocol debugging</help> @@ -1192,25 +1171,6 @@                <help>Monitor the Routing Information Base (RIB)</help>              </properties>              <children> -              <node name="background"> -                <properties> -                  <help>Monitor in background</help> -                </properties> -                <children> -                  <node name="start"> -                    <properties> -                      <help>Start background monitoring</help> -                    </properties> -                    <command>${vyatta_bindir}/vyatta-monitor-background RIB zebra</command> -                  </node> -                  <node name="stop"> -                    <properties> -                      <help>Stop background monitoring</help> -                    </properties> -                    <command>${vyatta_bindir}/vyatta-monitor-background-stop RIB</command> -                  </node> -                </children> -              </node>                <node name="disable">                  <properties>                    <help>Disable Route Information Base (RIB) debugging</help> @@ -1360,7 +1320,6 @@                <help>Monitor the Routing Information Protocol (RIP)</help>              </properties>              <children> -              #include <include/monitor-background.xml.i>                <node name="disable">                  <properties>                    <help>Disable Routing Information Protocol (RIP) debugging</help> @@ -1452,7 +1411,6 @@                <help>Monitor the Routing Information Protocol Next Generation (RIPng) protocol</help>              </properties>              <children> -              #include <include/monitor-background.xml.i>                <node name="disable">                  <properties>                    <help>Disable Routing Information Protocol Next Generation (RIPNG) debugging</help> diff --git a/op-mode-definitions/show-bridge.xml.in b/op-mode-definitions/show-bridge.xml.in index acf3a00c7..a272ea204 100644 --- a/op-mode-definitions/show-bridge.xml.in +++ b/op-mode-definitions/show-bridge.xml.in @@ -42,6 +42,18 @@              </properties>              <command>${vyos_op_scripts_dir}/bridge.py show_fdb --interface=$3</command>            </leafNode> +          <leafNode name="detail"> +            <properties> +              <help>Display bridge interface details</help> +            </properties> +            <command>${vyos_op_scripts_dir}/bridge.py show_detail --interface=$3</command> +          </leafNode> +          <leafNode name="nexthop-group"> +            <properties> +              <help>Display bridge interface nexthop-group</help> +            </properties> +            <command>${vyos_op_scripts_dir}/bridge.py show_detail --nexthop_group --interface=$3</command> +          </leafNode>          </children>        </tagNode>      </children> diff --git a/op-mode-definitions/show-ip-ospf.xml.in b/op-mode-definitions/show-ip-ospf.xml.in index 704ed984f..f3b9da90c 100644 --- a/op-mode-definitions/show-ip-ospf.xml.in +++ b/op-mode-definitions/show-ip-ospf.xml.in @@ -13,7 +13,7 @@              </properties>              <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>              <children> -              #include <include/ospf-common.xml.i> +              #include <include/ospf/common.xml.i>                <tagNode name="vrf">                  <properties>                    <help>Show OSPF routing protocol for given VRF</help> @@ -24,7 +24,7 @@                  </properties>                  <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>                  <children> -                  #include <include/ospf-common.xml.i> +                  #include <include/ospf/common.xml.i>                  </children>                </tagNode>              </children> diff --git a/op-mode-definitions/show-ipv6-ospfv3.xml.in b/op-mode-definitions/show-ipv6-ospfv3.xml.in index a63465472..e1fcf470f 100644 --- a/op-mode-definitions/show-ipv6-ospfv3.xml.in +++ b/op-mode-definitions/show-ipv6-ospfv3.xml.in @@ -41,6 +41,7 @@                </tagNode>                #include <include/ospfv3/border-routers.xml.i>                #include <include/ospfv3/database.xml.i> +              #include <include/ospf/graceful-restart.xml.i>                #include <include/ospfv3/interface.xml.i>                #include <include/ospfv3/linkstate.xml.i>                #include <include/ospfv3/neighbor.xml.i> @@ -94,6 +95,7 @@                    </tagNode>                    #include <include/ospfv3/border-routers.xml.i>                    #include <include/ospfv3/database.xml.i> +                  #include <include/ospf/graceful-restart.xml.i>                    #include <include/ospfv3/interface.xml.i>                    #include <include/ospfv3/linkstate.xml.i>                    #include <include/ospfv3/neighbor.xml.i> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index 579e348f7..925a780ac 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -1,6 +1,9 @@  <?xml version="1.0"?>  <interfaceDefinition>    <node name="show"> +    <properties> +      <help>Show system information</help> +    </properties>      <children>        <tagNode name="log">          <properties> @@ -395,23 +398,6 @@              </properties>              <command>journalctl --no-hostname --boot --unit ssh.service</command>            </leafNode> -          <node name="sstpc"> -            <properties> -              <help>Show log for SSTP client</help> -            </properties> -            <command>journalctl --no-hostname --boot --unit "ppp@sstpc*.service"</command> -            <children> -              <tagNode name="interface"> -                <properties> -                  <help>Show SSTP client log on specific interface</help> -                  <completionHelp> -                    <path>interfaces sstpc</path> -                  </completionHelp> -                </properties> -                <command>journalctl --no-hostname --boot --unit "ppp@$5.service"</command> -              </tagNode> -            </children> -          </node>            <tagNode name="tail">              <properties>                <help>Show last n changes to messages</help> @@ -427,47 +413,57 @@              </properties>              <command>tail -n 10 /var/log/messages</command>            </node> -          <node name="vpn"> +          <leafNode name="vpn"> +            <properties> +              <help>Monitor last lines of ALL Virtual Private Network services</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command> +          </leafNode> +          <leafNode name="ipsec"> +            <properties> +              <help>Show log for IPsec</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit strongswan.service</command> +          </leafNode> +          <leafNode name="l2tp"> +            <properties> +              <help>Show log for L2TP</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit accel-ppp@l2tp.service</command> +          </leafNode> +          <leafNode name="openconnect"> +            <properties> +              <help>Show log for OpenConnect</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit ocserv.service</command> +          </leafNode> +          <leafNode name="pptp"> +            <properties> +              <help>Show log for PPTP</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit accel-ppp@pptp.service</command> +          </leafNode> +          <leafNode name="sstp"> +            <properties> +              <help>Show log for Secure Socket Tunneling Protocol (SSTP) server</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit accel-ppp@sstp.service</command> +          </leafNode> +          <node name="sstpc">              <properties> -              <help>Show log for Virtual Private Network (VPN)</help> +              <help>Show log for Secure Socket Tunneling Protocol (SSTP) client</help>              </properties> +            <command>journalctl --no-hostname --boot --unit "ppp@sstpc*.service"</command>              <children> -              <leafNode name="all"> -                <properties> -                  <help>Show log for ALL</help> -                </properties> -                <command>journalctl --no-hostname --boot --unit strongswan.service --unit accel-ppp@*.service --unit ocserv.service</command> -              </leafNode> -              <leafNode name="ipsec"> -                <properties> -                  <help>Show log for IPsec</help> -                </properties> -                <command>journalctl --no-hostname --boot --unit strongswan.service</command> -              </leafNode> -              <leafNode name="l2tp"> -                <properties> -                  <help>Show log for L2TP</help> -                </properties> -                <command>journalctl --no-hostname --boot --unit accel-ppp@l2tp.service</command> -              </leafNode> -              <leafNode name="openconnect"> -                <properties> -                  <help>Show log for OpenConnect</help> -                </properties> -                <command>journalctl --no-hostname --boot --unit ocserv.service</command> -              </leafNode> -              <leafNode name="pptp"> -                <properties> -                  <help>Show log for PPTP</help> -                </properties> -                <command>journalctl --no-hostname --boot --unit accel-ppp@pptp.service</command> -              </leafNode> -              <leafNode name="sstp"> +              <tagNode name="interface">                  <properties> -                  <help>Show log for SSTP</help> +                  <help>Show SSTP client log on specific interface</help> +                  <completionHelp> +                    <path>interfaces sstpc</path> +                  </completionHelp>                  </properties> -                <command>journalctl --no-hostname --boot --unit accel-ppp@sstp.service</command> -              </leafNode> +                <command>journalctl --no-hostname --boot --unit "ppp@$5.service"</command> +              </tagNode>              </children>            </node>            <leafNode name="vpp"> diff --git a/op-mode-definitions/webproxy.xml.in b/op-mode-definitions/webproxy.xml.in index 5ae1577d8..57df44ff8 100644 --- a/op-mode-definitions/webproxy.xml.in +++ b/op-mode-definitions/webproxy.xml.in @@ -2,43 +2,28 @@  <interfaceDefinition>    <node name="monitor">      <children> -      <node name="webproxy"> -        <properties> -          <help>Monitor WebProxy service</help> -        </properties> -        <command>${vyatta_bindir}/vyatta-monitor Webproxy squid</command> +      <node name="log">          <children> -          <node name="access-log"> -            <properties> -              <help>Monitor the last lines of the squid access log</help> -            </properties> -            <command>if [ -f /var/log/squid/access.log ]; then sudo tail --follow=name /var/log/squid/access.log; else echo "WebProxy access-log does not exist"; fi</command> -          </node> -          <node name="background"> +          <node name="webproxy">              <properties> -              <help>Monitor Webproxy in the background</help> +              <help>Monitor last lines of Webproxy log</help>              </properties> +            <command>journalctl --no-hostname --boot --follow --unit squid.service</command>              <children> -              <node name="start"> +              <leafNode name="access-log">                  <properties> -                  <help>Start background monitoring of Webproxy</help> +                  <help>Monitor the last lines of the Webproxy access log</help>                  </properties> -                <command>${vyatta_bindir}/vyatta-monitor-background Webproxy squid</command> -              </node> -              <node name="stop"> +                <command>if [ -f /var/log/squid/access.log ]; then sudo tail --follow=name /var/log/squid/access.log; else echo "WebProxy access-log does not exist"; fi</command> +              </leafNode> +              <leafNode name="cache-log">                  <properties> -                  <help>Stop background monitoring of Webproxy</help> +                  <help>Monitor the last lines of the Webproxy cache log</help>                  </properties> -                <command>${vyatta_bindir}/vyatta-monitor-background-stop Webproxy </command> -              </node> +                <command>if [ -f /var/log/squid/cache.log ]; then sudo tail --follow=name /var/log/squid/cache.log; else echo "WebProxy cache-log does not exist"; fi</command> +              </leafNode>              </children>            </node> -          <node name="cache-log"> -            <properties> -              <help>Monitor the last lines of the squid cache log</help> -            </properties> -            <command>if [ -f /var/log/squid/cache.log ]; then sudo tail --follow=name /var/log/squid/cache.log; else echo "WebProxy cache-log does not exist"; fi</command> -          </node>          </children>        </node>      </children> @@ -102,6 +87,17 @@                <help>Update the webproxy blacklist database</help>              </properties>              <command>sudo ${vyos_op_scripts_dir}/webproxy_update_blacklist.sh --update-blacklist</command> +            <children> +              <tagNode name="vrf"> +                <properties> +                  <help>Update webproxy blacklist database via specified VRF</help> +                  <completionHelp> +                    <path>vrf name</path> +                  </completionHelp> +                </properties> +                <command>sudo ${vyos_op_scripts_dir}/webproxy_update_blacklist.sh --update-blacklist --vrf "${5}" </command> +              </tagNode> +            </children>            </node>          </children>        </node> diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index d0cd87464..e18d9817d 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -418,10 +418,6 @@ class DiffTree:          self.__diff_tree.argtypes = [c_char_p, c_void_p, c_void_p]          self.__diff_tree.restype = c_void_p -        self.__trim_tree = self.__lib.trim_tree -        self.__trim_tree.argtypes = [c_void_p, c_void_p] -        self.__trim_tree.restype = c_void_p -          check_path(path)          path_str = " ".join(map(str, path)).encode() @@ -435,11 +431,7 @@ class DiffTree:          self.add = self.full.get_subtree(['add'])          self.sub = self.full.get_subtree(['sub'])          self.inter = self.full.get_subtree(['inter']) - -        # trim sub(-tract) tree to get delete tree for commands -        ref = self.right.get_subtree(path, with_node=True) if path else self.right -        res = self.__trim_tree(self.sub._get_config(), ref._get_config()) -        self.delete = ConfigTree(address=res) +        self.delete = self.full.get_subtree(['del'])      def to_commands(self):          add = self.add.to_commands() diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 2793b201c..903cc8535 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -304,7 +304,7 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):      if 'ipsec' in rule_conf:          if 'match_ipsec' in rule_conf['ipsec']:              output.append('meta ipsec == 1') -        if 'match_non_ipsec' in rule_conf['ipsec']: +        if 'match_none' in rule_conf['ipsec']:              output.append('meta ipsec == 0')      if 'fragment' in rule_conf: diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 120f2131b..99ddb2021 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1288,9 +1288,11 @@ class Interface(Control):          ifname = self.ifname          config_file = f'/run/dhcp6c/dhcp6c.{ifname}.conf' +        options_file = f'/run/dhcp6c/dhcp6c.{ifname}.options'          systemd_service = f'dhcp6c@{ifname}.service'          if enable and 'disable' not in self._config: +            render(options_file, 'dhcp-client/dhcp6c_daemon-options.j2', self._config)              render(config_file, 'dhcp-client/ipv6.j2', self._config)              # We must ignore any return codes. This is required to enable diff --git a/python/vyos/nat.py b/python/vyos/nat.py index a56ca1ff3..418efe649 100644 --- a/python/vyos/nat.py +++ b/python/vyos/nat.py @@ -54,28 +54,32 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False):          translation_str = 'return'          log_suffix = '-EXCL'      elif 'translation' in rule_conf: -        translation_prefix = nat_type[:1] -        translation_output = [f'{translation_prefix}nat']          addr = dict_search_args(rule_conf, 'translation', 'address')          port = dict_search_args(rule_conf, 'translation', 'port') - -        if addr and is_ip_network(addr): -            if not ipv6: -                map_addr =  dict_search_args(rule_conf, nat_type, 'address') -                translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} }}') -                ignore_type_addr = True -            else: -                translation_output.append(f'prefix to {addr}') -        elif addr == 'masquerade': -            if port: -                addr = f'{addr} to ' -            translation_output = [addr] -            log_suffix = '-MASQ' +        redirect_port = dict_search_args(rule_conf, 'translation', 'redirect', 'port') +        if redirect_port: +            translation_output = [f'redirect to {redirect_port}']          else: -            translation_output.append('to') -            if addr: -                addr = bracketize_ipv6(addr) -                translation_output.append(addr) +            translation_prefix = nat_type[:1] +            translation_output = [f'{translation_prefix}nat'] + +            if addr and is_ip_network(addr): +                if not ipv6: +                    map_addr =  dict_search_args(rule_conf, nat_type, 'address') +                    translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} }}') +                    ignore_type_addr = True +                else: +                    translation_output.append(f'prefix to {addr}') +            elif addr == 'masquerade': +                if port: +                    addr = f'{addr} to ' +                translation_output = [addr] +                log_suffix = '-MASQ' +            else: +                translation_output.append('to') +                if addr: +                    addr = bracketize_ipv6(addr) +                    translation_output.append(addr)          options = []          addr_mapping = dict_search_args(rule_conf, 'translation', 'options', 'address_mapping') diff --git a/python/vyos/remote.py b/python/vyos/remote.py index 16fe2b2c2..cf731c881 100644 --- a/python/vyos/remote.py +++ b/python/vyos/remote.py @@ -25,7 +25,7 @@ import urllib.parse  from ftplib import FTP  from ftplib import FTP_TLS -from paramiko import SSHClient +from paramiko import SSHClient, SSHException  from paramiko import MissingHostKeyPolicy  from requests import Session @@ -50,7 +50,7 @@ class InteractivePolicy(MissingHostKeyPolicy):      def missing_host_key(self, client, hostname, key):          print_error(f"Host '{hostname}' not found in known hosts.")          print_error('Fingerprint: ' + key.get_fingerprint().hex()) -        if ask_yes_no('Do you wish to continue?'): +        if sys.stdout.isatty() and ask_yes_no('Do you wish to continue?'):              if client._host_keys_filename\                 and ask_yes_no('Do you wish to permanently add this host/key pair to known hosts?'):                  client._host_keys.add(hostname, key.get_name(), key) @@ -96,7 +96,13 @@ def check_storage(path, size):  class FtpC: -    def __init__(self, url, progressbar=False, check_space=False, source_host='', source_port=0): +    def __init__(self, +                 url, +                 progressbar=False, +                 check_space=False, +                 source_host='', +                 source_port=0, +                 timeout=10):          self.secure = url.scheme == 'ftps'          self.hostname = url.hostname          self.path = url.path @@ -106,12 +112,15 @@ class FtpC:          self.source = (source_host, source_port)          self.progressbar = progressbar          self.check_space = check_space +        self.timeout = timeout      def _establish(self):          if self.secure: -            return FTP_TLS(source_address=self.source, context=ssl.create_default_context()) +            return FTP_TLS(source_address=self.source, +                           context=ssl.create_default_context(), +                           timeout=self.timeout)          else: -            return FTP(source_address=self.source) +            return FTP(source_address=self.source, timeout=self.timeout)      def download(self, location: str):          # Open the file upfront before establishing connection. @@ -150,7 +159,13 @@ class FtpC:  class SshC:      known_hosts = os.path.expanduser('~/.ssh/known_hosts') -    def __init__(self, url, progressbar=False, check_space=False, source_host='', source_port=0): +    def __init__(self, +                 url, +                 progressbar=False, +                 check_space=False, +                 source_host='', +                 source_port=0, +                 timeout=10.0):          self.hostname = url.hostname          self.path = url.path          self.username = url.username or os.getenv('REMOTE_USERNAME') @@ -159,6 +174,7 @@ class SshC:          self.source = (source_host, source_port)          self.progressbar = progressbar          self.check_space = check_space +        self.timeout = timeout      def _establish(self):          ssh = SSHClient() @@ -169,7 +185,7 @@ class SshC:          ssh.set_missing_host_key_policy(InteractivePolicy())          # `socket.create_connection()` automatically picks a NIC and an IPv4/IPv6 address family          #  for us on dual-stack systems. -        sock = socket.create_connection((self.hostname, self.port), socket.getdefaulttimeout(), self.source) +        sock = socket.create_connection((self.hostname, self.port), self.timeout, self.source)          ssh.connect(self.hostname, self.port, self.username, self.password, sock=sock)          return ssh @@ -198,13 +214,20 @@ class SshC:  class HttpC: -    def __init__(self, url, progressbar=False, check_space=False, source_host='', source_port=0): +    def __init__(self, +                 url, +                 progressbar=False, +                 check_space=False, +                 source_host='', +                 source_port=0, +                 timeout=10.0):          self.urlstring = urllib.parse.urlunsplit(url)          self.progressbar = progressbar          self.check_space = check_space          self.source_pair = (source_host, source_port)          self.username = url.username or os.getenv('REMOTE_USERNAME')          self.password = url.password or os.getenv('REMOTE_PASSWORD') +        self.timeout = timeout      def _establish(self):          session = Session() @@ -220,8 +243,11 @@ class HttpC:              # Not only would it potentially mess up with the progress bar but              # `shutil.copyfileobj(request.raw, file)` does not handle automatic decoding.              s.headers.update({'Accept-Encoding': 'identity'}) -            with s.head(self.urlstring, allow_redirects=True) as r: +            with s.head(self.urlstring, +                        allow_redirects=True, +                        timeout=self.timeout) as r:                  # Abort early if the destination is inaccessible. +                print('pre-3')                  r.raise_for_status()                  # If the request got redirected, keep the last URL we ended up with.                  final_urlstring = r.url @@ -235,7 +261,8 @@ class HttpC:                      size = None              if self.check_space:                  check_storage(location, size) -            with s.get(final_urlstring, stream=True) as r, open(location, 'wb') as f: +            with s.get(final_urlstring, stream=True, +                       timeout=self.timeout) as r, open(location, 'wb') as f:                  if self.progressbar and size:                      progress = make_incremental_progressbar(CHUNK_SIZE / size)                      next(progress) @@ -249,7 +276,10 @@ class HttpC:      def upload(self, location: str):          # Does not yet support progressbars.          with self._establish() as s, open(location, 'rb') as f: -            s.post(self.urlstring, data=f, allow_redirects=True) +            s.post(self.urlstring, +                   data=f, +                   allow_redirects=True, +                   timeout=self.timeout)  class TftpC: @@ -258,10 +288,16 @@ class TftpC:      # 2. Since there's no concept authentication, we don't need to deal with keys/passwords.      # 3. It would be a waste to import, audit and maintain a third-party library for TFTP.      # 4. I'd rather not implement the entire protocol here, no matter how simple it is. -    def __init__(self, url, progressbar=False, check_space=False, source_host=None, source_port=0): +    def __init__(self, +                 url, +                 progressbar=False, +                 check_space=False, +                 source_host=None, +                 source_port=0, +                 timeout=10):          source_option = f'--interface {source_host} --local-port {source_port}' if source_host else ''          progress_flag = '--progress-bar' if progressbar else '-s' -        self.command = f'curl {source_option} {progress_flag}' +        self.command = f'curl {source_option} {progress_flag} --connect-timeout {timeout}'          self.urlstring = urllib.parse.urlunsplit(url)      def download(self, location: str): @@ -286,10 +322,16 @@ def urlc(urlstring, *args, **kwargs):          raise ValueError(f'Unsupported URL scheme: "{url.scheme}"')  def download(local_path, urlstring, *args, **kwargs): -    urlc(urlstring, *args, **kwargs).download(local_path) +    try: +        urlc(urlstring, *args, **kwargs).download(local_path) +    except Exception as err: +        print_error(f'Unable to download "{urlstring}": {err}')  def upload(local_path, urlstring, *args, **kwargs): -    urlc(urlstring, *args, **kwargs).upload(local_path) +    try: +        urlc(urlstring, *args, **kwargs).upload(local_path) +    except Exception as err: +        print_error(f'Unable to upload "{urlstring}": {err}')  def get_remote_config(urlstring, source_host='', source_port=0):      """ diff --git a/python/vyos/utils/kernel.py b/python/vyos/utils/kernel.py index 0eb113174..1f3bbdffe 100644 --- a/python/vyos/utils/kernel.py +++ b/python/vyos/utils/kernel.py @@ -25,3 +25,14 @@ def check_kmod(k_mod):          if not os.path.exists(f'/sys/module/{module}'):              if call(f'modprobe {module}') != 0:                  raise ConfigError(f'Loading Kernel module {module} failed') + +def unload_kmod(k_mod): +    """ Common utility function to unload required kernel modules on demand """ +    from vyos import ConfigError +    from vyos.utils.process import call +    if isinstance(k_mod, str): +        k_mod = k_mod.split() +    for module in k_mod: +        if os.path.exists(f'/sys/module/{module}'): +            if call(f'rmmod {module}') != 0: +                raise ConfigError(f'Unloading Kernel module {module} failed') diff --git a/python/vyos/xml_ref/generate_cache.py b/python/vyos/xml_ref/generate_cache.py index 792c6eea7..6a05d4608 100755 --- a/python/vyos/xml_ref/generate_cache.py +++ b/python/vyos/xml_ref/generate_cache.py @@ -18,10 +18,14 @@  import sys  import json -import argparse +from argparse import ArgumentParser +from argparse import ArgumentTypeError +from os import getcwd +from os import makedirs  from os.path import join  from os.path import abspath  from os.path import dirname +from os.path import basename  from xmltodict import parse  _here = dirname(__file__) @@ -29,9 +33,10 @@ _here = dirname(__file__)  sys.path.append(join(_here, '..'))  from configtree import reference_tree_to_json, ConfigTreeError -xml_cache = abspath(join(_here, 'cache.py'))  xml_cache_json = 'xml_cache.json'  xml_tmp = join('/tmp', xml_cache_json) +pkg_cache = abspath(join(_here, 'pkg_cache')) +ref_cache = abspath(join(_here, 'cache.py'))  node_data_fields = ("node_type", "multi", "valueless", "default_value") @@ -45,16 +50,26 @@ def trim_node_data(cache: dict):              if isinstance(cache[k], dict):                  trim_node_data(cache[k]) +def non_trivial(s): +    if not s: +        raise ArgumentTypeError("Argument must be non empty string") +    return s +  def main(): -    parser = argparse.ArgumentParser(description='generate and save dict from xml defintions') +    parser = ArgumentParser(description='generate and save dict from xml defintions')      parser.add_argument('--xml-dir', type=str, required=True,                          help='transcluded xml interface-definition directory') -    parser.add_argument('--save-json-dir', type=str, -                        help='directory to save json cache if needed') -    args = parser.parse_args() - -    xml_dir = abspath(args.xml_dir) -    save_dir = abspath(args.save_json_dir) if args.save_json_dir else None +    parser.add_argument('--package-name', type=non_trivial, default='vyos-1x', +                        help='name of current package') +    parser.add_argument('--output-path', help='path to generated cache') +    args = vars(parser.parse_args()) + +    xml_dir = abspath(args['xml_dir']) +    pkg_name = args['package_name'].replace('-','_') +    cache_name = pkg_name + '_cache.py' +    out_path = args['output_path'] +    path = out_path if out_path is not None else pkg_cache +    xml_cache = abspath(join(path, cache_name))      try:          reference_tree_to_json(xml_dir, xml_tmp) @@ -67,21 +82,30 @@ def main():      trim_node_data(d) -    if save_dir is not None: -        save_file = join(save_dir, xml_cache_json) -        with open(save_file, 'w') as f: -            f.write(json.dumps(d)) -      syntax_version = join(xml_dir, 'xml-component-version.xml') -    with open(syntax_version) as f: -        content = f.read() +    try: +        with open(syntax_version) as f: +            component = f.read() +    except FileNotFoundError: +        if pkg_name != 'vyos_1x': +            component = '' +        else: +            print("\nWARNING: missing xml-component-version.xml\n") +            sys.exit(1) -    parsed = parse(content) -    converted = parsed['interfaceDefinition']['syntaxVersion'] +    if component: +        parsed = parse(component) +    else: +        parsed = None      version = {} -    for i in converted: -        tmp = {i['@component']: i['@version']} -        version |= tmp +    # addon package definitions may have empty (== 0) version info +    if parsed is not None and parsed['interfaceDefinition'] is not None: +        converted = parsed['interfaceDefinition']['syntaxVersion'] +        if not isinstance(converted, list): +            converted = [converted] +        for i in converted: +            tmp = {i['@component']: i['@version']} +            version |= tmp      version = {"component_version": version} @@ -90,5 +114,7 @@ def main():      with open(xml_cache, 'w') as f:          f.write(f'reference = {str(d)}') +    print(cache_name) +  if __name__ == '__main__':      main() diff --git a/python/vyos/xml_ref/pkg_cache/__init__.py b/python/vyos/xml_ref/pkg_cache/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python/vyos/xml_ref/pkg_cache/__init__.py diff --git a/python/vyos/xml_ref/update_cache.py b/python/vyos/xml_ref/update_cache.py new file mode 100755 index 000000000..0842bcbe9 --- /dev/null +++ b/python/vyos/xml_ref/update_cache.py @@ -0,0 +1,51 @@ +#!/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 os +from copy import deepcopy +from generate_cache import pkg_cache +from generate_cache import ref_cache + +def dict_merge(source, destination): +    dest = deepcopy(destination) + +    for key, value in source.items(): +        if key not in dest: +            dest[key] = value +        elif isinstance(source[key], dict): +            dest[key] = dict_merge(source[key], dest[key]) + +    return dest + +def main(): +    res = {} +    cache_dir = os.path.basename(pkg_cache) +    for mod in os.listdir(pkg_cache): +        mod = os.path.splitext(mod)[0] +        if not mod.endswith('_cache'): +            continue +        d = getattr(__import__(f'{cache_dir}.{mod}', fromlist=[mod]), 'reference') +        if mod == 'vyos_1x_cache': +            res = dict_merge(res, d) +        else: +            res = dict_merge(d, res) + +    with open(ref_cache, 'w') as f: +        f.write(f'reference = {str(res)}') + +if __name__ == '__main__': +    main() diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index b4afac0e2..a3868fa70 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -791,6 +791,7 @@ class BasicInterfaceTest:                  # Enable DHCPv6 client                  self.cli_set(path + ['address', 'dhcpv6']) +                self.cli_set(path + ['dhcpv6-options', 'no-release'])                  self.cli_set(path + ['dhcpv6-options', 'rapid-commit'])                  self.cli_set(path + ['dhcpv6-options', 'parameters-only'])                  self.cli_set(path + ['dhcpv6-options', 'duid', duid]) @@ -798,6 +799,9 @@ class BasicInterfaceTest:              self.cli_commit() +            dhcp6c_options = read_file(f'/run/dhcp6c/dhcp6c.{interface}.options') +            self.assertIn(f'-n', dhcp6c_options) +              duid_base = 10              for interface in self._interfaces:                  duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base) diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py index f0c4fef2e..e6eaedeff 100755 --- a/smoketest/scripts/cli/test_nat.py +++ b/smoketest/scripts/cli/test_nat.py @@ -288,6 +288,5 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):          self.verify_nftables(nftables_search, 'ip vyos_nat') -  if __name__ == '__main__':      unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 977376bdd..80befbfd6 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -479,5 +479,32 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):              self.assertIn(f' ip ospf dead-interval 40', config)              self.assertIn(f' no ip ospf mpls ldp-sync', config) +    def test_ospf_16_graceful_restart(self): +        period = '300' +        supported_grace_time = '400' +        router_ids = ['192.0.2.1', '192.0.2.2'] + +        self.cli_set(base_path + ['capability', 'opaque']) +        self.cli_set(base_path + ['graceful-restart', 'grace-period', period]) +        self.cli_set(base_path + ['graceful-restart', 'helper', 'planned-only']) +        self.cli_set(base_path + ['graceful-restart', 'helper', 'no-strict-lsa-checking']) +        self.cli_set(base_path + ['graceful-restart', 'helper', 'supported-grace-time', supported_grace_time]) +        for router_id in router_ids: +            self.cli_set(base_path + ['graceful-restart', 'helper', 'enable', 'router-id', router_id]) + +        # commit changes +        self.cli_commit() + +        # Verify FRR ospfd configuration +        frrconfig = self.getFRRconfig('router ospf') +        self.assertIn(f'router ospf', frrconfig) +        self.assertIn(f' capability opaque', frrconfig) +        self.assertIn(f' graceful-restart grace-period {period}', frrconfig) +        self.assertIn(f' graceful-restart helper planned-only', frrconfig) +        self.assertIn(f' no graceful-restart helper strict-lsa-checking', frrconfig) +        self.assertIn(f' graceful-restart helper supported-grace-time {supported_grace_time}', frrconfig) +        for router_id in router_ids: +            self.assertIn(f' graceful-restart helper enable {router_id}', frrconfig) +  if __name__ == '__main__':      unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py index b67bfaac7..64dfa18db 100755 --- a/smoketest/scripts/cli/test_protocols_ospfv3.py +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -281,5 +281,31 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):          self.cli_delete(['vrf', 'name', vrf])          self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) + +    def test_ospfv3_09_graceful_restart(self): +        period = '300' +        supported_grace_time = '400' +        router_ids = ['192.0.2.1', '192.0.2.2'] + +        self.cli_set(base_path + ['graceful-restart', 'grace-period', period]) +        self.cli_set(base_path + ['graceful-restart', 'helper', 'planned-only']) +        self.cli_set(base_path + ['graceful-restart', 'helper', 'lsa-check-disable']) +        self.cli_set(base_path + ['graceful-restart', 'helper', 'supported-grace-time', supported_grace_time]) +        for router_id in router_ids: +            self.cli_set(base_path + ['graceful-restart', 'helper', 'enable', 'router-id', router_id]) + +        # commit changes +        self.cli_commit() + +        # Verify FRR ospfd configuration +        frrconfig = self.getFRRconfig('router ospf6') +        self.assertIn(f'router ospf6', frrconfig) +        self.assertIn(f' graceful-restart grace-period {period}', frrconfig) +        self.assertIn(f' graceful-restart helper planned-only', frrconfig) +        self.assertIn(f' graceful-restart helper lsa-check-disable', frrconfig) +        self.assertIn(f' graceful-restart helper supported-grace-time {supported_grace_time}', frrconfig) +        for router_id in router_ids: +            self.assertIn(f' graceful-restart helper enable {router_id}', frrconfig) +  if __name__ == '__main__':      unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_ntp.py b/smoketest/scripts/cli/test_service_ntp.py index 9a63fbbd1..5e385d5ad 100755 --- a/smoketest/scripts/cli/test_service_ntp.py +++ b/smoketest/scripts/cli/test_service_ntp.py @@ -108,7 +108,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):              self.assertIn(f'bindaddress {listen}', config)      def test_03_ntp_interface(self): -        interfaces = ['eth0', 'eth1'] +        interfaces = ['eth0']          for interface in interfaces:              self.cli_set(base_path + ['interface', interface]) diff --git a/smoketest/scripts/system/test_module_load.py b/smoketest/scripts/system/test_module_load.py index 69365761f..9d94f01e6 100755 --- a/smoketest/scripts/system/test_module_load.py +++ b/smoketest/scripts/system/test_module_load.py @@ -23,7 +23,8 @@ modules = {      "intel_qat": ["qat_200xx", "qat_200xxvf", "qat_c3xxx", "qat_c3xxxvf",                    "qat_c62x", "qat_c62xvf", "qat_d15xx", "qat_d15xxvf",                    "qat_dh895xcc", "qat_dh895xccvf"], -    "accel_ppp": ["ipoe", "vlan_mon"] +    "accel_ppp": ["ipoe", "vlan_mon"], +    "openvpn": ["ovpn-dco-v2"]  }  class TestKernelModules(unittest.TestCase): diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 607a19385..3bef9b8f6 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -56,6 +56,8 @@ from vyos.utils.list import is_list_equal  from vyos.utils.file import makedir  from vyos.utils.file import read_file  from vyos.utils.file import write_file +from vyos.utils.kernel import check_kmod +from vyos.utils.kernel import unload_kmod  from vyos.utils.process import call  from vyos.utils.permission import chown  from vyos.utils.process import cmd @@ -86,30 +88,45 @@ def get_config(config=None):          conf = Config()      base = ['interfaces', 'openvpn'] -    tmp_pki = conf.get_config_dict(['pki'], key_mangling=('-', '_'), -                                get_first_key=True, no_tag_node_value_mangle=True) -      ifname, openvpn = get_interface_dict(conf, base) - -    if 'deleted' not in openvpn: -        openvpn['pki'] = tmp_pki -        if is_node_changed(conf, base + [ifname, 'openvpn-option']): -            openvpn.update({'restart_required': {}}) - -        # We have to get the dict using 'get_config_dict' instead of 'get_interface_dict' -        # as 'get_interface_dict' merges the defaults in, so we can not check for defaults in there. -        tmp = conf.get_config_dict(base + [openvpn['ifname']], get_first_key=True) - -        # We have to cleanup the config dict, as default values could enable features -        # which are not explicitly enabled on the CLI. Example: server mfa totp -        # originate comes with defaults, which will enable the -        # totp plugin, even when not set via CLI so we -        # need to check this first and drop those keys -        if dict_search('server.mfa.totp', tmp) == None: -            del openvpn['server']['mfa'] -      openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn) +    if 'deleted' in openvpn: +        return openvpn + +    openvpn['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), +                                        get_first_key=True, +                                        no_tag_node_value_mangle=True) + +    if is_node_changed(conf, base + [ifname, 'openvpn-option']): +        openvpn.update({'restart_required': {}}) +    if is_node_changed(conf, base + [ifname, 'enable-dco']): +        openvpn.update({'restart_required': {}}) + +    # We have to get the dict using 'get_config_dict' instead of 'get_interface_dict' +    # as 'get_interface_dict' merges the defaults in, so we can not check for defaults in there. +    tmp = conf.get_config_dict(base + [openvpn['ifname']], get_first_key=True) + +    # We have to cleanup the config dict, as default values could enable features +    # which are not explicitly enabled on the CLI. Example: server mfa totp +    # originate comes with defaults, which will enable the +    # totp plugin, even when not set via CLI so we +    # need to check this first and drop those keys +    if dict_search('server.mfa.totp', tmp) == None: +        del openvpn['server']['mfa'] + +    # OpenVPN Data-Channel-Offload (DCO) is a Kernel module. If loaded it applies to all +    # OpenVPN interfaces. Check if DCO is used by any other interface instance. +    tmp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) +    for interface, interface_config in tmp.items(): +        # If one interface has DCO configured, enable it. No need to further check +        # all other OpenVPN interfaces. We must use a dedicated key to indicate +        # the Kernel module must be loaded or not. The per interface "offload.dco" +        # key is required per OpenVPN interface instance. +        if dict_search('offload.dco', interface_config) != None: +            openvpn['module_load_dco'] = {} +            break +      return openvpn  def is_ec_private_key(pki, cert_name): @@ -670,6 +687,15 @@ def apply(openvpn):          if interface in interfaces():              VTunIf(interface).remove() +    # dynamically load/unload DCO Kernel extension if requested +    dco_module = 'ovpn_dco_v2' +    if 'module_load_dco' in openvpn: +        check_kmod(dco_module) +    else: +        unload_kmod(dco_module) + +    # Now bail out early if interface is disabled or got deleted +    if 'deleted' in openvpn or 'disable' in openvpn:          return None      # verify specified IP address is present on any interface on this system diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py index 6658ca86a..2515dc838 100755 --- a/src/conf_mode/interfaces-wwan.py +++ b/src/conf_mode/interfaces-wwan.py @@ -75,7 +75,6 @@ def get_config(config=None):      # We need to know the amount of other WWAN interfaces as ModemManager needs      # to be started or stopped. -    conf.set_level(base)      wwan['other_interfaces'] = conf.get_config_dict([], key_mangling=('-', '_'),                                                         get_first_key=True,                                                         no_tag_node_value_mangle=True) diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index fa6fe9bb6..5af06ed16 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -72,6 +72,7 @@ def verify_rule(config, err_msg, groups_dict):      """ Common verify steps used for both source and destination NAT """      if (dict_search('translation.port', config) != None or +        dict_search('translation.redirect.port', config) != None or          dict_search('destination.port', config) != None or          dict_search('source.port', config)): diff --git a/src/conf_mode/ntp.py b/src/conf_mode/ntp.py index 917f6e058..1cc23a7df 100755 --- a/src/conf_mode/ntp.py +++ b/src/conf_mode/ntp.py @@ -24,6 +24,7 @@ from vyos.utils.process import call  from vyos.utils.permission import chmod_750  from vyos.utils.network import get_interface_config  from vyos.template import render +from vyos.template import is_ipv4  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -62,16 +63,29 @@ def verify(ntp):      if 'interface' in ntp:          # If ntpd should listen on a given interface, ensure it exists -        for interface in ntp['interface']: -            verify_interface_exists(interface) - -            # If we run in a VRF, our interface must belong to this VRF, too -            if 'vrf' in ntp: -                tmp = get_interface_config(interface) -                vrf_name = ntp['vrf'] -                if 'master' not in tmp or tmp['master'] != vrf_name: -                    raise ConfigError(f'NTP runs in VRF "{vrf_name}" - "{interface}" '\ -                                      f'does not belong to this VRF!') +        interface = ntp['interface'] +        verify_interface_exists(interface) + +        # If we run in a VRF, our interface must belong to this VRF, too +        if 'vrf' in ntp: +            tmp = get_interface_config(interface) +            vrf_name = ntp['vrf'] +            if 'master' not in tmp or tmp['master'] != vrf_name: +                raise ConfigError(f'NTP runs in VRF "{vrf_name}" - "{interface}" '\ +                                  f'does not belong to this VRF!') + +    if 'listen_address' in ntp: +        ipv4_addresses = 0 +        ipv6_addresses = 0 +        for address in ntp['listen_address']: +            if is_ipv4(address): +                ipv4_addresses += 1 +            else: +                ipv6_addresses += 1 +        if ipv4_addresses > 1: +            raise ConfigError(f'NTP Only admits one ipv4 value for listen-address parameter ') +        if ipv6_addresses > 1: +            raise ConfigError(f'NTP Only admits one ipv6 value for listen-address parameter ')      return None diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index cec025fea..7b9f15505 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -475,6 +475,8 @@ def verify(bgp):                      if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']):                          raise ConfigError(                              'Command "import vrf" conflicts with "rd vpn export" command!') +                    if not dict_search('parameters.router_id', bgp): +                        Warning(f'BGP "router-id" is required when using "rd" and "route-target"!')                  if dict_search('route_target.vpn.both', afi_config):                      if verify_vrf_as_import(vrf_name, afi, bgp['dependent_vrfs']): diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 509d4f501..f2075d25b 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -88,6 +88,8 @@ def get_config(config=None):          del default_values['area']['area_type']['nssa']      if 'mpls_te' not in ospf:          del default_values['mpls_te'] +    if 'graceful_restart' not in ospf: +        del default_values['graceful_restart']      for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static', 'table']:          # table is a tagNode thus we need to clean out all occurances for the diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index 7f50d8624..fbea51f56 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -83,6 +83,8 @@ def get_config(config=None):      # need to check this first and probably drop that key.      if dict_search('default_information.originate', ospfv3) is None:          del default_values['default_information'] +    if 'graceful_restart' not in ospfv3: +        del default_values['graceful_restart']      # XXX: T2665: we currently have no nice way for defaults under tag nodes,      # clean them out and add them manually :( diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py index 7b6150696..5def8d645 100755 --- a/src/conf_mode/protocols_static.py +++ b/src/conf_mode/protocols_static.py @@ -47,7 +47,7 @@ def get_config(config=None):      base_path = ['protocols', 'static']      # eqivalent of the C foo ? 'a' : 'b' statement      base = vrf and ['vrf', 'name', vrf, 'protocols', 'static'] or base_path -    static = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) +    static = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)      # Assign the name of our VRF context      if vrf: static['vrf'] = vrf diff --git a/src/conf_mode/service_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py index 2e678cf0b..f6b80552b 100755 --- a/src/conf_mode/service_ids_fastnetmon.py +++ b/src/conf_mode/service_ids_fastnetmon.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2018-2022 VyOS maintainers and contributors +# Copyright (C) 2018-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 @@ -30,6 +30,7 @@ airbag.enable()  config_file = r'/run/fastnetmon/fastnetmon.conf'  networks_list = r'/run/fastnetmon/networks_list'  excluded_networks_list = r'/run/fastnetmon/excluded_networks_list' +attack_dir = '/var/log/fastnetmon_attacks'  def get_config(config=None):      if config: @@ -55,8 +56,11 @@ def verify(fastnetmon):      if 'mode' not in fastnetmon:          raise ConfigError('Specify operating mode!') -    if 'listen_interface' not in fastnetmon: -        raise ConfigError('Specify interface(s) for traffic capture') +    if fastnetmon.get('mode') == 'mirror' and 'listen_interface' not in fastnetmon: +        raise ConfigError("Incorrect settings for 'mode mirror': must specify interface(s) for traffic mirroring") + +    if fastnetmon.get('mode') == 'sflow' and 'listen_address' not in fastnetmon.get('sflow', {}): +        raise ConfigError("Incorrect settings for 'mode sflow': must specify sFlow 'listen-address'")      if 'alert_script' in fastnetmon:          if os.path.isfile(fastnetmon['alert_script']): @@ -74,6 +78,10 @@ def generate(fastnetmon):          return None +    # Create dir for log attack details +    if not os.path.exists(attack_dir): +        os.mkdir(attack_dir) +      render(config_file, 'ids/fastnetmon.j2', fastnetmon)      render(networks_list, 'ids/fastnetmon_networks_list.j2', fastnetmon)      render(excluded_networks_list, 'ids/fastnetmon_excluded_networks_list.j2', fastnetmon) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 273475c18..82941e0c0 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -54,7 +54,7 @@ MAX_USER_UID: int = 59999  # LOGIN_TIMEOUT from /etc/loign.defs minus 10 sec  MAX_RADIUS_TIMEOUT: int = 50  # MAX_RADIUS_TIMEOUT divided by 2 sec (minimum recomended timeout) -MAX_RADIUS_COUNT: int = 25 +MAX_RADIUS_COUNT: int = 8  # Maximum number of supported TACACS servers  MAX_TACACS_COUNT: int = 8 @@ -389,7 +389,7 @@ def apply(login):                  # command until user is removed - userdel might return 8 as                  # SSH sessions are not all yet properly cleaned away, thus we                  # simply re-run the command until the account wen't away -                while run(f'userdel --remove {user}', stderr=DEVNULL): +                while run(f'userdel {user}', stderr=DEVNULL):                      sleep(0.250)              except Exception as e: diff --git a/src/etc/modprobe.d/openvpn.conf b/src/etc/modprobe.d/openvpn.conf new file mode 100644 index 000000000..a9259fea2 --- /dev/null +++ b/src/etc/modprobe.d/openvpn.conf @@ -0,0 +1 @@ +blacklist ovpn-dco-v2 diff --git a/src/migration-scripts/container/0-to-1 b/src/migration-scripts/container/0-to-1 index 9fcf295e8..86f89ee04 100755 --- a/src/migration-scripts/container/0-to-1 +++ b/src/migration-scripts/container/0-to-1 @@ -39,12 +39,12 @@ config = ConfigTree(config_file)  if config.exists(base):      for container in config.list_nodes(base):          # Stop any given container first -        call(f'systemctl stop vyos-container-{container}.service') +        call(f'sudo systemctl stop vyos-container-{container}.service')          # Export container image for later re-import to new filesystem. We store          # the backup on a real disk as a tmpfs (like /tmp) could probably lack          # memory if a host has too many containers stored.          image_name = config.return_value(base + [container, 'image']) -        call(f'podman image save --quiet --output /root/{container}.tar --format oci-archive {image_name}') +        call(f'sudo podman image save --quiet --output /root/{container}.tar --format oci-archive {image_name}')  # No need to adjust the strage driver online (this is only used for testing and  # debugging on a live system) - it is already overlay2 when the migration script @@ -66,10 +66,10 @@ if config.exists(base):          # Export container image for later re-import to new filesystem          image_name = config.return_value(base + [container, 'image'])          image_path = f'/root/{container}.tar' -        call(f'podman image load --quiet --input {image_path}') +        call(f'sudo podman image load --quiet --input {image_path}')          # Start any given container first -        call(f'systemctl start vyos-container-{container}.service') +        call(f'sudo systemctl start vyos-container-{container}.service')          # Delete temporary container image          if os.path.exists(image_path): diff --git a/src/migration-scripts/ntp/2-to-3 b/src/migration-scripts/ntp/2-to-3 new file mode 100755 index 000000000..7d4e0bd83 --- /dev/null +++ b/src/migration-scripts/ntp/2-to-3 @@ -0,0 +1,62 @@ +#!/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/>. + +# T5154: allow only one ip address per family for parameter 'listen-address' +# Allow only one interface for parameter 'interface' +# If more than one are specified, remove such entries + +import sys + +from vyos.configtree import ConfigTree +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 + +if (len(sys.argv) < 1): +    print("Must specify file name!") +    sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +config = ConfigTree(config_file) + +base_path = ['service', 'ntp'] +if not config.exists(base_path): +    # Nothing to do +    sys.exit(0) + +if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv4(addr)]) > 1): +    for addr in config.return_values(base_path + ['listen-address']): +        if is_ipv4(addr): +            config.delete_value(base_path + ['listen-address'], addr) + +if config.exists(base_path + ['listen-address']) and (len([addr for addr in config.return_values(base_path + ['listen-address']) if is_ipv6(addr)]) > 1): +    for addr in config.return_values(base_path + ['listen-address']): +        if is_ipv6(addr): +            config.delete_value(base_path + ['listen-address'], addr) + +if config.exists(base_path + ['interface']): +    if len(config.return_values(base_path + ['interface'])) > 1: +        config.delete(base_path + ['interface']) + +try: +    with open(file_name, 'w') as f: +        f.write(config.to_string()) +except OSError as e: +    print("Failed to save the modified config: {}".format(e)) +    sys.exit(1) diff --git a/src/op_mode/bridge.py b/src/op_mode/bridge.py index 5531c41d0..1834b9cc9 100755 --- a/src/op_mode/bridge.py +++ b/src/op_mode/bridge.py @@ -24,6 +24,7 @@ from tabulate import tabulate  from vyos.utils.process import cmd  from vyos.utils.process import rc_cmd +from vyos.utils.process	import call  from vyos.utils.dict import dict_search  import vyos.opmode @@ -129,7 +130,8 @@ def _get_formatted_output_vlan(data):              if vlan_entry.get('vlanEnd'):                  vlan_end = vlan_entry.get('vlanEnd')                  vlan = f'{vlan}-{vlan_end}' -            flags = ', '.join(vlan_entry.get('flags')).lower() +            flags_raw = vlan_entry.get('flags') +            flags = ', '.join(flags_raw if isinstance(flags_raw,list) else "").lower()              data_entries.append([interface, vlan, flags])      headers = ["Interface", "Vlan", "Flags"] @@ -164,6 +166,23 @@ def _get_formatted_output_mdb(data):      output = tabulate(data_entries, headers)      return output +def _get_bridge_detail(iface): +    """Get interface detail statistics""" +    return call(f'vtysh -c "show interface {iface}"') + +def _get_bridge_detail_nexthop_group(iface): +    """Get interface detail nexthop_group statistics""" +    return call(f'vtysh -c "show interface {iface} nexthop-group"') + +def _get_bridge_detail_nexthop_group_raw(iface): +    out = cmd(f'vtysh -c "show interface {iface} nexthop-group"') +    return out + +def _get_bridge_detail_raw(iface): +    """Get interface detail json statistics""" +    data =  cmd(f'vtysh -c "show interface {iface} json"') +    data_dict = json.loads(data) +    return data_dict  def show(raw: bool):      bridge_data = _get_raw_data_summary() @@ -196,6 +215,17 @@ def show_mdb(raw: bool, interface: str):      else:          return _get_formatted_output_mdb(mdb_data) +def show_detail(raw: bool, nexthop_group: typing.Optional[bool], interface: str): +    if raw: +        if nexthop_group: +            return _get_bridge_detail_nexthop_group_raw(interface) +        else: +            return _get_bridge_detail_raw(interface) +    else: +        if nexthop_group: +            return _get_bridge_detail_nexthop_group(interface) +        else: +            return _get_bridge_detail(interface)  if __name__ == '__main__':      try: diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index 3e51e990b..20ef698bd 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -14,10 +14,12 @@  # 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 sys  import typing  from datetime import datetime +from glob import glob  from ipaddress import ip_address  from isc_dhcp_leases import IscDhcpLeases  from tabulate import tabulate @@ -27,10 +29,13 @@ import vyos.opmode  from vyos.base import Warning  from vyos.configquery import ConfigTreeQuery -from vyos.utils.process import cmd  from vyos.utils.dict import dict_search +from vyos.utils.file import read_file +from vyos.utils.process import cmd  from vyos.utils.process import is_systemd_service_running +time_string = "%a %b %d %H:%M:%S %Z %Y" +  config = ConfigTreeQuery()  lease_valid_states = ['all', 'active', 'free', 'expired', 'released', 'abandoned', 'reset', 'backup']  sort_valid_inet = ['end', 'mac', 'hostname', 'ip', 'pool', 'remaining', 'start', 'state'] @@ -287,6 +292,87 @@ def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str],          return _get_formatted_server_leases(lease_data, family=family) +def _get_raw_client_leases(family='inet', interface=None): +    from time import mktime +    from datetime import datetime + +    lease_dir = '/var/lib/dhcp' +    lease_files = [] +    lease_data = [] + +    if interface: +        tmp = f'{lease_dir}/dhclient_{interface}.lease' +        if os.path.exists(tmp): +            lease_files.append(tmp) +    else: +        # All DHCP leases +        lease_files = glob(f'{lease_dir}/dhclient_*.lease') + +    for lease in lease_files: +        tmp = {} +        with open(lease, 'r') as f: +            for line in f.readlines(): +                line = line.rstrip() +                if 'last_update' not in tmp: +                    # ISC dhcp client contains least_update timestamp in human readable +                    # format this makes less sense for an API and also the expiry +                    # timestamp is provided in UNIX time. Convert string (e.g. Sun Jul +                    # 30 18:13:44 CEST 2023) to UNIX time (1690733624) +                    tmp.update({'last_update' : int(mktime(datetime.strptime(line, time_string).timetuple()))}) +                    continue + +                k, v = line.split('=') +                tmp.update({k : v.replace("'", "")}) + +        lease_data.append(tmp) + +    return lease_data + +def _get_formatted_client_leases(lease_data, family): +    from time import localtime +    from time import strftime + +    from vyos.validate import is_intf_addr_assigned + +    data_entries = [] +    for lease in lease_data: +        data_entries.append(["Interface", lease['interface']]) +        if 'new_ip_address' in lease: +            tmp = '[Active]' if is_intf_addr_assigned(lease['interface'], lease['new_ip_address']) else '[Inactive]' +            data_entries.append(["IP address", lease['new_ip_address'], tmp]) +        if 'new_subnet_mask' in lease: +            data_entries.append(["Subnet Mask", lease['new_subnet_mask']]) +        if 'new_domain_name' in lease: +            data_entries.append(["Domain Name", lease['new_domain_name']]) +        if 'new_routers' in lease: +            data_entries.append(["Router", lease['new_routers']]) +        if 'new_domain_name_servers' in lease: +            data_entries.append(["Name Server", lease['new_domain_name_servers']]) +        if 'new_dhcp_server_identifier' in lease: +            data_entries.append(["DHCP Server", lease['new_dhcp_server_identifier']]) +        if 'new_dhcp_lease_time' in lease: +            data_entries.append(["DHCP Server", lease['new_dhcp_lease_time']]) +        if 'last_update' in lease: +            tmp = strftime(time_string, localtime(int(lease['last_update']))) +            data_entries.append(["Last Update", tmp]) +        if 'new_expiry' in lease: +            tmp = strftime(time_string, localtime(int(lease['new_expiry']))) +            data_entries.append(["Expiry", tmp]) + +        # Add empty marker +        data_entries.append(['']) + +    output = tabulate(data_entries, tablefmt='plain') + +    return output + +def show_client_leases(raw: bool, family: ArgFamily, interface: typing.Optional[str]): +    lease_data = _get_raw_client_leases(family=family, interface=interface) +    if raw: +        return lease_data +    else: +        return _get_formatted_client_leases(lease_data, family=family) +  if __name__ == '__main__':      try:          res = vyos.opmode.run(sys.modules[__name__]) diff --git a/src/op_mode/show_ntp.sh b/src/op_mode/show_ntp.sh index 85f8eda15..4b59b801e 100755 --- a/src/op_mode/show_ntp.sh +++ b/src/op_mode/show_ntp.sh @@ -18,7 +18,7 @@ if ! ps -C chronyd &>/dev/null; then  fi  PID=$(pgrep chronyd | head -n1) -VRF_NAME=$(ip vrf identify ) +VRF_NAME=$(ip vrf identify ${PID})  if [ ! -z ${VRF_NAME} ]; then      VRF_CMD="sudo ip vrf exec ${VRF_NAME}" diff --git a/src/op_mode/webproxy_update_blacklist.sh b/src/op_mode/webproxy_update_blacklist.sh index 4fb9a54c6..05ea86f9e 100755 --- a/src/op_mode/webproxy_update_blacklist.sh +++ b/src/op_mode/webproxy_update_blacklist.sh @@ -45,6 +45,9 @@ do      --auto-update-blacklist)          auto="yes"          ;; +    --vrf) +        vrf="yes" +        ;;      (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;      (*) break;;      esac @@ -76,7 +79,11 @@ fi  if [[ -n $update ]] && [[ $update -eq "yes" ]]; then      tmp_blacklists='/tmp/blacklists.gz' -    curl -o $tmp_blacklists $blacklist_url +    if [[ -n $vrf ]] && [[ $vrf -eq "yes" ]]; then +        sudo ip vrf exec $1 curl -o $tmp_blacklists $blacklist_url +    else +        curl -o $tmp_blacklists $blacklist_url +    fi      if [ $? -ne 0 ]; then          echo "Unable to download [$blacklist_url]!"          exit 1 diff --git a/src/systemd/dhcp6c@.service b/src/systemd/dhcp6c@.service index 9a97ee261..495cb7e26 100644 --- a/src/systemd/dhcp6c@.service +++ b/src/systemd/dhcp6c@.service @@ -2,14 +2,16 @@  Description=WIDE DHCPv6 client on %i  Documentation=man:dhcp6c(8) man:dhcp6c.conf(5)  ConditionPathExists=/run/dhcp6c/dhcp6c.%i.conf +ConditionPathExists=/run/dhcp6c/dhcp6c.%i.options  After=vyos-router.service  StartLimitIntervalSec=0  [Service]  WorkingDirectory=/run/dhcp6c +EnvironmentFile=-/run/dhcp6c/dhcp6c.%i.options  Type=forking  PIDFile=/run/dhcp6c/dhcp6c.%i.pid -ExecStart=/usr/sbin/dhcp6c -D -k /run/dhcp6c/dhcp6c.%i.sock -c /run/dhcp6c/dhcp6c.%i.conf -p /run/dhcp6c/dhcp6c.%i.pid %i +ExecStart=/usr/sbin/dhcp6c $DHCP6C_OPTS  Restart=on-failure  RestartSec=20 | 
