diff options
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><id></format> - <description>CPU core id</description> - </valueHelp> - <valueHelp> - <format><idN>-<idM></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><xxxx:xx:xx.x></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><number>m</format> - <description>Megabyte</description> - </valueHelp> - <valueHelp> - <format><number>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" |