summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile8
-rw-r--r--data/templates/chrony/chrony.conf.j24
-rw-r--r--data/templates/dhcp-client/dhcp6c_daemon-options.j22
-rw-r--r--data/templates/frr/ospf6d.frr.j221
-rw-r--r--data/templates/frr/ospfd.frr.j224
-rw-r--r--data/templates/ids/fastnetmon.j211
-rw-r--r--data/templates/ids/fastnetmon_networks_list.j22
-rw-r--r--data/templates/openvpn/server.conf.j23
-rw-r--r--debian/control11
-rwxr-xr-xdebian/rules3
-rw-r--r--debian/vyos-1x.postinst3
-rw-r--r--interface-definitions/include/interface/dhcpv6-options.xml.i6
-rw-r--r--interface-definitions/include/ospf/graceful-restart.xml.i67
-rw-r--r--interface-definitions/include/ospf/protocol-common-config.xml.i30
-rw-r--r--interface-definitions/include/ospfv3/protocol-common-config.xml.i15
-rw-r--r--interface-definitions/include/version/ntp-version.xml.i2
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in13
-rw-r--r--interface-definitions/nat.xml.in8
-rw-r--r--interface-definitions/ntp.xml.in2
-rw-r--r--interface-definitions/service-ids-ddos-protection.xml.in33
-rw-r--r--op-mode-definitions/dhcp.xml.in24
-rw-r--r--op-mode-definitions/include/monitor-background.xml.i21
-rw-r--r--op-mode-definitions/include/ospf/common.xml.i (renamed from op-mode-definitions/include/ospf-common.xml.i)2
-rw-r--r--op-mode-definitions/include/ospf/graceful-restart.xml.i13
-rw-r--r--op-mode-definitions/monitor-bridge.xml.in4
-rw-r--r--op-mode-definitions/monitor-log.xml.in113
-rw-r--r--op-mode-definitions/monitor-ndp.xml.in8
-rw-r--r--op-mode-definitions/monitor-protocol.xml.in42
-rw-r--r--op-mode-definitions/show-bridge.xml.in12
-rw-r--r--op-mode-definitions/show-ip-ospf.xml.in4
-rw-r--r--op-mode-definitions/show-ipv6-ospfv3.xml.in2
-rw-r--r--op-mode-definitions/show-log.xml.in102
-rw-r--r--op-mode-definitions/webproxy.xml.in50
-rw-r--r--python/vyos/configtree.py10
-rw-r--r--python/vyos/firewall.py2
-rw-r--r--python/vyos/ifconfig/interface.py2
-rw-r--r--python/vyos/nat.py42
-rw-r--r--python/vyos/remote.py72
-rw-r--r--python/vyos/utils/kernel.py11
-rwxr-xr-xpython/vyos/xml_ref/generate_cache.py68
-rw-r--r--python/vyos/xml_ref/pkg_cache/__init__.py0
-rwxr-xr-xpython/vyos/xml_ref/update_cache.py51
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py4
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py1
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py27
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py26
-rwxr-xr-xsmoketest/scripts/cli/test_service_ntp.py2
-rwxr-xr-xsmoketest/scripts/system/test_module_load.py3
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py68
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py1
-rwxr-xr-xsrc/conf_mode/nat.py1
-rwxr-xr-xsrc/conf_mode/ntp.py34
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py2
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py2
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py2
-rwxr-xr-xsrc/conf_mode/protocols_static.py2
-rwxr-xr-xsrc/conf_mode/service_ids_fastnetmon.py14
-rwxr-xr-xsrc/conf_mode/system-login.py4
-rw-r--r--src/etc/modprobe.d/openvpn.conf1
-rwxr-xr-xsrc/migration-scripts/container/0-to-18
-rwxr-xr-xsrc/migration-scripts/ntp/2-to-362
-rwxr-xr-xsrc/op_mode/bridge.py32
-rwxr-xr-xsrc/op_mode/dhcp.py88
-rwxr-xr-xsrc/op_mode/show_ntp.sh2
-rwxr-xr-xsrc/op_mode/webproxy_update_blacklist.sh9
-rw-r--r--src/systemd/dhcp6c@.service4
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
diff --git a/Makefile b/Makefile
index 25f2c96d2..6dfa516e1 100644
--- a/Makefile
+++ b/Makefile
@@ -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