summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/config-mode-dependencies/vyos-1x.json6
-rw-r--r--data/templates/accel-ppp/config_shaper_radius.j22
-rw-r--r--data/templates/accel-ppp/ipoe.config.j25
-rw-r--r--data/templates/accel-ppp/l2tp.config.j25
-rw-r--r--data/templates/accel-ppp/pppoe.config.j25
-rw-r--r--data/templates/accel-ppp/pptp.config.j25
-rw-r--r--data/templates/accel-ppp/sstp.config.j23
-rw-r--r--data/templates/dns-dynamic/ddclient.conf.j22
-rw-r--r--data/templates/frr/bgpd.frr.j220
-rw-r--r--data/templates/frr/evpn.mh.frr.j216
-rw-r--r--data/templates/rsyslog/rsyslog.conf.j210
-rw-r--r--data/templates/snmp/etc.snmpd.conf.j210
-rw-r--r--data/templates/telegraf/telegraf.j22
-rw-r--r--data/templates/vpp/override.conf.j214
-rw-r--r--data/templates/vpp/startup.conf.j2116
-rw-r--r--debian/control2
-rw-r--r--debian/vyos-1x.postinst11
-rw-r--r--debian/vyos-1x.preinst1
-rw-r--r--interface-definitions/dns-dynamic.xml.in6
-rw-r--r--interface-definitions/include/accel-ppp/max-concurrent-sessions.xml.i15
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i6
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i70
-rw-r--r--interface-definitions/include/version/dns-dynamic-version.xml.i2
-rw-r--r--interface-definitions/include/version/nat-version.xml.i2
-rw-r--r--interface-definitions/include/version/nat66-version.xml.i2
-rw-r--r--interface-definitions/interfaces-bonding.xml.in54
-rw-r--r--interface-definitions/service-ipoe-server.xml.in1
-rw-r--r--interface-definitions/service-pppoe-server.xml.in1
-rw-r--r--interface-definitions/snmp.xml.in95
-rw-r--r--interface-definitions/vpn-l2tp.xml.in1
-rw-r--r--interface-definitions/vpn-pptp.xml.in1
-rw-r--r--interface-definitions/vpn-sstp.xml.in1
-rw-r--r--interface-definitions/vpp.xml.in342
-rw-r--r--op-mode-definitions/include/bgp/show-bgp-common.xml.i16
-rw-r--r--op-mode-definitions/mdns-reflector.xml.in62
-rw-r--r--python/vyos/ifconfig/wireguard.py5
-rw-r--r--python/vyos/vpp.py315
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py40
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireguard.py48
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py18
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_dynamic.py11
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_syslog.py85
-rwxr-xr-xsrc/completion/list_ddclient_protocols.sh2
-rwxr-xr-xsrc/conf_mode/dns_dynamic.py38
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py23
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py18
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py33
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py7
-rwxr-xr-xsrc/conf_mode/vpp.py207
-rw-r--r--src/etc/udev/rules.d/99-vyos-systemd.rules79
-rwxr-xr-xsrc/helpers/strip-private.py4
-rwxr-xr-xsrc/migration-scripts/dns-dynamic/1-to-270
-rwxr-xr-xsrc/validators/accel-radius-dictionary13
-rwxr-xr-xsrc/validators/ddclient-protocol2
56 files changed, 836 insertions, 1098 deletions
diff --git a/data/config-mode-dependencies/vyos-1x.json b/data/config-mode-dependencies/vyos-1x.json
index 4d73c844c..918fb0f17 100644
--- a/data/config-mode-dependencies/vyos-1x.json
+++ b/data/config-mode-dependencies/vyos-1x.json
@@ -9,6 +9,9 @@
"interfaces_bonding": {
"ethernet": ["interfaces-ethernet"]
},
+ "interfaces_bridge": {
+ "vxlan": ["interfaces-vxlan"]
+ },
"load_balancing_wan": {
"conntrack": ["conntrack"],
"conntrack_sync": ["conntrack_sync"]
@@ -48,8 +51,5 @@
"wireguard": ["interfaces-wireguard"],
"wireless": ["interfaces-wireless"],
"wwan": ["interfaces-wwan"]
- },
- "vpp": {
- "ethernet": ["interfaces-ethernet"]
}
}
diff --git a/data/templates/accel-ppp/config_shaper_radius.j2 b/data/templates/accel-ppp/config_shaper_radius.j2
index 0cf6a6a92..fcd68f69e 100644
--- a/data/templates/accel-ppp/config_shaper_radius.j2
+++ b/data/templates/accel-ppp/config_shaper_radius.j2
@@ -1,6 +1,7 @@
{% if authentication.mode is vyos_defined('radius') or shaper is vyos_defined %}
[shaper]
verbose=1
+down-limiter=tbf
{% if authentication.radius.rate_limit.enable is vyos_defined %}
attr={{ authentication.radius.rate_limit.attribute }}
{% if authentication.radius.rate_limit.vendor is vyos_defined %}
@@ -13,7 +14,6 @@ rate-multiplier={{ authentication.radius.rate_limit.multiplier }}
{% if shaper is vyos_defined %}
{% if shaper.fwmark is vyos_defined %}
fwmark={{ shaper.fwmark }}
-down-limiter=htb
{% endif %}
{% endif %}
{% endif %} \ No newline at end of file
diff --git a/data/templates/accel-ppp/ipoe.config.j2 b/data/templates/accel-ppp/ipoe.config.j2
index f59428509..555a033d3 100644
--- a/data/templates/accel-ppp/ipoe.config.j2
+++ b/data/templates/accel-ppp/ipoe.config.j2
@@ -14,6 +14,11 @@ ippool
[core]
thread-count={{ thread_count }}
+[common]
+{% if max_concurrent_sessions is vyos_defined %}
+max-starting={{ max_concurrent_sessions }}
+{% endif %}
+
[log]
syslog=accel-ipoe,daemon
copy=1
diff --git a/data/templates/accel-ppp/l2tp.config.j2 b/data/templates/accel-ppp/l2tp.config.j2
index a2f9c9fc7..b089d3e71 100644
--- a/data/templates/accel-ppp/l2tp.config.j2
+++ b/data/templates/accel-ppp/l2tp.config.j2
@@ -20,6 +20,11 @@ ipv6_dhcp
[core]
thread-count={{ thread_cnt }}
+[common]
+{% if max_concurrent_sessions is vyos_defined %}
+max-starting={{ max_concurrent_sessions }}
+{% endif %}
+
[log]
syslog=accel-l2tp,daemon
copy=1
diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2
index dd53edd28..e1ae3660e 100644
--- a/data/templates/accel-ppp/pppoe.config.j2
+++ b/data/templates/accel-ppp/pppoe.config.j2
@@ -62,10 +62,13 @@ wins{{ loop.index }}={{ server }}
{# Common chap-secrets and RADIUS server/option definitions #}
{% include 'accel-ppp/config_chap_secrets_radius.j2' %}
-{% if session_control is vyos_defined and session_control is not vyos_defined('disable') %}
[common]
+{% if session_control is vyos_defined and session_control is not vyos_defined('disable') %}
single-session={{ session_control }}
{% endif %}
+{% if max_concurrent_sessions is vyos_defined %}
+max-starting={{ max_concurrent_sessions }}
+{% endif %}
[ppp]
verbose=1
diff --git a/data/templates/accel-ppp/pptp.config.j2 b/data/templates/accel-ppp/pptp.config.j2
index 0082e55bf..46a9f933a 100644
--- a/data/templates/accel-ppp/pptp.config.j2
+++ b/data/templates/accel-ppp/pptp.config.j2
@@ -16,6 +16,11 @@ ippool
[core]
thread-count={{ thread_cnt }}
+[common]
+{% if max_concurrent_sessions is vyos_defined %}
+max-starting={{ max_concurrent_sessions }}
+{% endif %}
+
[log]
syslog=accel-pptp,daemon
copy=1
diff --git a/data/templates/accel-ppp/sstp.config.j2 b/data/templates/accel-ppp/sstp.config.j2
index 7ee28dd21..cf1d23f54 100644
--- a/data/templates/accel-ppp/sstp.config.j2
+++ b/data/templates/accel-ppp/sstp.config.j2
@@ -16,6 +16,9 @@ thread-count={{ thread_count }}
[common]
single-session=replace
+{% if max_concurrent_sessions is vyos_defined %}
+max-starting={{ max_concurrent_sessions }}
+{% endif %}
[log]
syslog=accel-sstp,daemon
diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2
index 6e77abdb5..879887a1f 100644
--- a/data/templates/dns-dynamic/ddclient.conf.j2
+++ b/data/templates/dns-dynamic/ddclient.conf.j2
@@ -21,7 +21,7 @@ if{{ ipv }}={{ address }}, \
{{ host }}
{% endmacro %}
### Autogenerated by dns_dynamic.py ###
-daemon={{ timeout }}
+daemon={{ interval }}
syslog=yes
ssl=yes
pid={{ config_file | replace('.conf', '.pid') }}
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index d724dbd79..6f81174ac 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -373,6 +373,26 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if afi_config.advertise_svi_ip is vyos_defined %}
advertise-svi-ip
{% endif %}
+{% if afi_config.default_originate.ipv4 is vyos_defined %}
+ default-originate ipv4
+{% endif %}
+{% if afi_config.default_originate.ipv6 is vyos_defined %}
+ default-originate ipv6
+{% endif %}
+{% if afi_config.disable_ead_evi_rx is vyos_defined %}
+ disable-ead-evi-rx
+{% endif %}
+{% if afi_config.disable_ead_evi_tx is vyos_defined %}
+ disable-ead-evi-tx
+{% endif %}
+{% if afi_config.ead_es_frag.evi_limit is vyos_defined %}
+ ead-es-frag evi-limit {{ afi_config.ead_es_frag.evi_limit }}
+{% endif %}
+{% if afi_config.ead_es_route_target.export is vyos_defined %}
+{% for route_target in afi_config.ead_es_route_target.export %}
+ ead-es-route-target export {{ route_target }}
+{% endfor %}
+{% endif %}
{% if afi_config.rt_auto_derive is vyos_defined %}
autort rfc8365-compatible
{% endif %}
diff --git a/data/templates/frr/evpn.mh.frr.j2 b/data/templates/frr/evpn.mh.frr.j2
new file mode 100644
index 000000000..03aaac44b
--- /dev/null
+++ b/data/templates/frr/evpn.mh.frr.j2
@@ -0,0 +1,16 @@
+!
+interface {{ ifname }}
+{% if evpn.es_df_pref is vyos_defined %}
+ evpn mh es-df-pref {{ evpn.es_df_pref }}
+{% endif %}
+{% if evpn.es_id is vyos_defined %}
+ evpn mh es-id {{ evpn.es_id }}
+{% endif %}
+{% if evpn.es_sys_mac is vyos_defined %}
+ evpn mh es-sys-mac {{ evpn.es_sys_mac }}
+{% endif %}
+{% if evpn.uplink is vyos_defined %}
+ evpn mh uplink
+{% endif %}
+exit
+!
diff --git a/data/templates/rsyslog/rsyslog.conf.j2 b/data/templates/rsyslog/rsyslog.conf.j2
index dff904129..8ca167803 100644
--- a/data/templates/rsyslog/rsyslog.conf.j2
+++ b/data/templates/rsyslog/rsyslog.conf.j2
@@ -15,7 +15,7 @@ $outchannel global,/var/log/messages,262144,/usr/sbin/logrotate {{ logrotate }}
{% if global.facility is vyos_defined %}
{% set tmp = [] %}
{% for facility, facility_options in global.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level.replace('all', '*')) %}
{% endfor %}
{{ tmp | join(';') }} :omfile:$global
{% endif %}
@@ -27,7 +27,7 @@ $outchannel global,/var/log/messages,262144,/usr/sbin/logrotate {{ logrotate }}
$outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archive.size }},/usr/sbin/logrotate {{ logrotate }}
{% if file_options.facility is vyos_defined %}
{% for facility, facility_options in file_options.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level.replace('all', '*')) %}
{% endfor %}
{% endif %}
{{ tmp | join(';') }} :omfile:${{ file }}
@@ -38,7 +38,7 @@ $outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archiv
# Console logging
{% set tmp = [] %}
{% for facility, facility_options in console.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level.replace('all', '*')) %}
{% endfor %}
{{ tmp | join(';') }} /dev/console
{% endif %}
@@ -49,7 +49,7 @@ $outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archiv
{% set tmp = [] %}
{% if host_options.facility is vyos_defined %}
{% for facility, facility_options in host_options.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level.replace('all', '*')) %}
{% endfor %}
{% endif %}
{% if host_options.protocol is vyos_defined('tcp') %}
@@ -70,7 +70,7 @@ $outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archiv
{% set tmp = [] %}
{% if user_options.facility is vyos_defined %}
{% for facility, facility_options in user_options.facility.items() %}
-{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %}
+{% set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level.replace('all', '*')) %}
{% endfor %}
{% endif %}
{{ tmp | join(';') }} :omusrmsg:{{ username }}
diff --git a/data/templates/snmp/etc.snmpd.conf.j2 b/data/templates/snmp/etc.snmpd.conf.j2
index 9d78d479a..3db8c4d7b 100644
--- a/data/templates/snmp/etc.snmpd.conf.j2
+++ b/data/templates/snmp/etc.snmpd.conf.j2
@@ -56,6 +56,16 @@ SysDescr {{ description }}
{% endif %}
agentaddress unix:/run/snmpd.socket{{ ',' ~ options | join(',') if options is vyos_defined }}
+{% if mib is vyos_defined %}
+# Interface MIB limits
+{% if mib.interface_max is vyos_defined %}
+ifmib_max_num_ifaces {{ mib.interface_max }}
+{% endif %}
+{% if mib.interface is vyos_defined %}
+include_ifmib_iface_prefix {{ mib.interface | join(' ') }}
+{% endif %}
+{% endif %}
+
# SNMP communities
{% if community is vyos_defined %}
{% for comm, comm_config in community.items() %}
diff --git a/data/templates/telegraf/telegraf.j2 b/data/templates/telegraf/telegraf.j2
index 5852d6232..02a9656da 100644
--- a/data/templates/telegraf/telegraf.j2
+++ b/data/templates/telegraf/telegraf.j2
@@ -89,7 +89,7 @@
ignore_fs = ["devtmpfs", "devfs"]
[[inputs.diskio]]
[[inputs.mem]]
-[[inputs.net]]
+[[inputs.nstat]]
[[inputs.system]]
[[inputs.netstat]]
[[inputs.processes]]
diff --git a/data/templates/vpp/override.conf.j2 b/data/templates/vpp/override.conf.j2
deleted file mode 100644
index a2c2b04ed..000000000
--- a/data/templates/vpp/override.conf.j2
+++ /dev/null
@@ -1,14 +0,0 @@
-[Unit]
-After=
-After=vyos-router.service
-ConditionPathExists=
-ConditionPathExists=/run/vpp/vpp.conf
-
-[Service]
-EnvironmentFile=
-ExecStart=
-ExecStart=/usr/bin/vpp -c /run/vpp/vpp.conf
-WorkingDirectory=
-WorkingDirectory=/run/vpp
-Restart=always
-RestartSec=10
diff --git a/data/templates/vpp/startup.conf.j2 b/data/templates/vpp/startup.conf.j2
deleted file mode 100644
index f33539fba..000000000
--- a/data/templates/vpp/startup.conf.j2
+++ /dev/null
@@ -1,116 +0,0 @@
-# Generated by /usr/libexec/vyos/conf_mode/vpp.py
-
-unix {
- nodaemon
- log /var/log/vpp.log
- full-coredump
- cli-listen /run/vpp/cli.sock
- gid vpp
- # exec /etc/vpp/bootstrap.vpp
-{% if unix is vyos_defined %}
-{% if unix.poll_sleep_usec is vyos_defined %}
- poll-sleep-usec {{ unix.poll_sleep_usec }}
-{% endif %}
-{% endif %}
-}
-
-{% if cpu is vyos_defined %}
-cpu {
-{% if cpu.main_core is vyos_defined %}
- main-core {{ cpu.main_core }}
-{% endif %}
-{% if cpu.corelist_workers is vyos_defined %}
- corelist-workers {{ cpu.corelist_workers | join(',') }}
-{% endif %}
-{% if cpu.skip_cores is vyos_defined %}
- skip-cores {{ cpu.skip_cores }}
-{% endif %}
-{% if cpu.workers is vyos_defined %}
- workers {{ cpu.workers }}
-{% endif %}
-}
-{% endif %}
-
-{# ip heap-size does not work now (23.06-rc2~1-g3a4e62ad4) #}
-{# vlib_call_all_config_functions: unknown input `ip heap-size 32M ' #}
-{% if ip is vyos_defined %}
-#ip {
-#{% if ip.heap_size is vyos_defined %}
-# heap-size {{ ip.heap_size }}M
-#{% endif %}
-#}
-{% endif %}
-
-{% if ip6 is vyos_defined %}
-ip6 {
-{% if ip6.hash_buckets is vyos_defined %}
- hash-buckets {{ ip6.hash_buckets }}
-{% endif %}
-{% if ip6.heap_size is vyos_defined %}
- heap-size {{ ip6.heap_size }}M
-{% endif %}
-}
-{% endif %}
-
-{% if l2learn is vyos_defined %}
-l2learn {
-{% if l2learn.limit is vyos_defined %}
- limit {{ l2learn.limit }}
-{% endif %}
-}
-{% endif %}
-
-{% if logging is vyos_defined %}
-logging {
-{% if logging.default_log_level is vyos_defined %}
- default-log-level {{ logging.default_log_level }}
-{% endif %}
-}
-{% endif %}
-
-{% if physmem is vyos_defined %}
-physmem {
-{% if physmem.max_size is vyos_defined %}
- max-size {{ physmem.max_size.upper() }}
-{% endif %}
-}
-{% endif %}
-
-plugins {
- path /usr/lib/x86_64-linux-gnu/vpp_plugins/
- plugin default { disable }
- plugin dpdk_plugin.so { enable }
- plugin linux_cp_plugin.so { enable }
- plugin linux_nl_plugin.so { enable }
-}
-
-linux-cp {
- lcp-sync
- lcp-auto-subint
-}
-
-dpdk {
- # Whitelist the fake PCI address 0000:00:00.0
- # This prevents all devices from being added to VPP-DPDK by default
- dev 0000:00:00.0
-{% for iface, iface_config in interface.items() %}
-{% if iface_config.pci is vyos_defined %}
- dev {{ iface_config.pci }} {
- name {{ iface }}
-{% if iface_config.num_rx_desc is vyos_defined %}
- num-rx-desc {{ iface_config.num_rx_desc }}
-{% endif %}
-{% if iface_config.num_tx_desc is vyos_defined %}
- num-tx-desc {{ iface_config.num_tx_desc }}
-{% endif %}
-{% if iface_config.num_rx_queues is vyos_defined %}
- num-rx-queues {{ iface_config.num_rx_queues }}
-{% endif %}
-{% if iface_config.num_tx_queues is vyos_defined %}
- num-tx-queues {{ iface_config.num_tx_queues }}
-{% endif %}
- }
-{% endif %}
-{% endfor %}
- uio-bind-force
-}
diff --git a/debian/control b/debian/control
index 32de13f1b..d2ed3991a 100644
--- a/debian/control
+++ b/debian/control
@@ -155,7 +155,7 @@ Depends:
# For "set service aws glb"
aws-gwlbtun,
# For "service dns dynamic"
- ddclient (>= 3.9.1),
+ ddclient (>= 3.11.1),
# End "service dns dynamic"
# # For "service ids"
fastnetmon [amd64],
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 860319edf..22b50ce2a 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -201,14 +201,3 @@ systemctl enable vyos-config-cloud-init.service
# 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:
-#
-# 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/debian/vyos-1x.preinst b/debian/vyos-1x.preinst
index 9bd6331a8..08f48cac2 100644
--- a/debian/vyos-1x.preinst
+++ b/debian/vyos-1x.preinst
@@ -9,3 +9,4 @@ dpkg-divert --package vyos-1x --add --no-rename /etc/sysctl.d/80-vpp.conf
dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplugd.conf
dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplug
dpkg-divert --package vyos-1x --add --no-rename /etc/rsyslog.d/45-frr.conf
+dpkg-divert --package vyos-1x --add --no-rename /lib/udev/rules.d/99-systemd.rules
diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in
index 723223f1c..07b1bf1b8 100644
--- a/interface-definitions/dns-dynamic.xml.in
+++ b/interface-definitions/dns-dynamic.xml.in
@@ -134,9 +134,9 @@
</tagNode>
</children>
</tagNode>
- <leafNode name="timeout">
+ <leafNode name="interval">
<properties>
- <help>Time in seconds to wait between DNS updates</help>
+ <help>Interval in seconds to wait between Dynamic DNS updates</help>
<valueHelp>
<format>u32:60-3600</format>
<description>Time in seconds</description>
@@ -144,7 +144,7 @@
<constraint>
<validator name="numeric" argument="--range 60-3600"/>
</constraint>
- <constraintErrorMessage>Timeout must be between 60 and 3600 seconds</constraintErrorMessage>
+ <constraintErrorMessage>Interval must be between 60 and 3600 seconds</constraintErrorMessage>
</properties>
<defaultValue>300</defaultValue>
</leafNode>
diff --git a/interface-definitions/include/accel-ppp/max-concurrent-sessions.xml.i b/interface-definitions/include/accel-ppp/max-concurrent-sessions.xml.i
new file mode 100644
index 000000000..f6ef41019
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/max-concurrent-sessions.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from accel-ppp/max-concurrent-sessions.xml.i -->
+<leafNode name="max-concurrent-sessions">
+ <properties>
+ <help>Maximum number of concurrent session start attempts</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>Maximum number of concurrent session start attempts</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-65535"/>
+ </constraint>
+ <constraintErrorMessage>Maximum concurent sessions must be in range 0-65535</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
index b8dbe73b2..c0367b891 100644
--- a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
@@ -13,12 +13,6 @@
<leafNode name="vendor">
<properties>
<help>Vendor dictionary</help>
- <completionHelp>
- <list>alcatel cisco microsoft mikrotik</list>
- </completionHelp>
- <constraint>
- <validator name="accel-radius-dictionary" />
- </constraint>
</properties>
</leafNode>
<leafNode name="enable">
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 3d9333639..4e43298bc 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -806,6 +806,76 @@
<valueless/>
</properties>
</leafNode>
+ <node name="default-originate">
+ <properties>
+ <help>Originate a default route</help>
+ </properties>
+ <children>
+ <leafNode name="ipv4">
+ <properties>
+ <help>IPv4 address family</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ipv6">
+ <properties>
+ <help>IPv6 address family</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-ead-evi-rx">
+ <properties>
+ <help>Activate PE on EAD-ES even if EAD-EVI is not received</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable-ead-evi-tx">
+ <properties>
+ <help>Do not advertise EAD-EVI for local ESs</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="ead-es-frag">
+ <properties>
+ <help>EAD ES fragment config</help>
+ </properties>
+ <children>
+ <leafNode name="evi-limit">
+ <properties>
+ <help>EVIs per-fragment</help>
+ <valueHelp>
+ <format>u32:1-1000</format>
+ <description>limit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-1000"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="ead-es-route-target">
+ <properties>
+ <help>EAD ES Route Target</help>
+ </properties>
+ <children>
+ <leafNode name="export">
+ <properties>
+ <help>Route Target export</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="bgp-rd-rt" argument="--route-target-multi"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<node name="flooding">
<properties>
<help>Specify handling for BUM packets</help>
diff --git a/interface-definitions/include/version/dns-dynamic-version.xml.i b/interface-definitions/include/version/dns-dynamic-version.xml.i
index b25fc6e76..7bdb90a35 100644
--- a/interface-definitions/include/version/dns-dynamic-version.xml.i
+++ b/interface-definitions/include/version/dns-dynamic-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/dns-dynamic-version.xml.i -->
-<syntaxVersion component='dns-dynamic' version='1'></syntaxVersion>
+<syntaxVersion component='dns-dynamic' version='2'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/nat-version.xml.i b/interface-definitions/include/version/nat-version.xml.i
index 027216a07..656da6e14 100644
--- a/interface-definitions/include/version/nat-version.xml.i
+++ b/interface-definitions/include/version/nat-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/nat-version.xml.i -->
-<syntaxVersion component='nat' version='5'></syntaxVersion>
+<syntaxVersion component='nat' version='7'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/nat66-version.xml.i b/interface-definitions/include/version/nat66-version.xml.i
index 7b7123dcc..478ca080f 100644
--- a/interface-definitions/include/version/nat66-version.xml.i
+++ b/interface-definitions/include/version/nat66-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/nat66-version.xml.i -->
-<syntaxVersion component='nat66' version='1'></syntaxVersion>
+<syntaxVersion component='nat66' version='2'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 427e04a54..86c4776b6 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -56,6 +56,60 @@
#include <include/interface/disable.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/interface/mirror.xml.i>
+ <node name="evpn">
+ <properties>
+ <help>EVPN Multihoming</help>
+ </properties>
+ <children>
+ <leafNode name="es-df-pref">
+ <properties>
+ <help>Preference value used for designated forwarder (DF) election</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>DF Preference value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="es-id">
+ <properties>
+ <help>Ethernet segment identifier</help>
+ <valueHelp>
+ <format>u32:1-16777215</format>
+ <description>Local discriminator</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>10-byte ID - 00:11:22:33:44:55:AA:BB:CC:DD</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ <regex>([0-9A-Fa-f][0-9A-Fa-f]:){9}[0-9A-Fa-f][0-9A-Fa-f]</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="es-sys-mac">
+ <properties>
+ <help>Ethernet segment system MAC</help>
+ <valueHelp>
+ <format>macaddr</format>
+ <description>MAC address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="uplink">
+ <properties>
+ <help>Uplink to the VXLAN core</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="hash-policy">
<properties>
<help>Bonding transmit hash policy</help>
diff --git a/interface-definitions/service-ipoe-server.xml.in b/interface-definitions/service-ipoe-server.xml.in
index b6e6503d3..9ac0c8fdf 100644
--- a/interface-definitions/service-ipoe-server.xml.in
+++ b/interface-definitions/service-ipoe-server.xml.in
@@ -102,6 +102,7 @@
#include <include/accel-ppp/vlan.xml.i>
</children>
</tagNode>
+ #include <include/accel-ppp/max-concurrent-sessions.xml.i>
#include <include/name-server-ipv4-ipv6.xml.i>
<node name="client-ip-pool">
<properties>
diff --git a/interface-definitions/service-pppoe-server.xml.in b/interface-definitions/service-pppoe-server.xml.in
index 022ac2885..44b689fe1 100644
--- a/interface-definitions/service-pppoe-server.xml.in
+++ b/interface-definitions/service-pppoe-server.xml.in
@@ -73,6 +73,7 @@
</children>
</tagNode>
#include <include/accel-ppp/gateway-address.xml.i>
+ #include <include/accel-ppp/max-concurrent-sessions.xml.i>
#include <include/accel-ppp/mtu-128-16384.xml.i>
<node name="limits">
<properties>
diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in
index 0851b8389..ec2151b98 100644
--- a/interface-definitions/snmp.xml.in
+++ b/interface-definitions/snmp.xml.in
@@ -79,6 +79,101 @@
</properties>
</leafNode>
#include <include/generic-description.xml.i>
+ <node name="mib">
+ <properties>
+ <help>Management information base (MIB)</help>
+ </properties>
+ <children>
+ <leafNode name="interface-max">
+ <properties>
+ <help>Sets the maximum number of interfaces included in IF-MIB data collection</help>
+ <valueHelp>
+ <format>u32:1-4294967295</format>
+ <description>Sets the maximum number of interfaces included in IF-MIB data collection</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="interface">
+ <properties>
+ <help>Sets the interface name prefix to include in the IF-MIB data collection</help>
+ <completionHelp>
+ <list>br bond dum eth gnv macsec peth sstpc tun veth vti vtun vxlan wg wlan wwan</list>
+ </completionHelp>
+ <valueHelp>
+ <format>br</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>bond</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dum</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>eth</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>gnv</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>macsec</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>peth</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sstpc</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tun</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>veth</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>vti</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>vtun</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>vxlan</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>wg</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>wlan</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>wwan</format>
+ <description>Allow prefix for IF-MIB data collection</description>
+ </valueHelp>
+ <constraint>
+ <regex>(br|bond|dum|eth|gnv|macsec|peth|sstpc|tun|veth|vti|vtun|vxlan|wg|wlan|wwan)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<tagNode name="listen-address">
<properties>
<help>IP address to listen for incoming SNMP requests</help>
diff --git a/interface-definitions/vpn-l2tp.xml.in b/interface-definitions/vpn-l2tp.xml.in
index ee0edc3e3..60a1d323b 100644
--- a/interface-definitions/vpn-l2tp.xml.in
+++ b/interface-definitions/vpn-l2tp.xml.in
@@ -13,6 +13,7 @@
<help>Remote access L2TP VPN</help>
</properties>
<children>
+ #include <include/accel-ppp/max-concurrent-sessions.xml.i>
#include <include/accel-ppp/mtu-128-16384.xml.i>
<leafNode name="outside-address">
<properties>
diff --git a/interface-definitions/vpn-pptp.xml.in b/interface-definitions/vpn-pptp.xml.in
index 5a8b4a78a..964c4d21e 100644
--- a/interface-definitions/vpn-pptp.xml.in
+++ b/interface-definitions/vpn-pptp.xml.in
@@ -13,6 +13,7 @@
<help>Remote access PPTP VPN</help>
</properties>
<children>
+ #include <include/accel-ppp/max-concurrent-sessions.xml.i>
#include <include/accel-ppp/mtu-128-16384.xml.i>
<leafNode name="outside-address">
<properties>
diff --git a/interface-definitions/vpn-sstp.xml.in b/interface-definitions/vpn-sstp.xml.in
index 9e912063f..9c818ba60 100644
--- a/interface-definitions/vpn-sstp.xml.in
+++ b/interface-definitions/vpn-sstp.xml.in
@@ -25,6 +25,7 @@
</node>
</children>
</node>
+ #include <include/accel-ppp/max-concurrent-sessions.xml.i>
#include <include/interface/mtu-68-1500.xml.i>
#include <include/accel-ppp/gateway-address.xml.i>
#include <include/name-server-ipv4-ipv6.xml.i>
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in
deleted file mode 100644
index 3f0758c0a..000000000
--- a/interface-definitions/vpp.xml.in
+++ /dev/null
@@ -1,342 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="vpp" owner="${vyos_conf_scripts_dir}/vpp.py">
- <properties>
- <help>Accelerated data-plane</help>
- <priority>295</priority>
- </properties>
- <children>
- <node name="cpu">
- <properties>
- <help>CPU settings</help>
- </properties>
- <children>
- <leafNode name="corelist-workers">
- <properties>
- <help>List of cores worker threads</help>
- <valueHelp>
- <format>&lt;id&gt;</format>
- <description>CPU core id</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;idN&gt;-&lt;idM&gt;</format>
- <description>CPU core id range (use '-' as delimiter)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--allow-range --range 0-512"/>
- </constraint>
- <constraintErrorMessage>not a valid CPU core value or range</constraintErrorMessage>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="main-core">
- <properties>
- <help>Main core</help>
- <valueHelp>
- <format>u32:0-512</format>
- <description>Assign main thread to specific core</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-512"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="skip-cores">
- <properties>
- <help>Skip cores</help>
- <valueHelp>
- <format>u32:0-512</format>
- <description>Skip cores</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-512"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="workers">
- <properties>
- <help>Create worker threads</help>
- <valueHelp>
- <format>u32:0-4294967295</format>
- <description>Worker threads</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-512"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <tagNode name="interface">
- <properties>
- <help>Interface</help>
- <valueHelp>
- <format>ethN</format>
- <description>Interface name</description>
- </valueHelp>
- <constraint>
- <regex>((eth|lan)[0-9]+|(eno|ens|enp|enx).+)</regex>
- </constraint>
- <constraintErrorMessage>Invalid interface name</constraintErrorMessage>
- </properties>
- <children>
- <leafNode name="num-rx-desc">
- <properties>
- <help>Number of receive ring descriptors</help>
- <valueHelp>
- <format>u32:256-8192</format>
- <description>Number of receive ring descriptors</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 256-8192"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="num-tx-desc">
- <properties>
- <help>Number of tranceive ring descriptors</help>
- <valueHelp>
- <format>u32:256-8192</format>
- <description>Number of tranceive ring descriptors</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 256-8192"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="num-rx-queues">
- <properties>
- <help>Number of receive ring descriptors</help>
- <valueHelp>
- <format>u32:256-8192</format>
- <description>Number of receive queues</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 256-8192"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="num-tx-queues">
- <properties>
- <help>Number of tranceive ring descriptors</help>
- <valueHelp>
- <format>u32:256-8192</format>
- <description>Number of tranceive queues</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 256-8192"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name='pci'>
- <properties>
- <help>PCI address allocation</help>
- <valueHelp>
- <format>auto</format>
- <description>Auto detect PCI address</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;xxxx:xx:xx.x&gt;</format>
- <description>Set Peripheral Component Interconnect (PCI) address</description>
- </valueHelp>
- <constraint>
- <regex>(auto|[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])</regex>
- </constraint>
- </properties>
- <defaultValue>auto</defaultValue>
- </leafNode>
- <leafNode name="rx-mode">
- <properties>
- <help>Receive packet processing mode</help>
- <completionHelp>
- <list>polling interrupt adaptive</list>
- </completionHelp>
- <valueHelp>
- <format>polling</format>
- <description>Constantly check for new data</description>
- </valueHelp>
- <valueHelp>
- <format>interrupt</format>
- <description>Interrupt mode</description>
- </valueHelp>
- <valueHelp>
- <format>adaptive</format>
- <description>Adaptive mode</description>
- </valueHelp>
- <constraint>
- <regex>(polling|interrupt|adaptive)</regex>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </tagNode>
- <node name="ip">
- <properties>
- <help>IP settings</help>
- </properties>
- <children>
- <leafNode name="heap-size">
- <properties>
- <help>IPv4 heap size</help>
- <valueHelp>
- <format>u32:0-4294967295</format>
- <description>Amount of memory (in Mbytes) dedicated to the destination IP lookup table</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4294967295"/>
- </constraint>
- </properties>
- <defaultValue>32</defaultValue>
- </leafNode>
- </children>
- </node>
- <node name="ip6">
- <properties>
- <help>IPv6 settings</help>
- </properties>
- <children>
- <leafNode name="heap-size">
- <properties>
- <help>IPv6 heap size</help>
- <valueHelp>
- <format>u32:0-4294967295</format>
- <description>Amount of memory (in Mbytes) dedicated to the destination IP lookup table</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4294967295"/>
- </constraint>
- </properties>
- <defaultValue>32</defaultValue>
- </leafNode>
- <leafNode name="hash-buckets">
- <properties>
- <help>IPv6 forwarding table hash buckets</help>
- <valueHelp>
- <format>u32:1-4294967295</format>
- <description>IPv6 forwarding table hash buckets</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4294967295"/>
- </constraint>
- </properties>
- <defaultValue>65536</defaultValue>
- </leafNode>
- </children>
- </node>
- <node name="l2learn">
- <properties>
- <help>Level 2 MAC address learning settings</help>
- </properties>
- <children>
- <leafNode name="limit">
- <properties>
- <help>Number of MAC addresses in the L2 FIB</help>
- <valueHelp>
- <format>u32:1-4294967295</format>
- <description>Number of concurent entries</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4294967295"/>
- </constraint>
- </properties>
- <defaultValue>4194304</defaultValue>
- </leafNode>
- </children>
- </node>
- <node name="logging">
- <properties>
- <help>Loggint settings</help>
- </properties>
- <children>
- <leafNode name="default-log-level">
- <properties>
- <help>default-log-level</help>
- <completionHelp>
- <list>alert crit debug disabled emerg err info notice warn</list>
- </completionHelp>
- <valueHelp>
- <format>alert</format>
- <description>Alert</description>
- </valueHelp>
- <valueHelp>
- <format>crit</format>
- <description>Critical</description>
- </valueHelp>
- <valueHelp>
- <format>debug</format>
- <description>Debug</description>
- </valueHelp>
- <valueHelp>
- <format>disabled</format>
- <description>Disabled</description>
- </valueHelp>
- <valueHelp>
- <format>emerg</format>
- <description>Emergency</description>
- </valueHelp>
- <valueHelp>
- <format>err</format>
- <description>Error</description>
- </valueHelp>
- <valueHelp>
- <format>info</format>
- <description>Informational</description>
- </valueHelp>
- <valueHelp>
- <format>notice</format>
- <description>Notice</description>
- </valueHelp>
- <valueHelp>
- <format>warn</format>
- <description>Warning</description>
- </valueHelp>
- <constraint>
- <regex>(alert|crit|debug|disabled|emerg|err|info|notice|warn)</regex>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="physmem">
- <properties>
- <help>Memory settings</help>
- </properties>
- <children>
- <leafNode name="max-size">
- <properties>
- <help>Set memory size for protectable memory allocator (pmalloc) memory space</help>
- <valueHelp>
- <format>&lt;number&gt;m</format>
- <description>Megabyte</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;number&gt;g</format>
- <description>Gigabyte</description>
- </valueHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="unix">
- <properties>
- <help>Unix settings</help>
- </properties>
- <children>
- <leafNode name="poll-sleep-usec">
- <properties>
- <help>Add a fixed-sleep between main loop poll</help>
- <valueHelp>
- <format>u32:0-4294967295</format>
- <description>Number of receive queues</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-4294967295"/>
- </constraint>
- </properties>
- <defaultValue>0</defaultValue>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/op-mode-definitions/include/bgp/show-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-bgp-common.xml.i
index de794a879..d888bc3b0 100644
--- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i
+++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i
@@ -107,6 +107,12 @@
#include <include/vni-tagnode.xml.i>
</children>
</node>
+ <leafNode name="es-vrf">
+ <properties>
+ <help>Ethernet Segment per VRF</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
<leafNode name="import-rt">
<properties>
<help>Show import route target</help>
@@ -136,11 +142,17 @@
</leafNode>
</children>
</tagNode>
+ <leafNode name="next-hops">
+ <properties>
+ <help>EVPN Nexthops</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
<tagNode name="rd">
<properties>
- <help>Show detailed BGP neighbor information</help>
+ <help>Display information for a route distinguisher</help>
<completionHelp>
- <list>ASN:NN IPADDRESS:NN</list>
+ <list>ASN:NN IPADDRESS:NN all</list>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
diff --git a/op-mode-definitions/mdns-reflector.xml.in b/op-mode-definitions/mdns-reflector.xml.in
new file mode 100644
index 000000000..a90d4d385
--- /dev/null
+++ b/op-mode-definitions/mdns-reflector.xml.in
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="monitor">
+ <children>
+ <node name="log">
+ <children>
+ <node name="mdns">
+ <properties>
+ <help>Monitor last lines of multicast Domain Name System related services</help>
+ </properties>
+ <children>
+ <node name="repeater">
+ <properties>
+ <help>Monitor last lines of mDNS repeater service</help>
+ </properties>
+ <command>journalctl --no-hostname --follow --boot --unit avahi-daemon.service</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="show">
+ <children>
+ <node name="log">
+ <children>
+ <node name="mdns">
+ <properties>
+ <help>Show log for multicast Domain Name System related services</help>
+ </properties>
+ <children>
+ <node name="repeater">
+ <properties>
+ <help>Show log for mDNS repeater service</help>
+ </properties>
+ <command>journalctl --no-hostname --boot --unit avahi-daemon.service</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="restart">
+ <children>
+ <node name="mdns">
+ <properties>
+ <help>Restart specific multicast Domain Name System service</help>
+ </properties>
+ <children>
+ <node name="repeater">
+ <properties>
+ <help>Restart mDNS repeater service</help>
+ </properties>
+ <command>sudo systemctl restart avahi-daemon.service</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 4aac103ec..5704f8b64 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -167,11 +167,6 @@ class WireGuardIf(Interface):
interface setup code and provide a single point of entry when workin
on any interface. """
- # remove no longer associated peers first
- if 'peer_remove' in config:
- for peer, public_key in config['peer_remove'].items():
- self._cmd(f'wg set {self.ifname} peer {public_key} remove')
-
tmp_file = NamedTemporaryFile('w')
tmp_file.write(config['private_key'])
tmp_file.flush()
diff --git a/python/vyos/vpp.py b/python/vyos/vpp.py
deleted file mode 100644
index 76e5d29c3..000000000
--- a/python/vyos/vpp.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# 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 functools import wraps
-from pathlib import Path
-from re import search as re_search, fullmatch as re_fullmatch, MULTILINE as re_M
-from subprocess import run
-from time import sleep
-
-from vpp_papi import VPPApiClient
-from vpp_papi import VPPIOError, VPPValueError
-
-
-class VPPControl:
- """Control VPP network stack
- """
-
- class _Decorators:
- """Decorators for VPPControl
- """
-
- @classmethod
- def api_call(cls, decorated_func):
- """Check if API is connected before API call
-
- Args:
- decorated_func: function to decorate
-
- Raises:
- VPPIOError: Connection to API is not established
- """
-
- @wraps(decorated_func)
- def api_safe_wrapper(cls, *args, **kwargs):
- if not cls.vpp_api_client.transport.connected:
- raise VPPIOError(2, 'VPP API is not connected')
- return decorated_func(cls, *args, **kwargs)
-
- return api_safe_wrapper
-
- @classmethod
- def check_retval(cls, decorated_func):
- """Check retval from API response
-
- Args:
- decorated_func: function to decorate
-
- Raises:
- VPPValueError: raised when retval is not 0
- """
-
- @wraps(decorated_func)
- def check_retval_wrapper(cls, *args, **kwargs):
- return_value = decorated_func(cls, *args, **kwargs)
- if not return_value.retval == 0:
- raise VPPValueError(
- f'VPP API call failed: {return_value.retval}')
- return return_value
-
- return check_retval_wrapper
-
- def __init__(self, attempts: int = 5, interval: int = 1000) -> None:
- """Create VPP API connection
-
- Args:
- attempts (int, optional): attempts to connect. Defaults to 5.
- interval (int, optional): interval between attempts in ms. Defaults to 1000.
-
- Raises:
- VPPIOError: Connection to API cannot be established
- """
- self.vpp_api_client = VPPApiClient()
- # connect with interval
- while attempts:
- try:
- attempts -= 1
- self.vpp_api_client.connect('vpp-vyos')
- break
- except (ConnectionRefusedError, FileNotFoundError) as err:
- print(f'VPP API connection timeout: {err}')
- sleep(interval / 1000)
- # raise exception if connection was not successful in the end
- if not self.vpp_api_client.transport.connected:
- raise VPPIOError(2, 'Cannot connect to VPP API')
-
- def __del__(self) -> None:
- """Disconnect from VPP API (destructor)
- """
- self.disconnect()
-
- def disconnect(self) -> None:
- """Disconnect from VPP API
- """
- if self.vpp_api_client.transport.connected:
- self.vpp_api_client.disconnect()
-
- @_Decorators.check_retval
- @_Decorators.api_call
- def cli_cmd(self, command: str):
- """Send raw CLI command
-
- Args:
- command (str): command to send
-
- Returns:
- vpp_papi.vpp_serializer.cli_inband_reply: CLI reply class
- """
- return self.vpp_api_client.api.cli_inband(cmd=command)
-
- @_Decorators.api_call
- def get_mac(self, ifname: str) -> str:
- """Find MAC address by interface name in VPP
-
- Args:
- ifname (str): interface name inside VPP
-
- Returns:
- str: MAC address
- """
- for iface in self.vpp_api_client.api.sw_interface_dump():
- if iface.interface_name == ifname:
- return iface.l2_address.mac_string
- return ''
-
- @_Decorators.api_call
- def get_sw_if_index(self, ifname: str) -> int | None:
- """Find interface index by interface name in VPP
-
- Args:
- ifname (str): interface name inside VPP
-
- Returns:
- int | None: Interface index or None (if was not fount)
- """
- for iface in self.vpp_api_client.api.sw_interface_dump():
- if iface.interface_name == ifname:
- return iface.sw_if_index
- return None
-
- @_Decorators.check_retval
- @_Decorators.api_call
- def lcp_pair_add(self, iface_name_vpp: str, iface_name_kernel: str) -> None:
- """Create LCP interface pair between VPP and kernel
-
- Args:
- iface_name_vpp (str): interface name in VPP
- iface_name_kernel (str): interface name in kernel
- """
- iface_index = self.get_sw_if_index(iface_name_vpp)
- if iface_index:
- return self.vpp_api_client.api.lcp_itf_pair_add_del(
- is_add=True,
- sw_if_index=iface_index,
- host_if_name=iface_name_kernel)
-
- @_Decorators.check_retval
- @_Decorators.api_call
- def lcp_pair_del(self, iface_name_vpp: str, iface_name_kernel: str) -> None:
- """Delete LCP interface pair between VPP and kernel
-
- Args:
- iface_name_vpp (str): interface name in VPP
- iface_name_kernel (str): interface name in kernel
- """
- iface_index = self.get_sw_if_index(iface_name_vpp)
- if iface_index:
- return self.vpp_api_client.api.lcp_itf_pair_add_del(
- is_add=False,
- sw_if_index=iface_index,
- host_if_name=iface_name_kernel)
-
- @_Decorators.check_retval
- @_Decorators.api_call
- def iface_rxmode(self, iface_name: str, rx_mode: str) -> None:
- """Set interface rx-mode in VPP
-
- Args:
- iface_name (str): interface name in VPP
- rx_mode (str): mode (polling, interrupt, adaptive)
- """
- modes_dict: dict[str, int] = {
- 'polling': 1,
- 'interrupt': 2,
- 'adaptive': 3
- }
- if rx_mode not in modes_dict:
- raise VPPValueError(f'Mode {rx_mode} is not known')
- iface_index = self.get_sw_if_index(iface_name)
- return self.vpp_api_client.api.sw_interface_set_rx_mode(
- sw_if_index=iface_index, mode=modes_dict[rx_mode])
-
- @_Decorators.api_call
- def get_pci_addr(self, ifname: str) -> str:
- """Find PCI address of interface by interface name in VPP
-
- Args:
- ifname (str): interface name inside VPP
-
- Returns:
- str: PCI address
- """
- hw_info = self.cli_cmd(f'show hardware-interfaces {ifname}').reply
-
- regex_filter = r'^\s+pci: device (?P<device>\w+:\w+) subsystem (?P<subsystem>\w+:\w+) address (?P<address>\w+:\w+:\w+\.\w+) numa (?P<numa>\w+)$'
- re_obj = re_search(regex_filter, hw_info, re_M)
-
- # return empty string if no interface or no PCI info was found
- if not hw_info or not re_obj:
- return ''
-
- address = re_obj.groupdict().get('address', '')
-
- # we need to modify address to math kernel style
- # for example: 0000:06:14.00 -> 0000:06:14.0
- address_chunks: list[str] = address.split('.')
- address_normalized: str = f'{address_chunks[0]}.{int(address_chunks[1])}'
-
- return address_normalized
-
-
-class HostControl:
- """Control Linux host
- """
-
- @staticmethod
- def pci_rescan(pci_addr: str = '') -> None:
- """Rescan PCI device by removing it and rescan PCI bus
-
- If PCI address is not defined - just rescan PCI bus
-
- Args:
- address (str, optional): PCI address of device. Defaults to ''.
- """
- if pci_addr:
- device_file = Path(f'/sys/bus/pci/devices/{pci_addr}/remove')
- if device_file.exists():
- device_file.write_text('1')
- # wait 10 seconds max until device will be removed
- attempts = 100
- while device_file.exists() and attempts:
- attempts -= 1
- sleep(0.1)
- if device_file.exists():
- raise TimeoutError(
- f'Timeout was reached for removing PCI device {pci_addr}'
- )
- else:
- raise FileNotFoundError(f'PCI device {pci_addr} does not exist')
- rescan_file = Path('/sys/bus/pci/rescan')
- rescan_file.write_text('1')
- if pci_addr:
- # wait 10 seconds max until device will be installed
- attempts = 100
- while not device_file.exists() and attempts:
- attempts -= 1
- sleep(0.1)
- if not device_file.exists():
- raise TimeoutError(
- f'Timeout was reached for installing PCI device {pci_addr}')
-
- @staticmethod
- def get_eth_name(pci_addr: str) -> str:
- """Find Ethernet interface name by PCI address
-
- Args:
- pci_addr (str): PCI address
-
- Raises:
- FileNotFoundError: no Ethernet interface was found
-
- Returns:
- str: Ethernet interface name
- """
- # find all PCI devices with eth* names
- net_devs: dict[str, str] = {}
- net_devs_dir = Path('/sys/class/net')
- regex_filter = r'^/sys/devices/pci[\w/:\.]+/(?P<pci_addr>\w+:\w+:\w+\.\w+)/[\w/:\.]+/(?P<iface_name>eth\d+)$'
- for dir in net_devs_dir.iterdir():
- real_dir: str = dir.resolve().as_posix()
- re_obj = re_fullmatch(regex_filter, real_dir)
- if re_obj:
- iface_name: str = re_obj.group('iface_name')
- iface_addr: str = re_obj.group('pci_addr')
- net_devs.update({iface_addr: iface_name})
- # match to provided PCI address and return a name if found
- if pci_addr in net_devs:
- return net_devs[pci_addr]
- # raise error if device was not found
- raise FileNotFoundError(
- f'PCI device {pci_addr} not found in ethernet interfaces')
-
- @staticmethod
- def rename_iface(name_old: str, name_new: str) -> None:
- """Rename interface
-
- Args:
- name_old (str): old name
- name_new (str): new name
- """
- rename_cmd: list[str] = [
- 'ip', 'link', 'set', name_old, 'name', name_new
- ]
- run(rename_cmd)
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index 8867cb427..419de774a 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -241,5 +241,45 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
for member in self._members:
self.assertIn(member, slaves)
+ def test_bonding_evpn_multihoming(self):
+ id = '5'
+ for interface in self._interfaces:
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_set(self._base_path + [interface, 'evpn', 'es-id', id])
+ self.cli_set(self._base_path + [interface, 'evpn', 'es-df-pref', id])
+ self.cli_set(self._base_path + [interface, 'evpn', 'es-sys-mac', f'00:12:34:56:78:0{id}'])
+ self.cli_set(self._base_path + [interface, 'evpn', 'uplink'])
+
+ id = int(id) + 1
+
+ self.cli_commit()
+
+ id = '5'
+ for interface in self._interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}', daemon='zebra')
+
+ self.assertIn(f' evpn mh es-id {id}', frrconfig)
+ self.assertIn(f' evpn mh es-df-pref {id}', frrconfig)
+ self.assertIn(f' evpn mh es-sys-mac 00:12:34:56:78:0{id}', frrconfig)
+ self.assertIn(f' evpn mh uplink', frrconfig)
+
+ id = int(id) + 1
+
+ for interface in self._interfaces:
+ self.cli_delete(self._base_path + [interface, 'evpn', 'es-id'])
+ self.cli_delete(self._base_path + [interface, 'evpn', 'es-df-pref'])
+
+ self.cli_commit()
+
+ id = '5'
+ for interface in self._interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}', daemon='zebra')
+ self.assertIn(f' evpn mh es-sys-mac 00:12:34:56:78:0{id}', frrconfig)
+ self.assertIn(f' evpn mh uplink', frrconfig)
+
+ id = int(id) + 1
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index 3a8b8ab99..17e4fc36f 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -189,7 +189,7 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
interface = 'vxlan555'
source_interface = 'eth0'
- self.cli_set(self._base_path + [interface, 'external'])
+ self.cli_set(self._base_path + [interface, 'parameters', 'external'])
self.cli_set(self._base_path + [interface, 'source-interface', source_interface])
self.cli_set(self._base_path + [interface, 'parameters', 'neighbor-suppress'])
diff --git a/smoketest/scripts/cli/test_interfaces_wireguard.py b/smoketest/scripts/cli/test_interfaces_wireguard.py
index 48c7cb6a1..4b994a659 100755
--- a/smoketest/scripts/cli/test_interfaces_wireguard.py
+++ b/smoketest/scripts/cli/test_interfaces_wireguard.py
@@ -20,6 +20,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.file import read_file
+from vyos.utils.process import cmd
base_path = ['interfaces', 'wireguard']
@@ -152,5 +153,52 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
tmp = read_file(f'/sys/class/net/{interface}/threaded')
self.assertTrue(tmp, "1")
+ def test_05_wireguard_peer_pubkey_change(self):
+ # T5707 changing WireGuard CLI public key of a peer - it's not removed
+
+ def get_peers(interface) -> list:
+ tmp = cmd(f'sudo wg show {interface} dump')
+ first_line = True
+ peers = []
+ for line in tmp.split('\n'):
+ if not line:
+ continue # Skip empty lines and last line
+ items = line.split('\t')
+ if first_line:
+ self.assertEqual(privkey, items[0])
+ first_line = False
+ else:
+ peers.append(items[0])
+ return peers
+
+
+ interface = 'wg1337'
+ port = '1337'
+ privkey = 'iJi4lb2HhkLx2KSAGOjji2alKkYsJjSPkHkrcpxgEVU='
+ pubkey_1 = 'srQ8VF6z/LDjKCzpxBzFpmaNUOeuHYzIfc2dcmoc/h4='
+ pubkey_2 = '8pbMHiQ7NECVP7F65Mb2W8+4ldGG2oaGvDSpSEsOBn8='
+
+ self.cli_set(base_path + [interface, 'address', '172.16.0.1/24'])
+ self.cli_set(base_path + [interface, 'port', port])
+ self.cli_set(base_path + [interface, 'private-key', privkey])
+
+ self.cli_set(base_path + [interface, 'peer', 'VyOS', 'public-key', pubkey_1])
+ self.cli_set(base_path + [interface, 'peer', 'VyOS', 'allowed-ips', '10.205.212.10/32'])
+
+ self.cli_commit()
+
+ peers = get_peers(interface)
+ self.assertIn(pubkey_1, peers)
+ self.assertNotIn(pubkey_2, peers)
+
+ # Now change the public key of our peer
+ self.cli_set(base_path + [interface, 'peer', 'VyOS', 'public-key', pubkey_2])
+ self.cli_commit()
+
+ # Verify config
+ peers = get_peers(interface)
+ self.assertNotIn(pubkey_1, peers)
+ self.assertIn(pubkey_2, peers)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 5e3402fa8..23e138ebe 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -728,15 +728,25 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def test_bgp_07_l2vpn_evpn(self):
vnis = ['10010', '10020', '10030']
neighbors = ['192.0.2.10', '192.0.2.20', '192.0.2.30']
+ evi_limit = '1000'
+ route_targets = ['1.1.1.1:100', '1.1.1.1:200', '1.1.1.1:300']
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-all-vni'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-svi-ip'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'flooding', 'disable'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'default-originate', 'ipv4'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'default-originate', 'ipv6'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'disable-ead-evi-rx'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'disable-ead-evi-tx'])
for vni in vnis:
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-svi-ip'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'ead-es-frag', 'evi-limit', evi_limit])
+ for route_target in route_targets:
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'ead-es-route-target', 'export', route_target])
+
# commit changes
self.cli_commit()
@@ -747,12 +757,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' advertise-all-vni', frrconfig)
self.assertIn(f' advertise-default-gw', frrconfig)
self.assertIn(f' advertise-svi-ip', frrconfig)
+ self.assertIn(f' default-originate ipv4', frrconfig)
+ self.assertIn(f' default-originate ipv6', frrconfig)
+ self.assertIn(f' disable-ead-evi-rx', frrconfig)
+ self.assertIn(f' disable-ead-evi-tx', frrconfig)
self.assertIn(f' flooding disable', frrconfig)
for vni in vnis:
vniconfig = self.getFRRconfig(f' vni {vni}')
self.assertIn(f'vni {vni}', vniconfig)
self.assertIn(f' advertise-default-gw', vniconfig)
self.assertIn(f' advertise-svi-ip', vniconfig)
+ self.assertIn(f' ead-es-frag evi-limit {evi_limit}', frrconfig)
+ for route_target in route_targets:
+ self.assertIn(f' ead-es-route-target export {route_target}', frrconfig)
+
def test_bgp_09_distance_and_flowspec(self):
distance_external = '25'
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index acabc0070..9624f823f 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.py
@@ -112,7 +112,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# IPv6 only DDNS service configuration
def test_02_dyndns_service_ipv6(self):
- timeout = '60'
+ interval = '60'
svc_path = ['address', interface, 'service', 'dynv6']
proto = 'dyndns2'
ip_version = 'ipv6'
@@ -120,7 +120,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
expiry_time_good = '3600'
expiry_time_bad = '360'
- self.cli_set(base_path + ['timeout', timeout])
+ self.cli_set(base_path + ['interval', interval])
self.cli_set(base_path + svc_path + ['ip-version', ip_version])
self.cli_set(base_path + svc_path + ['protocol', proto])
self.cli_set(base_path + svc_path + ['server', server])
@@ -140,7 +140,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
- self.assertIn(f'daemon={timeout}', ddclient_conf)
+ self.assertIn(f'daemon={interval}', ddclient_conf)
self.assertIn(f'usev6=ifv6', ddclient_conf)
self.assertIn(f'ifv6={interface}', ddclient_conf)
self.assertIn(f'protocol={proto}', ddclient_conf)
@@ -246,10 +246,11 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'{name}', ddclient_conf)
def test_06_dyndns_vrf(self):
- vrf_name = f'vyos-test-{"".join(random.choices(string.ascii_letters + string.digits, k=5))}'
+ vrf_table = "".join(random.choices(string.digits, k=5))
+ vrf_name = f'vyos-test-{vrf_table}'
svc_path = ['address', interface, 'service', 'cloudflare']
- self.cli_set(['vrf', 'name', vrf_name, 'table', '12345'])
+ self.cli_set(['vrf', 'name', vrf_name, 'table', vrf_table])
self.cli_set(base_path + ['vrf', vrf_name])
self.cli_set(base_path + svc_path + ['protocol', 'cloudflare'])
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 963784f0a..969abd3d5 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -144,7 +144,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
subnet = '172.18.0.0/24'
fwmark = '223'
- limiter = 'htb'
+ limiter = 'tbf'
self.set(['client-ip-pool', 'subnet', subnet])
diff --git a/smoketest/scripts/cli/test_system_syslog.py b/smoketest/scripts/cli/test_system_syslog.py
new file mode 100755
index 000000000..933a5704c
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_syslog.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019-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 re
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.template import is_ipv4
+from vyos.template import address_from_cidr
+from vyos.utils.process import call
+from vyos.utils.process import DEVNULL
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
+from vyos.version import get_version_data
+
+PROCESS_NAME = 'rsyslogd'
+RSYSLOG_CONF = '/etc/rsyslog.d/00-vyos.conf'
+
+base_path = ['system', 'syslog']
+
+def get_config_value(key):
+ tmp = read_file(RSYSLOG_CONF)
+ tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
+ return tmp[0]
+
+class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestRSYSLOGService, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # delete testing SYSLOG config
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Check for running process
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_syslog_basic(self):
+ host1 = '198.51.100.1'
+ host2 = '192.0.2.1'
+
+ self.cli_set(base_path + ['host', host1, 'port', '999'])
+ self.cli_set(base_path + ['host', host1, 'facility', 'all', 'level', 'all'])
+ self.cli_set(base_path + ['host', host2, 'facility', 'kern', 'level', 'err'])
+ self.cli_set(base_path + ['console', 'facility', 'all', 'level', 'warning'])
+
+
+ self.cli_commit()
+ # verify log level and facilities in config file
+ # *.warning /dev/console
+ # *.* @198.51.100.1:999
+ # kern.err @192.0.2.1:514
+ config = [get_config_value('\*.\*'), get_config_value('kern.err'), get_config_value('\*.warning')]
+ expected = ['@198.51.100.1:999', '@192.0.2.1:514', '/dev/console']
+
+ for i in range(0,3):
+ self.assertIn(expected[i], config[i])
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/completion/list_ddclient_protocols.sh b/src/completion/list_ddclient_protocols.sh
index 3b4eff4d6..c8855b5d1 100755
--- a/src/completion/list_ddclient_protocols.sh
+++ b/src/completion/list_ddclient_protocols.sh
@@ -14,4 +14,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-echo -n $(ddclient -list-protocols | grep -vE 'nsupdate|cloudns')
+echo -n $(ddclient -list-protocols | grep -vE 'nsupdate|cloudns|porkbun')
diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py
index 874c4b689..d6ef620fe 100755
--- a/src/conf_mode/dns_dynamic.py
+++ b/src/conf_mode/dns_dynamic.py
@@ -30,16 +30,21 @@ config_file = r'/run/ddclient/ddclient.conf'
systemd_override = r'/run/systemd/system/ddclient.service.d/override.conf'
# Protocols that require zone
-zone_necessary = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn']
+zone_necessary = ['cloudflare', 'digitalocean', 'godaddy', 'hetzner', 'gandi', 'nfsn']
+zone_supported = zone_necessary + ['dnsexit2', 'zoneedit1']
# Protocols that do not require username
-username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla']
+username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'digitalocean', 'dnsexit2',
+ 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla',
+ 'regfishde']
# Protocols that support TTL
-ttl_supported = ['cloudflare', 'gandi', 'hetzner', 'dnsexit', 'godaddy', 'nfsn']
+ttl_supported = ['cloudflare', 'dnsexit2', 'gandi', 'hetzner', 'godaddy', 'nfsn']
# Protocols that support both IPv4 and IPv6
-dualstack_supported = ['cloudflare', 'dyndns2', 'freedns', 'njalla']
+dualstack_supported = ['cloudflare', 'digitalocean', 'dnsexit2', 'duckdns',
+ 'dyndns2', 'easydns', 'freedns', 'hetzner', 'infomaniak',
+ 'njalla']
# dyndns2 protocol in ddclient honors dual stack for selective servers
# because of the way it is implemented in ddclient
@@ -82,34 +87,37 @@ def verify(dyndns):
f'based Dynamic DNS service on "{address}"')
# Dynamic DNS service provider - configuration validation
+ if 'web_options' in dyndns['address'][address] and address != 'web':
+ raise ConfigError(f'"web-options" is applicable only when using HTTP(S) web request to obtain the IP address')
+
+ # Dynamic DNS service provider - configuration validation
if 'service' in dyndns['address'][address]:
for service, config in dyndns['address'][address]['service'].items():
- error_msg = f'is required for Dynamic DNS service "{service}" on "{address}"'
+ error_msg_req = f'is required for Dynamic DNS service "{service}" on "{address}" with protocol "{config["protocol"]}"'
+ error_msg_uns = f'is not supported for Dynamic DNS service "{service}" on "{address}" with protocol "{config["protocol"]}"'
for field in ['host_name', 'password', 'protocol']:
if field not in config:
- raise ConfigError(f'"{field.replace("_", "-")}" {error_msg}')
+ raise ConfigError(f'"{field.replace("_", "-")}" {error_msg_req}')
if config['protocol'] in zone_necessary and 'zone' not in config:
- raise ConfigError(f'"zone" {error_msg}')
+ raise ConfigError(f'"zone" {error_msg_req}')
- if config['protocol'] not in zone_necessary and 'zone' in config:
- raise ConfigError(f'"{config["protocol"]}" does not support "zone"')
+ if config['protocol'] not in zone_supported and 'zone' in config:
+ raise ConfigError(f'"zone" {error_msg_uns}')
if config['protocol'] not in username_unnecessary and 'username' not in config:
- raise ConfigError(f'"username" {error_msg}')
+ raise ConfigError(f'"username" {error_msg_req}')
if config['protocol'] not in ttl_supported and 'ttl' in config:
- raise ConfigError(f'"{config["protocol"]}" does not support "ttl"')
+ raise ConfigError(f'"ttl" {error_msg_uns}')
if config['ip_version'] == 'both':
if config['protocol'] not in dualstack_supported:
- raise ConfigError(f'"{config["protocol"]}" does not support '
- f'both IPv4 and IPv6 at the same time')
+ raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns}')
# dyndns2 protocol in ddclient honors dual stack only for dyn.com (dyndns.org)
if config['protocol'] == 'dyndns2' and 'server' in config and config['server'] not in dyndns_dualstack_servers:
- raise ConfigError(f'"{config["protocol"]}" does not support '
- f'both IPv4 and IPv6 at the same time for "{config["server"]}"')
+ raise ConfigError(f'Both IPv4 and IPv6 at the same time {error_msg_uns} for "{config["server"]}"')
if {'wait_time', 'expiry_time'} <= config.keys() and int(config['expiry_time']) < int(config['wait_time']):
raise ConfigError(f'"expiry-time" must be greater than "wait-time"')
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 1179e3e4f..8184d8415 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -35,12 +35,14 @@ from vyos.configverify import verify_vrf
from vyos.ifconfig import BondIf
from vyos.ifconfig.ethernet import EthernetIf
from vyos.ifconfig import Section
+from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.dict import dict_to_paths_values
from vyos.configdict import has_address_configured
from vyos.configdict import has_vrf_configured
from vyos.configdep import set_dependents, call_dependents
from vyos import ConfigError
+from vyos import frr
from vyos import airbag
airbag.enable()
@@ -247,21 +249,38 @@ def verify(bond):
return None
def generate(bond):
+ bond['frr_zebra_config'] = ''
+ if 'deleted' not in bond:
+ bond['frr_zebra_config'] = render_to_string('frr/evpn.mh.frr.j2', bond)
return None
def apply(bond):
- b = BondIf(bond['ifname'])
+ ifname = bond['ifname']
+ b = BondIf(ifname)
if 'deleted' in bond:
# delete interface
b.remove()
else:
b.update(bond)
+
if dict_search('member.interface_remove', bond):
try:
call_dependents()
except ConfigError:
raise ConfigError('Error in updating ethernet interface '
'after deleting it from bond')
+
+ zebra_daemon = 'zebra'
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(f'^interface {ifname}', stop_pattern='^exit', remove_stop_mark=True)
+ if 'frr_zebra_config' in bond:
+ frr_cfg.add_before(frr.default_add_before, bond['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index c82f01e53..31508a3c5 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -28,7 +28,8 @@ from vyos.configverify import verify_vrf
from vyos.ifconfig import BridgeIf
from vyos.configdict import has_address_configured
from vyos.configdict import has_vrf_configured
-
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
from vyos.utils.dict import dict_search
from vyos import ConfigError
@@ -83,6 +84,12 @@ def get_config(config=None):
if 'enable_vlan' in bridge and tmp:
bridge['member']['interface'][interface].update({'has_vlan' : ''})
+ # When using VXLAN member interfaces that are configured for Single
+ # VXLAN Device (SVD) we need to call the VXLAN conf-mode script to re-create
+ # VLAN to VNI mappings if required
+ if interface.startswith('vxlan'):
+ set_dependents('vxlan', conf, interface)
+
# delete empty dictionary keys - no need to run code paths if nothing is there to do
if 'member' in bridge:
if 'interface' in bridge['member'] and len(bridge['member']['interface']) == 0:
@@ -159,6 +166,13 @@ def apply(bridge):
else:
br.update(bridge)
+ for interface in dict_search('member.interface', bridge) or []:
+ if interface.startswith('vxlan'):
+ try:
+ call_dependents()
+ except ConfigError:
+ raise ConfigError('Error in updating VXLAN interface after changing bridge!')
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index 122d9589a..79e5d3f44 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -51,17 +51,9 @@ def get_config(config=None):
tmp = is_node_changed(conf, base + [ifname, 'port'])
if tmp: wireguard['port_changed'] = {}
- # Determine which Wireguard peer has been removed.
- # Peers can only be removed with their public key!
- if 'peer' in wireguard:
- peer_remove = {}
- for peer, peer_config in wireguard['peer'].items():
- # T4702: If anything on a peer changes we remove the peer first and re-add it
- if is_node_changed(conf, base + [ifname, 'peer', peer]):
- if 'public_key' in peer_config:
- peer_remove = dict_merge({'peer_remove' : {peer : peer_config['public_key']}}, peer_remove)
- if peer_remove:
- wireguard.update(peer_remove)
+ # T4702: If anything on a peer changes we remove the peer first and re-add it
+ if is_node_changed(conf, base + [ifname, 'peer']):
+ wireguard.update({'rebuild_required': {}})
return wireguard
@@ -113,12 +105,21 @@ def verify(wireguard):
public_keys.append(peer['public_key'])
def apply(wireguard):
- tmp = WireGuardIf(wireguard['ifname'])
- if 'deleted' in wireguard:
- tmp.remove()
- return None
+ if 'rebuild_required' in wireguard or 'deleted' in wireguard:
+ wg = WireGuardIf(**wireguard)
+ # WireGuard only supports peer removal based on the configured public-key,
+ # by deleting the entire interface this is the shortcut instead of parsing
+ # out all peers and removing them one by one.
+ #
+ # Peer reconfiguration will always come with a short downtime while the
+ # WireGuard interface is recreated (see below)
+ wg.remove()
+
+ # Create the new interface if required
+ if 'deleted' not in wireguard:
+ wg = WireGuardIf(**wireguard)
+ wg.update(wireguard)
- tmp.update(wireguard)
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index aace267a7..87660c127 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -48,9 +48,12 @@ def get_config(config=None):
# reload-or-restart does not implemented in accel-ppp
# use this workaround until it will be implemented
# https://phabricator.accel-ppp.org/T3
- if is_node_changed(conf, base + ['client-ip-pool']) or is_node_changed(
- conf, base + ['client-ipv6-pool']):
+ conditions = [is_node_changed(conf, base + ['client-ip-pool']),
+ is_node_changed(conf, base + ['client-ipv6-pool']),
+ is_node_changed(conf, base + ['interface'])]
+ if any(conditions):
pppoe.update({'restart_required': {}})
+
return pppoe
def verify(pppoe):
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
deleted file mode 100755
index 82c2f236e..000000000
--- a/src/conf_mode/vpp.py
+++ /dev/null
@@ -1,207 +0,0 @@
-#!/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 psutil import virtual_memory
-
-from pathlib import Path
-from re import search as re_search, MULTILINE as re_M
-
-from vyos.config import Config
-from vyos.configdep import set_dependents, call_dependents
-from vyos.configdict import node_changed
-from vyos.ifconfig import Section
-from vyos.utils.boot import boot_configuration_complete
-from vyos.utils.process import call
-from vyos.utils.process import rc_cmd
-from vyos.utils.system import sysctl_read
-from vyos.utils.system import sysctl_apply
-from vyos.template import render
-
-from vyos import ConfigError
-from vyos import airbag
-from vyos.vpp import VPPControl
-from vyos.vpp import HostControl
-
-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'
-
-# 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:
- rc, out = rc_cmd(f'ethtool -i {iface}')
- # if ethtool command was successful
- if rc == 0 and out:
- regex_filter = r'^bus-info: (?P<address>\w+:\w+:\w+\.\w+)$'
- re_obj = re_search(regex_filter, out, re_M)
- # if bus-info with PCI address found
- if re_obj:
- address = re_obj.groupdict().get('address', '')
- return address
- # use VPP - maybe interface already attached to it
- vpp_control = VPPControl(attempts=20, interval=500)
- pci_addr = vpp_control.get_pci_addr(iface)
- if pci_addr:
- return pci_addr
- # raise error if PCI address was not found
- raise ConfigError(f'Cannot find PCI address for interface {iface}')
-
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
-
- base = ['vpp']
- base_ethernet = ['interfaces', 'ethernet']
-
- # find interfaces removed from VPP
- removed_ifaces = []
- tmp = node_changed(conf, base + ['interface'])
- if tmp:
- for removed_iface in tmp:
- pci_address: str = _get_pci_address_by_interface(removed_iface)
- removed_ifaces.append({
- 'iface_name': removed_iface,
- 'iface_pci_addr': pci_address
- })
- # add an interface to a list of interfaces that need
- # to be reinitialized after the commit
- set_dependents('ethernet', conf, removed_iface)
-
- if not conf.exists(base):
- return {'removed_ifaces': removed_ifaces}
-
- config = conf.get_config_dict(base, key_mangling=('-', '_'),
- no_tag_node_value_mangle=True,
- get_first_key=True,
- with_recursive_defaults=True)
-
- if 'interface' in config:
- for iface, iface_config in config['interface'].items():
- # add an interface to a list of interfaces that need
- # to be reinitialized after the commit
- set_dependents('ethernet', conf, iface)
-
- # Get PCI address auto
- if iface_config['pci'] == 'auto':
- config['interface'][iface]['pci'] = _get_pci_address_by_interface(iface)
-
- config['other_interfaces'] = conf.get_config_dict(base_ethernet, key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
- if removed_ifaces:
- config['removed_ifaces'] = removed_ifaces
-
- return config
-
-
-def verify(config):
- # bail out early - looks like removal from running config
- if not config or (len(config) == 1 and 'removed_ifaces' in config):
- return None
-
- if 'interface' not in config:
- 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('"cpu main-core" is required but not set!')
-
- memory_available: int = virtual_memory().available
- if memory_available < MIN_AVAILABLE_MEMORY:
- raise ConfigError(
- '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)
- return None
-
- render(service_conf, 'vpp/startup.conf.j2', config)
- render(systemd_override, 'vpp/override.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
-
-
-def apply(config):
- if not config or (len(config) == 1 and 'removed_ifaces' in config):
- call(f'systemctl stop {service_name}.service')
- else:
- call('systemctl daemon-reload')
- call(f'systemctl restart {service_name}.service')
-
- # Initialize interfaces removed from VPP
- for iface in config.get('removed_ifaces', []):
- host_control = HostControl()
- # rescan PCI to use a proper driver
- host_control.pci_rescan(iface['iface_pci_addr'])
- # rename to the proper name
- iface_new_name: str = host_control.get_eth_name(iface['iface_pci_addr'])
- host_control.rename_iface(iface_new_name, iface['iface_name'])
-
- if 'interface' in config:
- # connect to VPP
- # must be performed multiple attempts because API is not available
- # immediately after the service restart
- vpp_control = VPPControl(attempts=20, interval=500)
- for iface, _ in config['interface'].items():
- # Create lcp
- if iface not in Section.interfaces():
- vpp_control.lcp_pair_add(iface, iface)
-
- # reinitialize interfaces, but not during the first boot
- if boot_configuration_complete():
- call_dependents()
-
-
-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/etc/udev/rules.d/99-vyos-systemd.rules b/src/etc/udev/rules.d/99-vyos-systemd.rules
new file mode 100644
index 000000000..54aea668c
--- /dev/null
+++ b/src/etc/udev/rules.d/99-vyos-systemd.rules
@@ -0,0 +1,79 @@
+# The main reason that we store this file is systemd-udevd interfaces excludes
+# /lib/systemd/systemd-sysctl for dynamic interfaces (ppp|ipoe|l2tp etc)
+
+ACTION=="remove", GOTO="systemd_end"
+
+SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd"
+KERNEL=="vport*", TAG+="systemd"
+
+SUBSYSTEM=="ptp", TAG+="systemd"
+
+SUBSYSTEM=="ubi", TAG+="systemd"
+
+SUBSYSTEM=="block", TAG+="systemd"
+
+# We can't make any conclusions about suspended DM devices so let's just import previous SYSTEMD_READY state and skip other rules
+SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", IMPORT{db}="SYSTEMD_READY", GOTO="systemd_end"
+SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
+
+# Ignore encrypted devices with no identified superblock on it, since
+# we are probably still calling mke2fs or mkswap on it.
+SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
+
+# Explicitly set SYSTEMD_READY=1 for DM devices that don't have it set yet, so that we always have something to import above
+SUBSYSTEM=="block", ENV{DM_UUID}=="?*", ENV{SYSTEMD_READY}=="", ENV{SYSTEMD_READY}="1"
+
+# add symlink to GPT root disk
+SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
+SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
+SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root"
+
+# Ignore raid devices that are not yet assembled and started
+SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0"
+SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0"
+
+# Ignore loop devices that don't have any file attached
+SUBSYSTEM=="block", KERNEL=="loop[0-9]*", ENV{DEVTYPE}=="disk", TEST!="loop/backing_file", ENV{SYSTEMD_READY}="0"
+
+# Ignore nbd devices until the PID file exists (which signals a connected device)
+SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTEMD_READY}="0"
+
+# We need a hardware independent way to identify network devices. We
+# use the /sys/subsystem/ path for this. Kernel "bus" and "class" names
+# should be treated as one namespace, like udev handles it. This is mostly
+# just an identification string for systemd, so whether the path actually is
+# accessible or not does not matter as long as it is unique and in the
+# filesystem namespace.
+
+SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name"
+SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k", \
+ ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target"
+
+ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target"
+SUBSYSTEM=="sound", KERNEL=="controlC*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target"
+
+SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target"
+SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target", ENV{SYSTEMD_USER_WANTS}+="printer.target"
+
+SUBSYSTEM=="udc", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="usb-gadget.target"
+
+# Apply sysctl variables to network devices (and only to those) as they appear.
+# T5706. Exclude: lo, dummy*, ppp*, ipoe*, l2tp*, pptp*, sslvpn* and sstp*.
+ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo|dummy*|ppp*|ipoe*|l2tp*|pptp*|sslvpn*|sstp*", RUN+="/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name"
+
+# Pull in backlight save/restore for all backlight devices and
+# keyboard backlights
+SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service"
+SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service"
+
+# Pull in rfkill save/restore for all rfkill devices
+SUBSYSTEM=="rfkill", ENV{SYSTEMD_RFKILL}="1"
+SUBSYSTEM=="rfkill", IMPORT{builtin}="path_id"
+SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket"
+
+# Asynchronously mount file systems implemented by these modules as soon as they are loaded.
+SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount"
+SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount"
+
+LABEL="systemd_end"
diff --git a/src/helpers/strip-private.py b/src/helpers/strip-private.py
index eb584edaf..cb29069cf 100755
--- a/src/helpers/strip-private.py
+++ b/src/helpers/strip-private.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
-# Copyright 2021-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2021-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
@@ -21,7 +21,6 @@ import sys
from netaddr import IPNetwork, AddrFormatError
-
parser = argparse.ArgumentParser(description='strip off private information from VyOS config')
strictness = parser.add_mutually_exclusive_group()
@@ -119,6 +118,7 @@ if __name__ == "__main__":
(True, re.compile(r'(shared-secret-key-file|ca-cert-file|cert-file|dh-file|key-file|client) (\S+)'), r'\1 xxxxxx'),
# Strip IPSEC secrets
(True, re.compile(r'pre-shared-secret \S+'), 'pre-shared-secret xxxxxx'),
+ (True, re.compile(r'secret \S+'), 'secret xxxxxx'),
# Strip OSPF md5-key
(True, re.compile(r'md5-key \S+'), 'md5-key xxxxxx'),
# Strip WireGuard private-key
diff --git a/src/migration-scripts/dns-dynamic/1-to-2 b/src/migration-scripts/dns-dynamic/1-to-2
new file mode 100755
index 000000000..8b599b57a
--- /dev/null
+++ b/src/migration-scripts/dns-dynamic/1-to-2
@@ -0,0 +1,70 @@
+#!/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/>.
+
+# T5708:
+# - migrate "service dns dynamic timeout ..."
+# to "service dns dynamic interval ..."
+# - remove "service dns dynamic address <interface> web-options ..." when <interface> != "web"
+# - migrate "service dns dynamic address <interface> service <service> protocol dnsexit"
+# to "service dns dynamic address <interface> service <service> protocol dnsexit2"
+
+import sys
+from vyos.configtree import ConfigTree
+
+if len(sys.argv) < 2:
+ 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', 'dns', 'dynamic']
+timeout_path = base_path + ['timeout']
+address_path = base_path + ['address']
+
+if not config.exists(base_path):
+ # Nothing to do
+ sys.exit(0)
+
+# Migrate "service dns dynamic timeout ..."
+# to "service dns dynamic interval ..."
+if config.exists(timeout_path):
+ config.rename(timeout_path, 'interval')
+
+# Remove "service dns dynamic address <interface> web-options ..." when <interface> != "web"
+for address in config.list_nodes(address_path):
+ if config.exists(address_path + [address, 'web-options']) and address != 'web':
+ config.delete(address_path + [address, 'web-options'])
+
+# Migrate "service dns dynamic address <interface> service <service> protocol dnsexit"
+# to "service dns dynamic address <interface> service <service> protocol dnsexit2"
+for address in config.list_nodes(address_path):
+ for svc_cfg in config.list_nodes(address_path + [address, 'service']):
+ if config.exists(address_path + [address, 'service', svc_cfg, 'protocol']):
+ protocol = config.return_value(address_path + [address, 'service', svc_cfg, 'protocol'])
+ if protocol == 'dnsexit':
+ config.set(address_path + [address, 'service', svc_cfg, 'protocol'], 'dnsexit2')
+
+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/validators/accel-radius-dictionary b/src/validators/accel-radius-dictionary
deleted file mode 100755
index 05287e770..000000000
--- a/src/validators/accel-radius-dictionary
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-DICT_PATH=/usr/share/accel-ppp/radius
-NAME=$1
-
-if [ -n "$NAME" -a -e $DICT_PATH/dictionary.$NAME ]; then
- exit 0
-else
- echo "$NAME is not a valid RADIUS dictionary name"
- echo "Please make sure that $DICT_PATH/dictionary.$NAME file exists"
- exit 1
-fi
-
diff --git a/src/validators/ddclient-protocol b/src/validators/ddclient-protocol
index bc6826120..8f455e12e 100755
--- a/src/validators/ddclient-protocol
+++ b/src/validators/ddclient-protocol
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-ddclient -list-protocols | grep -vE 'nsupdate|cloudns' | grep -qw $1
+ddclient -list-protocols | grep -vE 'nsupdate|cloudns|porkbun' | grep -qw $1
if [ $? -gt 0 ]; then
echo "Error: $1 is not a valid protocol, please choose from the supported list of protocols"