summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.vscode/settings.json7
-rw-r--r--data/templates/high-availability/keepalived.conf.j22
-rw-r--r--data/templates/vpp/sysctl.conf.j215
-rw-r--r--debian/control2
-rwxr-xr-xdebian/rules8
-rw-r--r--debian/vyos-1x.install1
-rw-r--r--debian/vyos-1x.postinst3
-rw-r--r--interface-definitions/high-availability.xml.in3
-rw-r--r--interface-definitions/include/address-ipv4-ipv6-single.xml.i18
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i2
-rw-r--r--interface-definitions/include/version/vrrp-version.xml.i2
-rw-r--r--interface-definitions/protocols-mpls.xml.in2
-rw-r--r--interface-definitions/service-config-sync.xml.in104
-rw-r--r--op-mode-definitions/container.xml.in2
-rw-r--r--op-mode-definitions/dns-dynamic.xml.in10
-rw-r--r--op-mode-definitions/monitor-log.xml.in60
-rw-r--r--op-mode-definitions/pki.xml.in33
-rw-r--r--op-mode-definitions/show-log.xml.in17
-rw-r--r--python/vyos/config_mgmt.py27
-rw-r--r--python/vyos/util.py16
-rw-r--r--python/vyos/utils/network.py8
-rw-r--r--python/vyos/utils/system.py82
-rw-r--r--python/vyos/xml_ref/definition.py7
-rwxr-xr-xsmoketest/scripts/cli/test_ha_virtual_server.py38
-rwxr-xr-xsrc/conf_mode/high-availability.py5
-rwxr-xr-xsrc/conf_mode/service_config_sync.py111
-rwxr-xr-xsrc/conf_mode/snmp.py8
-rwxr-xr-xsrc/conf_mode/vpp.py49
-rwxr-xr-xsrc/etc/systemd/system-generators/vyos-generator94
-rw-r--r--src/etc/systemd/system/getty@.service.d/aftervyos.conf3
-rw-r--r--src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf3
-rwxr-xr-xsrc/helpers/vyos_config_sync.py192
-rwxr-xr-xsrc/init/vyos-config16
-rwxr-xr-xsrc/init/vyos-router421
-rwxr-xr-xsrc/migration-scripts/vrrp/3-to-451
-rwxr-xr-xsrc/op_mode/pki.py29
-rw-r--r--src/systemd/vyos-router.service19
-rw-r--r--src/systemd/vyos.target3
39 files changed, 1358 insertions, 119 deletions
diff --git a/.gitignore b/.gitignore
index fe92f5b9d..e766a2c27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,6 +133,10 @@ debian/*.substvars
*.vpwhist
*.vtg
+# VS Code
+.vscode/*
+!.vscode/settings.json
+
# VIM
*.swp
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..994718d2e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+ "files.trimTrailingWhitespace": true,
+ "editor.tabSize": 4,
+ "editor.insertSpaces": true,
+ "files.insertFinalNewline": true,
+ "files.eol": "\n",
+}
diff --git a/data/templates/high-availability/keepalived.conf.j2 b/data/templates/high-availability/keepalived.conf.j2
index bcd92358f..47408e345 100644
--- a/data/templates/high-availability/keepalived.conf.j2
+++ b/data/templates/high-availability/keepalived.conf.j2
@@ -173,7 +173,7 @@ vrrp_sync_group {{ name }} {
{% for vserver, vserver_config in virtual_server.items() %}
# Vserver {{ vserver }}
{% if vserver_config.port is vyos_defined %}
-virtual_server {{ vserver }} {{ vserver_config.port }} {
+virtual_server {{ vserver_config.address }} {{ vserver_config.port }} {
{% else %}
virtual_server fwmark {{ vserver_config.fwmark }} {
{% endif %}
diff --git a/data/templates/vpp/sysctl.conf.j2 b/data/templates/vpp/sysctl.conf.j2
deleted file mode 100644
index 2207e2e38..000000000
--- a/data/templates/vpp/sysctl.conf.j2
+++ /dev/null
@@ -1,15 +0,0 @@
-# Number of 2MB hugepages desired
-vm.nr_hugepages=1024
-
-# Must be greater than or equal to (2 * vm.nr_hugepages).
-vm.max_map_count=3096
-
-# All groups allowed to access hugepages
-vm.hugetlb_shm_group=0
-
-# Shared Memory Max must be greater or equal to the total size of hugepages.
-# For 2MB pages, TotalHugepageSize = vm.nr_hugepages * 2 * 1024 * 1024
-# If the existing kernel.shmmax setting (cat /proc/sys/kernel/shmmax)
-# is greater than the calculated TotalHugepageSize then set this parameter
-# to current shmmax value.
-kernel.shmmax=2147483648
diff --git a/debian/control b/debian/control
index 40920cadc..8e9aaa702 100644
--- a/debian/control
+++ b/debian/control
@@ -72,6 +72,7 @@ Depends:
iperf,
iperf3,
iproute2 (>= 6.0.0),
+ iptables,
iputils-arping,
isc-dhcp-client,
isc-dhcp-relay,
@@ -92,6 +93,7 @@ Depends:
libstrongswan-standard-plugins (>=5.9),
libvppinfra,
libvyosconfig0,
+ linux-cpupower,
lldpd,
lm-sensors,
lsscsi,
diff --git a/debian/rules b/debian/rules
index e613f1e0a..9ada2bf87 100755
--- a/debian/rules
+++ b/debian/rules
@@ -51,6 +51,10 @@ override_dh_auto_install:
mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/op_mode
cp -r src/op_mode/* $(DIR)/$(VYOS_LIBEXEC_DIR)/op_mode
+ # Install op mode scripts
+ mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/init
+ cp -r src/init/* $(DIR)/$(VYOS_LIBEXEC_DIR)/init
+
# Install validators
mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/validators
cp -r src/validators/* $(DIR)/$(VYOS_LIBEXEC_DIR)/validators
@@ -123,3 +127,7 @@ override_dh_auto_install:
# Install udev script
mkdir -p $(DIR)/usr/lib/udev
cp src/helpers/vyos_net_name $(DIR)/usr/lib/udev
+
+override_dh_installsystemd:
+ dh_installsystemd -pvyos-1x --name vyos-router vyos-router.service
+ dh_installsystemd -pvyos-1x --name vyos vyos.target
diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install
index 07cdf8c74..9e43669be 100644
--- a/debian/vyos-1x.install
+++ b/debian/vyos-1x.install
@@ -30,6 +30,7 @@ usr/bin/vyos-hostsd-client
usr/lib
usr/libexec/vyos/completion
usr/libexec/vyos/conf_mode
+usr/libexec/vyos/init
usr/libexec/vyos/op_mode
usr/libexec/vyos/services
usr/libexec/vyos/system
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 2958afd0a..93e7ced9b 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -186,5 +186,8 @@ systemctl enable vyos-config-cloud-init.service
#
# sudo mv /opt/vyatta/share/vyatta-cfg/vpp /opt/vyatta/share/vyatta-cfg/templates/vpp
if [ -d /opt/vyatta/share/vyatta-cfg/templates/vpp ]; then
+ if [ -d /opt/vyatta/share/vyatta-cfg/vpp ]; then
+ rm -rf /opt/vyatta/share/vyatta-cfg/vpp
+ fi
mv /opt/vyatta/share/vyatta-cfg/templates/vpp /opt/vyatta/share/vyatta-cfg/vpp
fi
diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in
index d1bbcc365..4f55916fa 100644
--- a/interface-definitions/high-availability.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -336,9 +336,10 @@
</node>
<tagNode name="virtual-server">
<properties>
- <help>Load-balancing virtual server address</help>
+ <help>Load-balancing virtual server alias</help>
</properties>
<children>
+ #include <include/address-ipv4-ipv6-single.xml.i>
<leafNode name="algorithm">
<properties>
<help>Schedule algorithm (default - least-connection)</help>
diff --git a/interface-definitions/include/address-ipv4-ipv6-single.xml.i b/interface-definitions/include/address-ipv4-ipv6-single.xml.i
new file mode 100644
index 000000000..dc3d6fc1b
--- /dev/null
+++ b/interface-definitions/include/address-ipv4-ipv6-single.xml.i
@@ -0,0 +1,18 @@
+<!-- include start from interface/address-ipv4-ipv6.xml.i -->
+<leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index f95031a75..07b6868ee 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1575,7 +1575,9 @@
</properties>
<children>
#include <include/bgp/neighbor-afi-ipv4-unicast.xml.i>
+ #include <include/bgp/neighbor-afi-ipv4-vpn.xml.i>
#include <include/bgp/neighbor-afi-ipv6-unicast.xml.i>
+ #include <include/bgp/neighbor-afi-ipv6-vpn.xml.i>
#include <include/bgp/neighbor-afi-l2vpn-evpn.xml.i>
</children>
</node>
diff --git a/interface-definitions/include/version/vrrp-version.xml.i b/interface-definitions/include/version/vrrp-version.xml.i
index 626dd6cbc..1514b19ab 100644
--- a/interface-definitions/include/version/vrrp-version.xml.i
+++ b/interface-definitions/include/version/vrrp-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/vrrp-version.xml.i -->
-<syntaxVersion component='vrrp' version='3'></syntaxVersion>
+<syntaxVersion component='vrrp' version='4'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/protocols-mpls.xml.in b/interface-definitions/protocols-mpls.xml.in
index 43ca659e9..831601fc6 100644
--- a/interface-definitions/protocols-mpls.xml.in
+++ b/interface-definitions/protocols-mpls.xml.in
@@ -6,7 +6,7 @@
<node name="mpls" owner="${vyos_conf_scripts_dir}/protocols_mpls.py">
<properties>
<help>Multiprotocol Label Switching (MPLS)</help>
- <priority>400</priority>
+ <priority>490</priority>
</properties>
<children>
<node name="ldp">
diff --git a/interface-definitions/service-config-sync.xml.in b/interface-definitions/service-config-sync.xml.in
new file mode 100644
index 000000000..e804e17f7
--- /dev/null
+++ b/interface-definitions/service-config-sync.xml.in
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="config-sync" owner="${vyos_conf_scripts_dir}/service_config_sync.py">
+ <properties>
+ <help>Configuration synchronization</help>
+ </properties>
+ <children>
+ <node name="secondary">
+ <properties>
+ <help>Secondary server parameters</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hostname</format>
+ <description>FQDN address to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ <validator name="fqdn"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="timeout">
+ <properties>
+ <help>Connection API timeout</help>
+ <valueHelp>
+ <format>u32:1-300</format>
+ <description>Connection API timeout</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-300"/>
+ </constraint>
+ </properties>
+ <defaultValue>60</defaultValue>
+ </leafNode>
+ <leafNode name="key">
+ <properties>
+ <help>HTTP API key</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="mode">
+ <properties>
+ <help>Synchronization mode</help>
+ <completionHelp>
+ <list>load set</list>
+ </completionHelp>
+ <valueHelp>
+ <format>load</format>
+ <description>Load and replace configuration section</description>
+ </valueHelp>
+ <valueHelp>
+ <format>set</format>
+ <description>Set configuration section</description>
+ </valueHelp>
+ <constraint>
+ <regex>(load|set)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="section">
+ <properties>
+ <help>Section for synchronization</help>
+ <completionHelp>
+ <list>nat nat66 firewall</list>
+ </completionHelp>
+ <valueHelp>
+ <format>nat</format>
+ <description>NAT</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nat66</format>
+ <description>NAT66</description>
+ </valueHelp>
+ <valueHelp>
+ <format>firewall</format>
+ <description>firewall</description>
+ </valueHelp>
+ <constraint>
+ <regex>(nat|nat66|firewall)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/container.xml.in b/op-mode-definitions/container.xml.in
index ada9a4d59..f581d39fa 100644
--- a/op-mode-definitions/container.xml.in
+++ b/op-mode-definitions/container.xml.in
@@ -83,7 +83,7 @@
<children>
<tagNode name="container">
<properties>
- <help>Monitor last lines of container logs</help>
+ <help>Monitor last lines of container log</help>
<completionHelp>
<path>container name</path>
</completionHelp>
diff --git a/op-mode-definitions/dns-dynamic.xml.in b/op-mode-definitions/dns-dynamic.xml.in
index 4f0399964..8f32f63f9 100644
--- a/op-mode-definitions/dns-dynamic.xml.in
+++ b/op-mode-definitions/dns-dynamic.xml.in
@@ -6,7 +6,7 @@
<children>
<node name="dns">
<properties>
- <help>Monitor last lines of Domain Name System (DNS) related services</help>
+ <help>Monitor last lines of Domain Name System related services</help>
</properties>
<children>
<node name="dynamic">
@@ -27,7 +27,7 @@
<children>
<node name="dns">
<properties>
- <help>Show log for Domain Name System (DNS) related services</help>
+ <help>Show log for Domain Name System related services</help>
</properties>
<children>
<node name="dynamic">
@@ -42,7 +42,7 @@
</node>
<node name="dns">
<properties>
- <help>Show Domain Name System (DNS) related information</help>
+ <help>Show Domain Name System related information</help>
</properties>
<children>
<node name="dynamic">
@@ -66,7 +66,7 @@
<children>
<node name="dns">
<properties>
- <help>Restart specific Domain Name System (DNS) related service</help>
+ <help>Restart specific Domain Name System related service</help>
</properties>
<children>
<node name="dynamic">
@@ -86,7 +86,7 @@
<children>
<node name="dns">
<properties>
- <help>Update Domain Name System (DNS) related information</help>
+ <help>Update Domain Name System related information</help>
</properties>
<children>
<node name="dynamic">
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index c577c5a39..2e5439414 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -16,7 +16,7 @@
</node>
<node name="ids">
<properties>
- <help>Monitor log for Intrusion Detection System</help>
+ <help>Monitor Intrusion Detection System log</help>
</properties>
<children>
<leafNode name="ddos-protection">
@@ -29,18 +29,18 @@
</node>
<node name="dhcp">
<properties>
- <help>Monitor last lines of Dynamic Host Control Protocol (DHCP)</help>
+ <help>Monitor last lines of Dynamic Host Control Protocol log</help>
</properties>
<children>
<node name="server">
<properties>
- <help>Monitor last lines of DHCP server</help>
+ <help>Monitor last lines of DHCP server log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit isc-dhcp-server.service</command>
</node>
<node name="client">
<properties>
- <help>Monitor last lines of DHCP client</help>
+ <help>Monitor last lines of DHCP client log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit "dhclient@*.service"</command>
<children>
@@ -59,18 +59,18 @@
</node>
<node name="dhcpv6">
<properties>
- <help>Monitor last lines of Dynamic Host Control Protocol IPv6 (DHCPv6)</help>
+ <help>Monitor last lines of Dynamic Host Control Protocol IPv6 log</help>
</properties>
<children>
<node name="server">
<properties>
- <help>Monitor last lines of DHCPv6 server</help>
+ <help>Monitor last lines of DHCPv6 server log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit isc-dhcp-server6.service</command>
</node>
<node name="client">
<properties>
- <help>Monitor last lines of DHCPv6 client</help>
+ <help>Monitor last lines of DHCPv6 client log</help>
</properties>
<command>journalctl --no-hostname --follow --boot --unit "dhcp6c@*.service"</command>
<children>
@@ -95,7 +95,7 @@
</leafNode>
<leafNode name="ipoe-server">
<properties>
- <help>Monitor last lines of IPoE server log</help>
+ <help>Monitor last lines of IP over Ethernet server log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit accel-ppp@ipoe.service</command>
</leafNode>
@@ -107,13 +107,13 @@
</leafNode>
<leafNode name="nhrp">
<properties>
- <help>Monitor last lines of Next Hop Resolution Protocol (NHRP) log</help>
+ <help>Monitor last lines of Next Hop Resolution Protocol log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit opennhrp.service</command>
</leafNode>
<leafNode name="ntp">
<properties>
- <help>Monitor last lines of Network Time Protocol (NTP) log</help>
+ <help>Monitor last lines of Network Time Protocol log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit chrony.service</command>
</leafNode>
@@ -142,7 +142,7 @@
</leafNode>
<node name="protocol">
<properties>
- <help>Monitor log for Routing Protocol</help>
+ <help>Monitor routing protocol logs</help>
</properties>
<children>
<leafNode name="ospf">
@@ -232,25 +232,25 @@
</node>
<leafNode name="router-advert">
<properties>
- <help>Monitor last lines of Router Advertisement Daemon (radvd)</help>
+ <help>Monitor last lines of Router Advertisement Daemon log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit radvd.service</command>
</leafNode>
<leafNode name="snmp">
<properties>
- <help>Monitor last lines of Simple Network Monitoring Protocol (SNMP)</help>
+ <help>Monitor last lines of Simple Network Monitoring Protocol log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit snmpd.service</command>
</leafNode>
<leafNode name="ssh">
<properties>
- <help>Monitor last lines of Secure Shell (SSH)</help>
+ <help>Monitor last lines of Secure Shell log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit ssh.service</command>
</leafNode>
<node name="sstpc">
<properties>
- <help>Monitor last lines of SSTP client log</help>
+ <help>Monitor last lines of Secure Socket Tunneling Protocol log</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit "ppp@sstpc*.service"</command>
<children>
@@ -267,7 +267,7 @@
</node>
<node name="vpn">
<properties>
- <help>Monitor Virtual Private Network (VPN) services</help>
+ <help>Monitor Virtual Private Network services</help>
</properties>
<children>
<leafNode name="all">
@@ -278,36 +278,54 @@
</leafNode>
<leafNode name="ipsec">
<properties>
- <help>Monitor last lines of IPsec</help>
+ <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</help>
+ <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</help>
+ <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</help>
+ <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</help>
+ <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>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit vpp.service</command>
+ </leafNode>
+ <leafNode name="vrrp">
+ <properties>
+ <help>Monitor last lines of Virtual Router Redundancy Protocol log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit keepalived.service</command>
+ </leafNode>
+ <leafNode name="webproxy">
+ <properties>
+ <help>Monitor last lines of Webproxy log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit squid.service</command>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in
index 346febec0..c5abf86cd 100644
--- a/op-mode-definitions/pki.xml.in
+++ b/op-mode-definitions/pki.xml.in
@@ -505,6 +505,14 @@
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4"</command>
+ <children>
+ <leafNode name="pem">
+ <properties>
+ <help>Show x509 CA certificate in PEM format</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --ca "$4" --pem</command>
+ </leafNode>
+ </children>
</tagNode>
<leafNode name="certificate">
<properties>
@@ -520,6 +528,14 @@
</completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4"</command>
+ <children>
+ <leafNode name="pem">
+ <properties>
+ <help>Show x509 certificate in PEM format</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --certificate "$4" --pem</command>
+ </leafNode>
+ </children>
</tagNode>
<leafNode name="crl">
<properties>
@@ -527,6 +543,23 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "all"</command>
</leafNode>
+ <tagNode name="crl">
+ <properties>
+ <help>Show x509 certificate revocation lists by CA name</help>
+ <completionHelp>
+ <path>pki ca</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "$4"</command>
+ <children>
+ <leafNode name="pem">
+ <properties>
+ <help>Show x509 certificate revocation lists by CA name in PEM format</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/pki.py --action show --crl "$4" --pem</command>
+ </leafNode>
+ </children>
+ </tagNode>
</children>
<command>sudo ${vyos_op_scripts_dir}/pki.py --action show</command>
</node>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 7663e4c00..579e348f7 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -2,9 +2,18 @@
<interfaceDefinition>
<node name="show">
<children>
+ <tagNode name="log">
+ <properties>
+ <help>Show last number of messages in master logging buffer</help>
+ <completionHelp>
+ <list>&lt;1-9999&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>if ${vyos_validators_dir}/numeric --range 1-9999 "$3"; then journalctl --no-hostname --boot --lines "$3"; fi</command>
+ </tagNode>
<node name="log">
<properties>
- <help>Show contents of current master log file</help>
+ <help>Show contents of current master logging buffer</help>
</properties>
<command>journalctl --no-hostname --boot</command>
<children>
@@ -461,6 +470,12 @@
</leafNode>
</children>
</node>
+ <leafNode name="vpp">
+ <properties>
+ <help>Show log for Vector Packet Processor (VPP)</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit vpp.service</command>
+ </leafNode>
<leafNode name="vrrp">
<properties>
<help>Show log for Virtual Router Redundancy Protocol (VRRP)</help>
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index 26114149f..e1870aa0a 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -21,6 +21,8 @@ import logging
from typing import Optional, Tuple, Union
from filecmp import cmp
from datetime import datetime
+from textwrap import dedent
+from pathlib import Path
from tabulate import tabulate
from vyos.config import Config
@@ -456,19 +458,18 @@ Proceed ?'''
return ConfigTree(c)
def _add_logrotate_conf(self):
- conf = f"""{archive_config_file} {{
- su root vyattacfg
- rotate {self.max_revisions}
- start 0
- compress
- copy
-}}"""
- mask = os.umask(0o133)
-
- with open(logrotate_conf, 'w') as f:
- f.write(conf)
-
- os.umask(mask)
+ conf: str = dedent(f"""\
+ {archive_config_file} {{
+ su root vyattacfg
+ rotate {self.max_revisions}
+ start 0
+ compress
+ copy
+ }}
+ """)
+ conf_file = Path(logrotate_conf)
+ conf_file.write_text(conf)
+ conf_file.chmod(0o644)
def _archive_active_config(self) -> bool:
mask = os.umask(0o113)
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 33da5da40..ed651fdc3 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -913,7 +913,7 @@ def get_interface_config(interface):
if not os.path.exists(f'/sys/class/net/{interface}'):
return None
from json import loads
- tmp = loads(cmd(f'ip -d -j link show {interface}'))[0]
+ tmp = loads(cmd(f'ip --detail --json link show dev {interface}'))[0]
return tmp
def get_interface_address(interface):
@@ -923,7 +923,7 @@ def get_interface_address(interface):
if not os.path.exists(f'/sys/class/net/{interface}'):
return None
from json import loads
- tmp = loads(cmd(f'ip -d -j addr show {interface}'))[0]
+ tmp = loads(cmd(f'ip --detail --json addr show dev {interface}'))[0]
return tmp
def get_interface_namespace(iface):
@@ -937,17 +937,17 @@ def get_interface_namespace(iface):
return None
for ns in tmp:
- namespace = f'{ns["name"]}'
+ netns = f'{ns["name"]}'
# Search interface in each netns
- data = loads(cmd(f'ip netns exec {namespace} ip -j link show'))
- for compare in data:
- if iface == compare["ifname"]:
- return namespace
+ data = loads(cmd(f'ip netns exec {netns} ip --json link show'))
+ for tmp in data:
+ if iface == tmp["ifname"]:
+ return netns
def get_all_vrfs():
""" Return a dictionary of all system wide known VRF instances """
from json import loads
- tmp = loads(cmd('ip -j vrf list'))
+ tmp = loads(cmd('ip --json vrf list'))
# Result is of type [{"name":"red","table":1000},{"name":"blue","table":2000}]
# so we will re-arrange it to a more nicer representation:
# {'red': {'table': 1000}, 'blue': {'table': 2000}}
diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py
index 72b7ca6da..7386d44f0 100644
--- a/python/vyos/utils/network.py
+++ b/python/vyos/utils/network.py
@@ -15,7 +15,6 @@
import os
-
def get_protocol_by_name(protocol_name):
"""Get protocol number by protocol name
@@ -28,3 +27,10 @@ def get_protocol_by_name(protocol_name):
return protocol_number
except socket.error:
return protocol_name
+
+def interface_exists_in_netns(interface_name, netns):
+ from vyos.util import rc_cmd
+ rc, out = rc_cmd(f'ip netns exec {netns} ip link show dev {interface_name}')
+ if rc == 0:
+ return True
+ return False
diff --git a/python/vyos/utils/system.py b/python/vyos/utils/system.py
new file mode 100644
index 000000000..7102d5985
--- /dev/null
+++ b/python/vyos/utils/system.py
@@ -0,0 +1,82 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+from subprocess import run
+
+
+def sysctl_read(name: str) -> str:
+ """Read and return current value of sysctl() option
+
+ Args:
+ name (str): sysctl key name
+
+ Returns:
+ str: sysctl key value
+ """
+ tmp = run(['sysctl', '-nb', name], capture_output=True)
+ return tmp.stdout.decode()
+
+
+def sysctl_write(name: str, value: str | int) -> bool:
+ """Change value via sysctl()
+
+ Args:
+ name (str): sysctl key name
+ value (str | int): sysctl key value
+
+ Returns:
+ bool: True if changed, False otherwise
+ """
+ # convert other types to string before comparison
+ if not isinstance(value, str):
+ value = str(value)
+ # do not change anything if a value is already configured
+ if sysctl_read(name) == value:
+ return True
+ # return False if sysctl call failed
+ if run(['sysctl', '-wq', f'{name}={value}']).returncode != 0:
+ return False
+ # compare old and new values
+ # sysctl may apply value, but its actual value will be
+ # different from requested
+ if sysctl_read(name) == value:
+ return True
+ # False in other cases
+ return False
+
+
+def sysctl_apply(sysctl_dict: dict[str, str], revert: bool = True) -> bool:
+ """Apply sysctl values.
+
+ Args:
+ sysctl_dict (dict[str, str]): dictionary with sysctl keys with values
+ revert (bool, optional): Revert to original values if new were not
+ applied. Defaults to True.
+
+ Returns:
+ bool: True if all params configured properly, False in other cases
+ """
+ # get current values
+ sysctl_original: dict[str, str] = {}
+ for key_name in sysctl_dict.keys():
+ sysctl_original[key_name] = sysctl_read(key_name)
+ # apply new values and revert in case one of them was not applied
+ for key_name, value in sysctl_dict.items():
+ if not sysctl_write(key_name, value):
+ if revert:
+ sysctl_apply(sysctl_original, revert=False)
+ return False
+ # everything applied
+ return True
diff --git a/python/vyos/xml_ref/definition.py b/python/vyos/xml_ref/definition.py
index 33a49ca69..7634773d6 100644
--- a/python/vyos/xml_ref/definition.py
+++ b/python/vyos/xml_ref/definition.py
@@ -123,9 +123,6 @@ class Xml:
return d
def multi_to_list(self, rpath: list, conf: dict) -> dict:
- if rpath and rpath[-1] in list(conf):
- raise ValueError('rpath should be disjoint from conf keys')
-
res: Any = {}
for k in list(conf):
@@ -246,10 +243,6 @@ class Xml:
if not conf:
return self.get_defaults(path, get_first_key=get_first_key,
recursive=recursive)
- if path and path[-1] in list(conf):
- conf = conf[path[-1]]
- conf = {} if not isinstance(conf, dict) else conf
-
if not self._well_defined(path, conf):
print('path to config dict does not define full config paths')
return {}
diff --git a/smoketest/scripts/cli/test_ha_virtual_server.py b/smoketest/scripts/cli/test_ha_virtual_server.py
index e3a91283e..6327df9df 100755
--- a/smoketest/scripts/cli/test_ha_virtual_server.py
+++ b/smoketest/scripts/cli/test_ha_virtual_server.py
@@ -47,6 +47,7 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
delay = '10'
method = 'nat'
persistence_timeout = '600'
+ vs = 'serv-one'
vip = '203.0.113.111'
vport = '2222'
rservers = ['192.0.2.21', '192.0.2.22', '192.0.2.23']
@@ -56,21 +57,23 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
vserver_base = base_path + ['virtual-server']
- self.cli_set(vserver_base + [vip, 'algorithm', algo])
- self.cli_set(vserver_base + [vip, 'delay-loop', delay])
- self.cli_set(vserver_base + [vip, 'forward-method', method])
- self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
- self.cli_set(vserver_base + [vip, 'port', vport])
- self.cli_set(vserver_base + [vip, 'protocol', proto])
+ self.cli_set(vserver_base + [vs, 'address', vip])
+ self.cli_set(vserver_base + [vs, 'algorithm', algo])
+ self.cli_set(vserver_base + [vs, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vs, 'forward-method', method])
+ self.cli_set(vserver_base + [vs, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vs, 'port', vport])
+ self.cli_set(vserver_base + [vs, 'protocol', proto])
for rs in rservers:
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'port', rport])
# commit changes
self.cli_commit()
config = read_file(KEEPALIVED_CONF)
+ self.assertIn(f'virtual_server {vip} {vport}', config)
self.assertIn(f'delay_loop {delay}', config)
self.assertIn(f'lb_algo lc', config)
self.assertIn(f'lb_kind {method.upper()}', config)
@@ -86,6 +89,7 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
delay = '15'
method = 'nat'
persistence_timeout = '300'
+ vs = 'serv-two'
vip = '203.0.113.222'
vport = '22322'
rservers = ['192.0.2.11', '192.0.2.12']
@@ -107,15 +111,16 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(vrrp_base + [group, 'vrid', vrid])
# Virtual-server config
- self.cli_set(vserver_base + [vip, 'algorithm', algo])
- self.cli_set(vserver_base + [vip, 'delay-loop', delay])
- self.cli_set(vserver_base + [vip, 'forward-method', method])
- self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
- self.cli_set(vserver_base + [vip, 'port', vport])
- self.cli_set(vserver_base + [vip, 'protocol', proto])
+ self.cli_set(vserver_base + [vs, 'address', vip])
+ self.cli_set(vserver_base + [vs, 'algorithm', algo])
+ self.cli_set(vserver_base + [vs, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vs, 'forward-method', method])
+ self.cli_set(vserver_base + [vs, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vs, 'port', vport])
+ self.cli_set(vserver_base + [vs, 'protocol', proto])
for rs in rservers:
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
- self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vs, 'real-server', rs, 'port', rport])
# commit changes
self.cli_commit()
@@ -131,6 +136,7 @@ class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'preempt_delay 0', config) # default value
# Keepalived virtual-server
+ self.assertIn(f'virtual_server {vip} {vport}', config)
self.assertIn(f'delay_loop {delay}', config)
self.assertIn(f'lb_algo lc', config)
self.assertIn(f'lb_kind {method.upper()}', config)
diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py
index e18b426b1..2b1cdbd23 100755
--- a/src/conf_mode/high-availability.py
+++ b/src/conf_mode/high-availability.py
@@ -175,6 +175,11 @@ def verify(ha):
# Virtual-server
if 'virtual_server' in ha:
for vs, vs_config in ha['virtual_server'].items():
+
+ if 'address' not in vs_config and 'fwmark' not in vs_config:
+ raise ConfigError('Either address or fwmark is required '
+ f'but not set for virtual-server "{vs}"')
+
if 'port' not in vs_config and 'fwmark' not in vs_config:
raise ConfigError(f'Port or fwmark is required but not set for virtual-server "{vs}"')
if 'port' in vs_config and 'fwmark' in vs_config:
diff --git a/src/conf_mode/service_config_sync.py b/src/conf_mode/service_config_sync.py
new file mode 100755
index 000000000..5cde735a1
--- /dev/null
+++ b/src/conf_mode/service_config_sync.py
@@ -0,0 +1,111 @@
+#!/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
+import json
+from pathlib import Path
+
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.xml import defaults
+from vyos import ConfigError
+from vyos import airbag
+
+airbag.enable()
+
+
+service_conf = Path(f'/run/config_sync_conf.conf')
+post_commit_dir = '/run/scripts/commit/post-hooks.d'
+post_commit_file_src = '/usr/libexec/vyos/vyos_config_sync.py'
+post_commit_file = f'{post_commit_dir}/vyos_config_sync'
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ base = ['service', 'config-sync']
+ if not conf.exists(base):
+ return None
+ config = conf.get_config_dict(base,
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ default_values = defaults(base)
+ config = dict_merge(default_values, config)
+
+ return config
+
+
+def verify(config):
+ # bail out early - looks like removal from running config
+ if not config:
+ return None
+
+ if 'mode' not in config:
+ raise ConfigError(f'config-sync mode is mandatory!')
+
+ for option in ['secondary', 'section']:
+ if option not in config:
+ raise ConfigError(f"config-sync '{option}' is not configured!")
+
+ if 'address' not in config['secondary']:
+ raise ConfigError(f'secondary address is mandatory!')
+ if 'key' not in config['secondary']:
+ raise ConfigError(f'secondary key is mandatory!')
+
+
+def generate(config):
+ if not config:
+
+ if os.path.exists(post_commit_file):
+ os.unlink(post_commit_file)
+
+ if service_conf.exists():
+ service_conf.unlink()
+
+ return None
+
+ # Write configuration file
+ conf_json = json.dumps(config, indent=4)
+ service_conf.write_text(conf_json)
+
+ # Create post commit dir
+ if not os.path.isdir(post_commit_dir):
+ os.makedirs(post_commit_dir)
+
+ # Symlink from helpers to post-commit
+ if not os.path.exists(post_commit_file):
+ os.symlink(post_commit_file_src, post_commit_file)
+
+ return None
+
+
+def apply(config):
+ return None
+
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 9b7c04eb0..f4611e15e 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -161,8 +161,12 @@ def verify(snmp):
for address in snmp['listen_address']:
# We only wan't to configure addresses that exist on the system.
# Hint the user if they don't exist
- if not is_addr_assigned(address):
- Warning(f'SNMP listen address "{address}" not configured!')
+ if 'vrf' in snmp:
+ vrf_name = snmp['vrf']
+ if not is_addr_assigned(address, vrf_name) and address not in ['::1','127.0.0.1']:
+ raise ConfigError(f'SNMP listen address "{address}" not configured in vrf "{vrf_name}"!')
+ elif not is_addr_assigned(address):
+ raise ConfigError(f'SNMP listen address "{address}" not configured in default vrf!')
if 'trap_target' in snmp:
for trap, trap_config in snmp['trap_target'].items():
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
index dc13f4e60..87ebc3ea9 100755
--- a/src/conf_mode/vpp.py
+++ b/src/conf_mode/vpp.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import psutil
+from psutil import virtual_memory
from pathlib import Path
from re import search as re_search, MULTILINE as re_M
@@ -26,6 +26,7 @@ from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.ifconfig import Section
from vyos.util import call, rc_cmd, boot_configuration_complete
+from vyos.utils.system import sysctl_read, sysctl_apply
from vyos.template import render
from vyos.xml import defaults
@@ -39,10 +40,10 @@ airbag.enable()
service_name = 'vpp'
service_conf = Path(f'/run/vpp/{service_name}.conf')
systemd_override = '/run/systemd/system/vpp.service.d/10-override.conf'
-sysctl_vpp = '/etc/sysctl.d/80-vpp.conf'
-# Min memory 6GB (2GB reserved for vpp)
-MIN_TOTAL_MEMORY = 6
+# Free memory required for VPP
+# 2 GB for hugepages + 1 GB for other services
+MIN_AVAILABLE_MEMORY: int = 3 * 1024**3
def _get_pci_address_by_interface(iface) -> str:
@@ -64,7 +65,6 @@ def _get_pci_address_by_interface(iface) -> str:
raise ConfigError(f'Cannot find PCI address for interface {iface}')
-
def get_config(config=None):
if config:
conf = config
@@ -131,32 +131,45 @@ def verify(config):
return None
if 'interface' not in config:
- raise ConfigError(f'"interface" is required but not set!')
+ raise ConfigError('"interface" is required but not set!')
if 'cpu' in config:
- if 'corelist_workers' in config['cpu'] and 'main_core' not in config['cpu']:
- raise ConfigError(f'"cpu main-core" is required but not set!')
+ if 'corelist_workers' in config['cpu'] and 'main_core' not in config[
+ 'cpu']:
+ raise ConfigError('"cpu main-core" is required but not set!')
- memory = psutil.virtual_memory()
- memory_total = round(memory.total / (1024 ** 3), 2)
- if memory_total < MIN_TOTAL_MEMORY:
+ memory_available: int = virtual_memory().available
+ if memory_available < MIN_AVAILABLE_MEMORY:
raise ConfigError(
- f'Not enough installed memory {memory_total}GB! '
- f'The minimum required memory is {MIN_TOTAL_MEMORY}GB.'
- )
+ 'Not enough free memory to start VPP:\n'
+ f'available: {round(memory_available / 1024**3, 1)}GB\n'
+ f'required: {round(MIN_AVAILABLE_MEMORY / 1024**3, 1)}GB')
def generate(config):
if not config or (len(config) == 1 and 'removed_ifaces' in config):
# Remove old config and return
service_conf.unlink(missing_ok=True)
- if os.path.isfile(sysctl_vpp):
- os.unlink(sysctl_vpp)
return None
render(service_conf, 'vpp/startup.conf.j2', config)
render(systemd_override, 'vpp/override.conf.j2', config)
- render(sysctl_vpp, 'vpp/sysctl.conf.j2', config)
+
+ # apply default sysctl values from
+ # https://github.com/FDio/vpp/blob/v23.06/src/vpp/conf/80-vpp.conf
+ sysctl_config: dict[str, str] = {
+ 'vm.nr_hugepages': '1024',
+ 'vm.max_map_count': '3096',
+ 'vm.hugetlb_shm_group': '0',
+ 'kernel.shmmax': '2147483648'
+ }
+ # we do not want to reduce `kernel.shmmax`
+ kernel_shmnax_current: str = sysctl_read('kernel.shmmax')
+ if int(kernel_shmnax_current) > int(sysctl_config['kernel.shmmax']):
+ sysctl_config['kernel.shmmax'] = kernel_shmnax_current
+
+ if not sysctl_apply(sysctl_config):
+ raise ConfigError('Cannot configure sysctl parameters for VPP')
return None
@@ -168,8 +181,6 @@ def apply(config):
call('systemctl daemon-reload')
call(f'systemctl restart {service_name}.service')
- call(f'sysctl -qp {sysctl_vpp}')
-
# Initialize interfaces removed from VPP
for iface in config.get('removed_ifaces', []):
host_control = HostControl()
diff --git a/src/etc/systemd/system-generators/vyos-generator b/src/etc/systemd/system-generators/vyos-generator
new file mode 100755
index 000000000..34faab6a2
--- /dev/null
+++ b/src/etc/systemd/system-generators/vyos-generator
@@ -0,0 +1,94 @@
+#!/bin/sh
+set -f
+
+LOG=""
+DEBUG_LEVEL=1
+LOG_D="/run/vyos-router"
+ENABLE="enabled"
+DISABLE="disabled"
+FOUND="found"
+NOTFOUND="notfound"
+RUN_ENABLED_FILE="$LOG_D/$ENABLE"
+VYOS_SYSTEM_TARGET="/lib/systemd/system/vyos.target"
+VYOS_TARGET_NAME="vyos.target"
+
+debug() {
+ local lvl="$1"
+ shift
+ [ "$lvl" -gt "$DEBUG_LEVEL" ] && return
+ if [ -z "$LOG" ]; then
+ local log="$LOG_D/${0##*/}.log"
+ { [ -d "$LOG_D" ] || mkdir -p "$LOG_D"; } &&
+ { : > "$log"; } >/dev/null 2>&1 && LOG="$log" ||
+ LOG="/dev/kmsg"
+ fi
+ echo "$@" >> "$LOG"
+}
+
+default() {
+ _RET="$ENABLE"
+}
+
+main() {
+ local normal_d="$1" early_d="$2" late_d="$3"
+ local target_name="multi-user.target" gen_d="$early_d"
+ local link_path="$gen_d/${target_name}.wants/${VYOS_TARGET_NAME}"
+ local ds="$NOTFOUND"
+
+ debug 1 "$0 normal=$normal_d early=$early_d late=$late_d"
+ debug 2 "$0 $*"
+
+ local search result="error" ret=""
+ for search in default; do
+ if $search; then
+ debug 1 "$search found $_RET"
+ [ "$_RET" = "$ENABLE" -o "$_RET" = "$DISABLE" ] &&
+ result=$_RET && break
+ else
+ ret=$?
+ debug 0 "search $search returned $ret"
+ fi
+ done
+
+ # enable AND ds=found == enable
+ # enable AND ds=notfound == disable
+ # disable || <any> == disabled
+ if [ "$result" = "$ENABLE" ]; then
+ if [ -e "$link_path" ]; then
+ debug 1 "already enabled: no change needed"
+ else
+ [ -d "${link_path%/*}" ] || mkdir -p "${link_path%/*}" ||
+ debug 0 "failed to make dir $link_path"
+ if ln -snf "$VYOS_SYSTEM_TARGET" "$link_path"; then
+ debug 1 "enabled via $link_path -> $VYOS_SYSTEM_TARGET"
+ else
+ ret=$?
+ debug 0 "[$ret] enable failed:" \
+ "ln $VYOS_SYSTEM_TARGET $link_path"
+ fi
+ fi
+ : > "$RUN_ENABLED_FILE"
+ elif [ "$result" = "$DISABLE" ]; then
+ if [ -f "$link_path" ]; then
+ if rm -f "$link_path"; then
+ debug 1 "disabled. removed existing $link_path"
+ else
+ ret=$?
+ debug 0 "[$ret] disable failed, remove $link_path"
+ fi
+ else
+ debug 1 "already disabled: no change needed [no $link_path]"
+ fi
+ if [ -e "$RUN_ENABLED_FILE" ]; then
+ rm -f "$RUN_ENABLED_FILE"
+ fi
+ else
+ debug 0 "unexpected result '$result' 'ds=$ds'"
+ ret=3
+ fi
+ return $ret
+}
+
+main "$@"
+
+# vi: ts=4 expandtab
diff --git a/src/etc/systemd/system/getty@.service.d/aftervyos.conf b/src/etc/systemd/system/getty@.service.d/aftervyos.conf
new file mode 100644
index 000000000..c5753900e
--- /dev/null
+++ b/src/etc/systemd/system/getty@.service.d/aftervyos.conf
@@ -0,0 +1,3 @@
+[Service]
+ExecStartPre=-/usr/libexec/vyos/init/vyos-config
+StandardOutput=journal+console
diff --git a/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf b/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf
new file mode 100644
index 000000000..8ba42778d
--- /dev/null
+++ b/src/etc/systemd/system/serial-getty@.service.d/aftervyos.conf
@@ -0,0 +1,3 @@
+[Service]
+ExecStartPre=-/usr/libexec/vyos/init/vyos-config SERIAL
+StandardOutput=journal+console
diff --git a/src/helpers/vyos_config_sync.py b/src/helpers/vyos_config_sync.py
new file mode 100755
index 000000000..7cfa8fe88
--- /dev/null
+++ b/src/helpers/vyos_config_sync.py
@@ -0,0 +1,192 @@
+#!/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
+import json
+import requests
+import urllib3
+import logging
+from typing import Optional, List, Union, Dict, Any
+
+from vyos.config import Config
+from vyos.template import bracketize_ipv6
+
+
+CONFIG_FILE = '/run/config_sync_conf.conf'
+
+# Logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+logger.name = os.path.basename(__file__)
+
+# API
+API_HEADERS = {'Content-Type': 'application/json'}
+
+
+def post_request(url: str,
+ data: str,
+ headers: Dict[str, str]) -> requests.Response:
+ """Sends a POST request to the specified URL
+
+ Args:
+ url (str): The URL to send the POST request to.
+ data (Dict[str, Any]): The data to send with the POST request.
+ headers (Dict[str, str]): The headers to include with the POST request.
+
+ Returns:
+ requests.Response: The response object representing the server's response to the request
+ """
+
+ response = requests.post(url,
+ data=data,
+ headers=headers,
+ verify=False,
+ timeout=timeout)
+ return response
+
+
+def retrieve_config(section: str = None) -> Optional[Dict[str, Any]]:
+ """Retrieves the configuration from the local server.
+
+ Args:
+ section: str: The section of the configuration to retrieve. Default is None.
+
+ Returns:
+ Optional[Dict[str, Any]]: The retrieved configuration as a dictionary, or None if an error occurred.
+ """
+ if section is None:
+ section = []
+ else:
+ section = section.split()
+
+ conf = Config()
+ config = conf.get_config_dict(section, get_first_key=True)
+ if config:
+ return config
+ return None
+
+
+def set_remote_config(
+ address: str,
+ key: str,
+ op: str,
+ path: str = None,
+ section: Optional[str] = None) -> Optional[Dict[str, Any]]:
+ """Loads the VyOS configuration in JSON format to a remote host.
+
+ Args:
+ address (str): The address of the remote host.
+ key (str): The key to use for loading the configuration.
+ path (Optional[str]): The path of the configuration. Default is None.
+ section (Optional[str]): The section of the configuration to load. Default is None.
+
+ Returns:
+ Optional[Dict[str, Any]]: The response from the remote host as a dictionary, or None if an error occurred.
+ """
+
+ if path is None:
+ path = []
+ else:
+ path = path.split()
+ headers = {'Content-Type': 'application/json'}
+
+ # Disable the InsecureRequestWarning
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+ url = f'https://{address}/configure-section'
+ data = json.dumps({
+ 'op': mode,
+ 'path': path,
+ 'section': section,
+ 'key': key
+ })
+
+ try:
+ config = post_request(url, data, headers)
+ return config.json()
+ except requests.exceptions.RequestException as e:
+ print(f"An error occurred: {e}")
+ logger.error(f"An error occurred: {e}")
+ return None
+
+
+def is_section_revised(section: str) -> bool:
+ from vyos.config_mgmt import is_node_revised
+ return is_node_revised([section])
+
+
+def config_sync(secondary_address: str,
+ secondary_key: str,
+ sections: List[str],
+ mode: str):
+ """Retrieve a config section from primary router in JSON format and send it to
+ secondary router
+ """
+ # Config sync only if sections changed
+ if not any(map(is_section_revised, sections)):
+ return
+
+ logger.info(
+ f"Config synchronization: Mode={mode}, Secondary={secondary_address}"
+ )
+
+ # Sync sections ("nat", "firewall", etc)
+ for section in sections:
+ config_json = retrieve_config(section=section)
+ # Check if config path deesn't exist, for example "set nat"
+ # we set empty value for config_json data
+ # As we cannot send to the remote host section "nat None" config
+ if not config_json:
+ config_json = ""
+ logger.debug(
+ f"Retrieved config for section '{section}': {config_json}")
+ set_config = set_remote_config(address=secondary_address,
+ key=secondary_key,
+ op=mode,
+ path=section,
+ section=config_json)
+ logger.debug(f"Set config for section '{section}': {set_config}")
+
+
+if __name__ == '__main__':
+ # Read configuration from file
+ if not os.path.exists(CONFIG_FILE):
+ logger.error(f"Post-commit: No config file '{CONFIG_FILE}' exists")
+ exit(0)
+
+ with open(CONFIG_FILE, 'r') as f:
+ config_data = f.read()
+
+ config = json.loads(config_data)
+
+ mode = config.get('mode')
+ secondary_address = config.get('secondary', {}).get('address')
+ secondary_address = bracketize_ipv6(secondary_address)
+ secondary_key = config.get('secondary', {}).get('key')
+ sections = config.get('section')
+ timeout = int(config.get('secondary', {}).get('timeout'))
+
+ if not all([
+ mode, secondary_address, secondary_key, sections
+ ]):
+ logger.error(
+ "Missing required configuration data for config synchronization.")
+ exit(0)
+
+ config_sync(secondary_address, secondary_key,
+ sections, mode)
diff --git a/src/init/vyos-config b/src/init/vyos-config
new file mode 100755
index 000000000..356427024
--- /dev/null
+++ b/src/init/vyos-config
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+while [ ! -f /tmp/vyos-config-status ]
+do
+ sleep 1
+done
+
+status=$(cat /tmp/vyos-config-status)
+
+if [ -z "$1" ]; then
+ if [ $status -ne 0 ]; then
+ echo "Configuration error"
+ else
+ echo "Configuration success"
+ fi
+fi
diff --git a/src/init/vyos-router b/src/init/vyos-router
new file mode 100755
index 000000000..7b752b84b
--- /dev/null
+++ b/src/init/vyos-router
@@ -0,0 +1,421 @@
+#!/bin/bash
+# Copyright (C) 2021 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/>.
+
+. /lib/lsb/init-functions
+
+: ${vyatta_env:=/etc/default/vyatta}
+source $vyatta_env
+
+declare progname=${0##*/}
+declare action=$1; shift
+
+declare -x BOOTFILE=$vyatta_sysconfdir/config/config.boot
+
+# If vyos-config= boot option is present, use that file instead
+for x in $(cat /proc/cmdline); do
+ [[ $x = vyos-config=* ]] || continue
+ VYOS_CONFIG="${x#vyos-config=}"
+done
+
+if [ ! -z "$VYOS_CONFIG" ]; then
+ if [ -r "$VYOS_CONFIG" ]; then
+ echo "Config selected manually: $VYOS_CONFIG"
+ declare -x BOOTFILE="$VYOS_CONFIG"
+ else
+ echo "WARNING: Could not read selected config file, using default!"
+ fi
+fi
+
+declare -a subinit
+declare -a all_subinits=( firewall )
+
+if [ $# -gt 0 ] ; then
+ for s in $@ ; do
+ [ -x ${vyatta_sbindir}/${s}.init ] && subinit[${#subinit}]=$s
+ done
+else
+ for s in ${all_subinits[@]} ; do
+ [ -x ${vyatta_sbindir}/${s}.init ] && subinit[${#subinit}]=$s
+ done
+fi
+
+GROUP=vyattacfg
+
+# easy way to make empty file without any command
+empty()
+{
+ >$1
+}
+
+# check if bootup of this portion is disabled
+disabled () {
+ grep -q -w no-vyos-$1 /proc/cmdline
+}
+
+# if necessary, provide initial config
+init_bootfile () {
+ if [ ! -r $BOOTFILE ] ; then
+ if [ -f $vyatta_sysconfdir/config.boot.default ]; then
+ cp $vyatta_sysconfdir/config.boot.default $BOOTFILE
+ else
+ $vyos_libexec_dir/system-versions-foot.py > $BOOTFILE
+ fi
+ chgrp ${GROUP} $BOOTFILE
+ chmod 660 $BOOTFILE
+ fi
+}
+
+# if necessary, migrate initial config
+migrate_bootfile ()
+{
+ if [ -x $vyos_libexec_dir/run-config-migration.py ]; then
+ log_progress_msg migrate
+ sg ${GROUP} -c "$vyos_libexec_dir/run-config-migration.py $BOOTFILE"
+ fi
+}
+
+# load the initial config
+load_bootfile ()
+{
+ log_progress_msg configure
+ (
+ if [ -f /etc/default/vyatta-load-boot ]; then
+ # build-specific environment for boot-time config loading
+ source /etc/default/vyatta-load-boot
+ fi
+ if [ -x $vyos_libexec_dir/vyos-boot-config-loader.py ]; then
+ sg ${GROUP} -c "$vyos_libexec_dir/vyos-boot-config-loader.py $BOOTFILE"
+ fi
+ )
+}
+
+# execute the pre-config script
+run_preconfig_script ()
+{
+ if [ -x $vyatta_sysconfdir/config/scripts/vyos-preconfig-bootup.script ]; then
+ $vyatta_sysconfdir/config/scripts/vyos-preconfig-bootup.script
+ fi
+}
+
+# execute the post-config scripts
+run_postconfig_scripts ()
+{
+ if [ -x $vyatta_sysconfdir/config/scripts/vyatta-postconfig-bootup.script ]; then
+ $vyatta_sysconfdir/config/scripts/vyatta-postconfig-bootup.script
+ fi
+ if [ -x $vyatta_sysconfdir/config/scripts/vyos-postconfig-bootup.script ]; then
+ $vyatta_sysconfdir/config/scripts/vyos-postconfig-bootup.script
+ fi
+}
+
+run_postupgrade_script ()
+{
+ if [ -f $vyatta_sysconfdir/config/.upgraded ]; then
+ # Run the system script
+ /usr/libexec/vyos/system/post-upgrade
+
+ # Run user scripts
+ if [ -d $vyatta_sysconfdir/config/scripts/post-upgrade.d ]; then
+ run-parts $vyatta_sysconfdir/config/scripts/post-upgrade.d
+ fi
+ rm -f $vyatta_sysconfdir/config/.upgraded
+ fi
+}
+
+#
+# On image booted machines, we need to mount /boot from the image-specific
+# boot directory so that kernel package installation will put the
+# files in the right place. We also have to mount /boot/grub from the
+# system-wide grub directory so that tools that edit the grub.cfg
+# file will find it in the expected location.
+#
+bind_mount_boot ()
+{
+ persist_path=$(/opt/vyatta/sbin/vyos-persistpath)
+ if [ $? == 0 ]; then
+ if [ -e $persist_path/boot ]; then
+ image_name=$(cat /proc/cmdline | sed -e s+^.*vyos-union=/boot/++ | sed -e 's/ .*$//')
+
+ if [ -n "$image_name" ]; then
+ mount --bind $persist_path/boot/$image_name /boot
+ if [ $? -ne 0 ]; then
+ echo "Couldn't bind mount /boot"
+ fi
+
+ if [ ! -d /boot/grub ]; then
+ mkdir /boot/grub
+ fi
+
+ mount --bind $persist_path/boot/grub /boot/grub
+ if [ $? -ne 0 ]; then
+ echo "Couldn't bind mount /boot/grub"
+ fi
+ fi
+ fi
+ fi
+}
+
+clear_or_override_config_files ()
+{
+ for conf in snmp/snmpd.conf snmp/snmptrapd.conf snmp/snmp.conf \
+ keepalived/keepalived.conf cron.d/vyos-crontab \
+ ipvsadm.rules default/ipvsadm resolv.conf
+ do
+ if [ -s /etc/$conf ] ; then
+ empty /etc/$conf
+ chmod 0644 /etc/$conf
+ fi
+ done
+}
+
+update_interface_config ()
+{
+ if [ -d /run/udev/vyos ]; then
+ $vyos_libexec_dir/vyos-interface-rescan.py $BOOTFILE
+ fi
+}
+
+cleanup_post_commit_hooks () {
+ # Remove links from the post-commit hooks directory.
+ # note that this approach only supports hooks that are "configured",
+ # i.e., it does not support hooks that need to always be present.
+ cpostdir=$(cli-shell-api getPostCommitHookDir)
+ # exclude commits hooks from vyatta-cfg
+ excluded="10vyatta-log-commit.pl 99vyos-user-postcommit-hooks"
+ if [ -d "$cpostdir" ]; then
+ for f in $cpostdir/*; do
+ if [[ ! $excluded =~ $(basename $f) ]]; then
+ rm -f $cpostdir/$(basename $f)
+ fi
+ done
+ fi
+}
+
+# These are all the default security setting which are later
+# overridden when configuration is read. These are the values the
+# system defaults.
+security_reset ()
+{
+ # restore PAM back to virgin state (no radius/tacacs services)
+ pam-auth-update --package --remove radius
+ rm -f /etc/pam_radius_auth.conf
+ pam-auth-update --package --remove tacplus
+ rm -f /etc/tacplus_nss.conf /etc/tacplus_servers
+
+ # Certain configuration files are re-generated by the configuration
+ # subsystem and must reside under /etc and can not easily be moved to /run.
+ # So on every boot we simply delete any remaining files and let the CLI
+ # regenearte them.
+
+ # PPPoE
+ rm -f /etc/ppp/peers/pppoe* /etc/ppp/peers/wlm*
+
+ # IPSec
+ rm -rf /etc/ipsec.conf /etc/ipsec.secrets
+ find /etc/swanctl -type f | xargs rm -f
+
+ # limit cleanup
+ rm -f /etc/security/limits.d/10-vyos.conf
+
+ # iproute2 cleanup
+ rm -f /etc/iproute2/rt_tables.d/vyos-*.conf
+
+ # Container
+ rm -f /etc/containers/storage.conf /etc/containers/registries.conf /etc/containers/containers.conf
+ # Clean all networks and re-create them from our CLI
+ rm -f /etc/containers/networks/*
+
+ # System Options (SSH/cURL)
+ rm -f /etc/ssh/ssh_config.d/*vyos*.conf
+ rm -f /etc/curlrc
+}
+
+# XXX: T3885 - generate persistend DHCPv6 DUID (Type4 - UUID based)
+gen_duid ()
+{
+ DUID_FILE="/var/lib/dhcpv6/dhcp6c_duid"
+ UUID_FILE="/sys/class/dmi/id/product_uuid"
+ UUID_FILE_ALT="/sys/class/dmi/id/product_serial"
+ if [ ! -f ${UUID_FILE} ] && [ ! -f ${UUID_FILE_ALT} ]; then
+ return 1
+ fi
+
+ # DUID is based on the BIOS/EFI UUID. We omit additional - characters
+ if [ -f ${UUID_FILE} ]; then
+ UUID=$(cat ${UUID_FILE} | tr -d -)
+ fi
+ if [ -z ${UUID} ]; then
+ UUID=$(uuidgen --sha1 --namespace @dns --name $(cat ${UUID_FILE_ALT}) | tr -d -)
+ fi
+ # Add DUID type4 (UUID) information
+ DUID_TYPE="0004"
+
+ # The length-information (as per RFC6355 UUID is 128 bits long) is in big-endian
+ # format - beware when porting to ARM64. The length field consists out of the
+ # UUID (128 bit + 16 bits DUID type) resulting in hex 12.
+ DUID_LEN="0012"
+ if [ "$(echo -n I | od -to2 | head -n1 | cut -f2 -d" " | cut -c6 )" -eq 1 ]; then
+ # true on little-endian (x86) systems
+ DUID_LEN="1200"
+ fi
+
+ for i in $(echo -n ${DUID_LEN}${DUID_TYPE}${UUID} | sed 's/../& /g'); do
+ echo -ne "\x$i"
+ done > ${DUID_FILE}
+}
+
+start ()
+{
+ # reset and clean config files
+ security_reset || log_failure_msg "security reset failed"
+
+ # some legacy directories migrated over from old rl-system.init
+ mkdir -p /var/run/vyatta /var/log/vyatta
+ chgrp vyattacfg /var/run/vyatta /var/log/vyatta
+ chmod 775 /var/run/vyatta /var/log/vyatta
+
+ log_daemon_msg "Waiting for NICs to settle down"
+ # On boot time udev migth take a long time to reorder nic's, this will ensure that
+ # all udev activity is completed and all nics presented at boot-time will have their
+ # final name before continuing with vyos-router initialization.
+ SECONDS=0
+ udevadm settle
+ STATUS=$?
+ log_progress_msg "settled in ${SECONDS}sec."
+ log_end_msg ${STATUS}
+
+ # mountpoint for bpf maps required by xdp
+ mount -t bpf none /sys/fs/bpf
+
+ # Clear out Debian APT source config file
+ empty /etc/apt/sources.list
+
+ # Generate DHCPv6 DUID
+ gen_duid || log_failure_msg "could not generate DUID"
+
+ # Mount a temporary filesystem for container networks.
+ # Configuration should be loaded from VyOS cli.
+ cni_dir="/etc/cni/net.d"
+ [ ! -d ${cni_dir} ] && mkdir -p ${cni_dir}
+ mount -t tmpfs none ${cni_dir}
+
+ # Init firewall
+ nfct helper add rpc inet tcp
+ nfct helper add rpc inet udp
+ nfct helper add tns inet tcp
+ nft -f /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules"
+
+ rm -f /etc/hostname
+ ${vyos_conf_scripts_dir}/host_name.py || log_failure_msg "could not reset host-name"
+ systemctl start frr.service
+
+ # As VyOS does not execute commands that are not present in the CLI we call
+ # the script by hand to have a single source for the login banner and MOTD
+ ${vyos_conf_scripts_dir}/system_console.py || log_failure_msg "could not reset serial console"
+ ${vyos_conf_scripts_dir}/system-login.py || log_failure_msg "could not reset system login"
+ ${vyos_conf_scripts_dir}/system-login-banner.py || log_failure_msg "could not reset motd and issue files"
+ ${vyos_conf_scripts_dir}/system-option.py || log_failure_msg "could not reset system option files"
+ ${vyos_conf_scripts_dir}/conntrack.py || log_failure_msg "could not reset conntrack subsystem"
+ ${vyos_conf_scripts_dir}/container.py || log_failure_msg "could not reset container subsystem"
+
+ clear_or_override_config_files || log_failure_msg "could not reset config files"
+
+ # enable some debugging before loading the configuration
+ if grep -q vyos-debug /proc/cmdline; then
+ log_action_begin_msg "Enable runtime debugging options"
+ touch /tmp/vyos.container.debug
+ touch /tmp/vyos.ifconfig.debug
+ touch /tmp/vyos.frr.debug
+ touch /tmp/vyos.container.debug
+ fi
+
+ log_action_begin_msg "Mounting VyOS Config"
+ # ensure the vyatta_configdir supports a large number of inodes since
+ # the config hierarchy is often inode-bound (instead of size).
+ # impose a minimum and then scale up dynamically with the actual size
+ # of the system memory.
+ local tmem=$(sed -n 's/^MemTotal: \+\([0-9]\+\) kB$/\1/p' /proc/meminfo)
+ local tpages
+ local tmpfs_opts="nosuid,nodev,mode=775,nr_inodes=0" #automatically allocate inodes
+ mount -o $tmpfs_opts -t tmpfs none ${vyatta_configdir} \
+ && chgrp ${GROUP} ${vyatta_configdir}
+ log_action_end_msg $?
+
+ disabled bootfile || init_bootfile
+
+ cleanup_post_commit_hooks
+
+ log_daemon_msg "Starting VyOS router"
+ disabled migrate || migrate_bootfile
+
+ run_preconfig_script
+
+ run_postupgrade_script
+
+ update_interface_config
+
+ for s in ${subinit[@]} ; do
+ if ! disabled $s; then
+ log_progress_msg $s
+ if ! ${vyatta_sbindir}/${s}.init start
+ then log_failure_msg
+ exit 1
+ fi
+ fi
+ done
+
+ bind_mount_boot
+
+ disabled configure || load_bootfile
+ log_end_msg $?
+
+ telinit q
+ chmod g-w,o-w /
+
+ run_postconfig_scripts
+}
+
+stop()
+{
+ local -i status=0
+ log_daemon_msg "Stopping VyOS router"
+ for ((i=${#sub_inits[@]} - 1; i >= 0; i--)) ; do
+ s=${subinit[$i]}
+ log_progress_msg $s
+ ${vyatta_sbindir}/${s}.init stop
+ let status\|=$?
+ done
+ log_end_msg $status
+ log_action_begin_msg "Un-mounting VyOS Config"
+ umount ${vyatta_configdir}
+ log_action_end_msg $?
+
+ systemctl stop frr.service
+}
+
+case "$action" in
+ start) start ;;
+ stop) stop ;;
+ restart|force-reload) stop && start ;;
+ *) log_failure_msg "usage: $progname [ start|stop|restart ] [ subinit ... ]" ;
+ false ;;
+esac
+
+exit $?
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 4
+# End:
diff --git a/src/migration-scripts/vrrp/3-to-4 b/src/migration-scripts/vrrp/3-to-4
new file mode 100755
index 000000000..b0a6975c2
--- /dev/null
+++ b/src/migration-scripts/vrrp/3-to-4
@@ -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/>.
+
+from sys import argv
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print('Must specify file name!')
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['high-availability', 'virtual-server']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+if config.exists(base):
+ for vs in config.list_nodes(base):
+ vs_base = base + [vs]
+
+ # If the fwmark is used, the address is not required
+ if not config.exists(vs_base + ['fwmark']):
+ # add option: 'virtual-server <tag> address x.x.x.x'
+ config.set(vs_base + ['address'], value=vs)
+
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print(f'Failed to save the modified config: {e}')
+ exit(1)
diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py
index b054690b0..7ea295ff1 100755
--- a/src/op_mode/pki.py
+++ b/src/op_mode/pki.py
@@ -840,7 +840,7 @@ def import_openvpn_secret(name, path):
install_openvpn_key(name, key_data, key_version)
# Show functions
-def show_certificate_authority(name=None):
+def show_certificate_authority(name=None, pem=False):
headers = ['Name', 'Subject', 'Issuer CN', 'Issued', 'Expiry', 'Private Key', 'Parent']
data = []
certs = get_config_ca_certificate()
@@ -852,6 +852,11 @@ def show_certificate_authority(name=None):
continue
cert = load_certificate(cert_dict['certificate'])
+
+ if name and pem:
+ print(encode_certificate(cert))
+ return
+
parent_ca_name = get_certificate_ca(cert, certs)
cert_issuer_cn = cert.issuer.rfc4514_string().split(",")[0]
@@ -867,7 +872,7 @@ def show_certificate_authority(name=None):
print("Certificate Authorities:")
print(tabulate.tabulate(data, headers))
-def show_certificate(name=None):
+def show_certificate(name=None, pem=False):
headers = ['Name', 'Type', 'Subject CN', 'Issuer CN', 'Issued', 'Expiry', 'Revoked', 'Private Key', 'CA Present']
data = []
certs = get_config_certificate()
@@ -885,6 +890,10 @@ def show_certificate(name=None):
if not cert:
continue
+ if name and pem:
+ print(encode_certificate(cert))
+ return
+
ca_name = get_certificate_ca(cert, ca_certs)
cert_subject_cn = cert.subject.rfc4514_string().split(",")[0]
cert_issuer_cn = cert.issuer.rfc4514_string().split(",")[0]
@@ -906,7 +915,7 @@ def show_certificate(name=None):
print("Certificates:")
print(tabulate.tabulate(data, headers))
-def show_crl(name=None):
+def show_crl(name=None, pem=False):
headers = ['CA Name', 'Updated', 'Revokes']
data = []
certs = get_config_ca_certificate()
@@ -927,9 +936,16 @@ def show_crl(name=None):
if not crl:
continue
+ if name and pem:
+ print(encode_certificate(crl))
+ continue
+
certs = get_revoked_by_serial_numbers([revoked.serial_number for revoked in crl])
data.append([cert_name, crl.last_update, ", ".join(certs)])
+ if name and pem:
+ return
+
print("Certificate Revocation Lists:")
print(tabulate.tabulate(data, headers))
@@ -943,6 +959,7 @@ if __name__ == '__main__':
parser.add_argument('--crl', help='Certificate Revocation List', required=False)
parser.add_argument('--sign', help='Sign certificate with specified CA', required=False)
parser.add_argument('--self-sign', help='Self-sign the certificate', action='store_true')
+ parser.add_argument('--pem', help='Output using PEM encoding', action='store_true')
# SSH
parser.add_argument('--ssh', help='SSH Key', required=False)
@@ -1032,16 +1049,16 @@ if __name__ == '__main__':
if not conf.exists(['pki', 'ca', ca_name]):
print(f'CA "{ca_name}" does not exist!')
exit(1)
- show_certificate_authority(ca_name)
+ show_certificate_authority(ca_name, args.pem)
elif args.certificate:
cert_name = None if args.certificate == 'all' else args.certificate
if cert_name:
if not conf.exists(['pki', 'certificate', cert_name]):
print(f'Certificate "{cert_name}" does not exist!')
exit(1)
- show_certificate(None if args.certificate == 'all' else args.certificate)
+ show_certificate(None if args.certificate == 'all' else args.certificate, args.pem)
elif args.crl:
- show_crl(None if args.crl == 'all' else args.crl)
+ show_crl(None if args.crl == 'all' else args.crl, args.pem)
else:
show_certificate_authority()
show_certificate()
diff --git a/src/systemd/vyos-router.service b/src/systemd/vyos-router.service
new file mode 100644
index 000000000..6f683cebb
--- /dev/null
+++ b/src/systemd/vyos-router.service
@@ -0,0 +1,19 @@
+[Unit]
+Description=VyOS Router
+After=systemd-journald-dev-log.socket time-sync.target local-fs.target cloud-config.service
+Requires=frr.service
+Conflicts=shutdown.target
+Before=systemd-user-sessions.service
+
+[Service]
+Type=simple
+Restart=no
+TimeoutSec=20min
+KillMode=process
+RemainAfterExit=yes
+ExecStart=/usr/libexec/vyos/init/vyos-router start
+ExecStop=/usr/libexec/vyos/init/vyos-router stop
+StandardOutput=journal+console
+
+[Install]
+WantedBy=vyos.target
diff --git a/src/systemd/vyos.target b/src/systemd/vyos.target
new file mode 100644
index 000000000..47c91c1cc
--- /dev/null
+++ b/src/systemd/vyos.target
@@ -0,0 +1,3 @@
+[Unit]
+Description=VyOS target
+After=multi-user.target