summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/accel-ppp/chap-secrets.config_dict.tmpl (renamed from data/templates/accel-ppp/chap-secrets.pppoe.tmpl)0
-rw-r--r--data/templates/accel-ppp/config_chap_secrets_radius.j236
-rw-r--r--data/templates/accel-ppp/config_ipv6_pool.j24
-rw-r--r--data/templates/accel-ppp/config_modules_auth_mode.j25
-rw-r--r--data/templates/accel-ppp/config_modules_auth_protocols.j210
-rw-r--r--data/templates/accel-ppp/config_modules_ipv6.j25
-rw-r--r--data/templates/accel-ppp/config_shaper_radius.j210
-rw-r--r--data/templates/accel-ppp/ipoe.config.tmpl1
-rw-r--r--data/templates/accel-ppp/pppoe.config.tmpl66
-rw-r--r--data/templates/accel-ppp/sstp.config.tmpl133
-rw-r--r--data/templates/dhcpv6-server/dhcpdv6.conf.tmpl9
-rw-r--r--data/templates/dns-forwarding/recursor.conf.tmpl4
-rw-r--r--data/templates/firewall/nftables-nat.tmpl10
-rw-r--r--data/templates/frr/ldpd.frr.tmpl24
-rw-r--r--data/templates/syslog/rsyslog.conf.tmpl22
-rw-r--r--data/templates/tftp-server/default.tmpl2
-rw-r--r--interface-definitions/dhcp-server.xml.in13
-rw-r--r--interface-definitions/dhcpv6-server.xml.in44
-rw-r--r--interface-definitions/dns-forwarding.xml.in40
-rw-r--r--interface-definitions/include/accel-auth-local-users.xml.i4
-rw-r--r--interface-definitions/include/accel-radius-additions-rate-limit.xml.i1
-rw-r--r--interface-definitions/include/dhcp-server-domain-search.xml.i12
-rw-r--r--interface-definitions/include/interface-disable-forwarding.xml.i8
-rw-r--r--interface-definitions/include/interface-ipv4.xml.i1
-rw-r--r--interface-definitions/include/listen-address.xml.i20
-rw-r--r--interface-definitions/include/vif.xml.i1
-rw-r--r--interface-definitions/interfaces-bonding.xml.in1
-rw-r--r--interface-definitions/interfaces-bridge.xml.in3
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in1
-rw-r--r--interface-definitions/interfaces-geneve.xml.in6
-rw-r--r--interface-definitions/interfaces-pseudo-ethernet.xml.in1
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in1
-rw-r--r--interface-definitions/interfaces-wireless.xml.in2
-rw-r--r--interface-definitions/nat.xml.in6
-rw-r--r--interface-definitions/ntp.xml.in19
-rw-r--r--interface-definitions/protocols-mpls.xml.in57
-rw-r--r--interface-definitions/ssh.xml.in19
-rw-r--r--interface-definitions/system-syslog.xml.in13
-rw-r--r--interface-definitions/tftp-server.xml.in24
-rw-r--r--interface-definitions/vpn_openconnect.xml.in19
-rw-r--r--op-mode-definitions/reset-mpls.xml31
-rw-r--r--op-mode-definitions/show-protocols-bfd.xml6
-rw-r--r--op-mode-definitions/show-protocols-static.xml17
-rw-r--r--python/vyos/configdict.py99
-rw-r--r--python/vyos/configverify.py87
-rw-r--r--python/vyos/ifconfig/bridge.py29
-rw-r--r--python/vyos/ifconfig/interface.py74
-rw-r--r--python/vyos/ifconfig/stp.py70
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py17
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py30
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py97
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py136
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py2
-rwxr-xr-xsmoketest/scripts/cli/test_service_tftp-server.py105
-rwxr-xr-xsmoketest/scripts/cli/test_system_ntp.py8
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_sstp.py1
-rwxr-xr-xsrc/conf_mode/bcast_relay.py3
-rwxr-xr-xsrc/conf_mode/dhcpv6_server.py26
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py16
-rwxr-xr-xsrc/conf_mode/intel_qat.py2
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py6
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py23
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py3
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py3
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py8
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py42
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py101
-rwxr-xr-xsrc/conf_mode/system-login.py4
-rwxr-xr-xsrc/conf_mode/system-syslog.py6
-rwxr-xr-xsrc/conf_mode/system-timezone.py1
-rwxr-xr-xsrc/conf_mode/tftp_server.py72
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py346
-rwxr-xr-xsrc/migration-scripts/pppoe-server/1-to-237
-rwxr-xr-xsrc/migration-scripts/pppoe-server/2-to-3117
-rwxr-xr-xsrc/migration-scripts/pppoe-server/3-to-4104
-rwxr-xr-xsrc/services/vyos-configd6
76 files changed, 1347 insertions, 1045 deletions
diff --git a/data/templates/accel-ppp/chap-secrets.pppoe.tmpl b/data/templates/accel-ppp/chap-secrets.config_dict.tmpl
index da64b64d5..da64b64d5 100644
--- a/data/templates/accel-ppp/chap-secrets.pppoe.tmpl
+++ b/data/templates/accel-ppp/chap-secrets.config_dict.tmpl
diff --git a/data/templates/accel-ppp/config_chap_secrets_radius.j2 b/data/templates/accel-ppp/config_chap_secrets_radius.j2
new file mode 100644
index 000000000..c94e75a23
--- /dev/null
+++ b/data/templates/accel-ppp/config_chap_secrets_radius.j2
@@ -0,0 +1,36 @@
+{% if authentication.mode is defined and authentication.mode == 'local' %}
+[chap-secrets]
+chap-secrets={{ chap_secrets_file }}
+{% elif authentication.mode is defined and authentication.mode == 'radius' %}
+[radius]
+verbose=1
+{% for server, options in authentication.radius.server.items() if not options.disable is defined %}
+server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }}
+{% endfor %}
+{% if authentication.radius.acct_interim_jitter is defined and authentication.radius.acct_interim_jitter is not none %}
+acct-interim-jitter={{ authentication.radius.acct_interim_jitter }}
+{% endif %}
+acct-timeout={{ authentication.radius.acct_timeout }}
+timeout={{ authentication.radius.timeout }}
+max-try={{ authentication.radius.max_try }}
+{% if authentication.radius.nas_identifier is defined and authentication.radius.nas_identifier is not none %}
+nas-identifier={{ authentication.radius.nas_identifier }}
+{% endif %}
+{% if authentication.radius.nas_ip_address is defined and authentication.radius.nas_ip_address is not none %}
+nas-ip-address={{ authentication.radius.nas_ip_address }}
+{% endif %}
+{% if authentication.radius.source_address is defined and authentication.radius.source_address is not none %}
+bind={{ authentication.radius.source_address }}
+{% endif %}
+{% if authentication.radius.called_sid_format is defined and authentication.radius.called_sid_format is not none %}
+called-sid={{ authentication.radius.called_sid_format }}
+{% endif %}
+{% if authentication.radius.dynamic_author.server is defined and authentication.radius.dynamic_author.server is not none %}
+dae-server={{ authentication.radius.dynamic_author.server }}:{{ authentication.radius.dynamic_author.port }},{{ authentication.radius.dynamic_author.key }}
+{% endif -%}
+{% endif %}
+{# Both chap-secrets and radius block required the gw-ip-address #}
+{% if gateway_address is defined and gateway_address is not none %}
+gw-ip-address={{ gateway_address }}
+{% endif %}
+
diff --git a/data/templates/accel-ppp/config_ipv6_pool.j2 b/data/templates/accel-ppp/config_ipv6_pool.j2
index b764fc6f0..f45bf9442 100644
--- a/data/templates/accel-ppp/config_ipv6_pool.j2
+++ b/data/templates/accel-ppp/config_ipv6_pool.j2
@@ -13,4 +13,8 @@ delegate={{ prefix }},{{ options.delegation_prefix }}
{% endfor %}
{% endif %}
{% endif %}
+{% if client_ipv6_pool.delegate is defined and client_ipv6_pool.delegate is not none %}
+[ipv6-dhcp]
+verbose=1
+{% endif %}
{% endif %}
diff --git a/data/templates/accel-ppp/config_modules_auth_mode.j2 b/data/templates/accel-ppp/config_modules_auth_mode.j2
new file mode 100644
index 000000000..5eca76f91
--- /dev/null
+++ b/data/templates/accel-ppp/config_modules_auth_mode.j2
@@ -0,0 +1,5 @@
+{% if authentication is defined and authentication.mode is defined and authentication.mode == 'local' %}
+chap-secrets
+{% elif authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %}
+radius
+{% endif %}
diff --git a/data/templates/accel-ppp/config_modules_auth_protocols.j2 b/data/templates/accel-ppp/config_modules_auth_protocols.j2
new file mode 100644
index 000000000..e122d6c48
--- /dev/null
+++ b/data/templates/accel-ppp/config_modules_auth_protocols.j2
@@ -0,0 +1,10 @@
+{% for protocol in authentication.protocols %}
+{# this should be fixed in the CLI by a migrator #}
+{% if protocol == 'chap' %}
+auth_chap_md5
+{% elif protocol == 'mschap' %}
+auth_mschap_v1
+{% else %}
+auth_{{ protocol.replace('-', '_') }}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/accel-ppp/config_modules_ipv6.j2 b/data/templates/accel-ppp/config_modules_ipv6.j2
new file mode 100644
index 000000000..e9ea4924b
--- /dev/null
+++ b/data/templates/accel-ppp/config_modules_ipv6.j2
@@ -0,0 +1,5 @@
+{% if ppp_options.ipv6 is defined and ppp_options.ipv6 != 'deny' %}
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+{% endif %}
diff --git a/data/templates/accel-ppp/config_shaper_radius.j2 b/data/templates/accel-ppp/config_shaper_radius.j2
new file mode 100644
index 000000000..2a6641245
--- /dev/null
+++ b/data/templates/accel-ppp/config_shaper_radius.j2
@@ -0,0 +1,10 @@
+{% if authentication is defined and authentication.mode is defined and authentication.mode == 'radius' %}
+{% if authentication is defined and authentication.radius is defined and authentication.radius.rate_limit is defined and authentication.radius.rate_limit.enable is defined %}
+[shaper]
+verbose=1
+attr={{ authentication.radius.rate_limit.attribute }}
+{% if authentication.radius.rate_limit.vendor is defined and authentication.radius.rate_limit.vendor is not none %}
+vendor={{ authentication.radius.rate_limit.vendor }}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/data/templates/accel-ppp/ipoe.config.tmpl b/data/templates/accel-ppp/ipoe.config.tmpl
index 5086c386e..ab61f7f5a 100644
--- a/data/templates/accel-ppp/ipoe.config.tmpl
+++ b/data/templates/accel-ppp/ipoe.config.tmpl
@@ -33,6 +33,7 @@ noauth=1
username=ifname
password=csid
{% endif %}
+proxy-arp=1
{%- for interface in interfaces %}
{% if (interface.shared == '0') and (interface.vlan_mon) %}
diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl
index 8f1b9e7c5..19adbc890 100644
--- a/data/templates/accel-ppp/pppoe.config.tmpl
+++ b/data/templates/accel-ppp/pppoe.config.tmpl
@@ -2,25 +2,15 @@
[modules]
log_syslog
pppoe
-{{ "radius" if authentication.mode is defined and authentication.mode == 'radius' }}
-chap-secrets
-ippool
-{% if ppp_options.ipv6 is defined and ppp_options.ipv6 != 'deny' %}
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-{% endif %}
-{% for protocol in authentication.protocols %}
-{# this should be fixed in the CLI by a migrator #}
-{% if protocol == 'chap' %}
-auth_chap_md5
-{% elif protocol == 'mschap' %}
-auth_mschap_v1
-{% else %}
-auth_{{ protocol.replace('-', '_') }}
-{% endif %}
-{% endfor %}
shaper
+{# Common authentication backend definitions #}
+{% include 'accel-ppp/config_modules_auth_mode.j2' %}
+ippool
+{# Common IPv6 definitions #}
+{% include 'accel-ppp/config_modules_ipv6.j2' %}
+{# Common authentication protocols (pap, chap ...) #}
+{% include 'accel-ppp/config_modules_auth_protocols.j2' %}
+
{% if snmp is defined %}
net-snmp
{% endif %}
@@ -60,41 +50,8 @@ wins{{ loop.index }}={{ server }}
{% endfor %}
{% endif %}
-{% if authentication.mode is defined and authentication.mode == 'local' %}
-[chap-secrets]
-chap-secrets={{ chap_secrets_file }}
-{% elif authentication.mode is defined and authentication.mode == 'radius' %}
-[radius]
-verbose=1
-{% for server, options in authentication.radius.server.items() if not options.disable is defined %}
-server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }}
-{% endfor %}
-{% if authentication.radius.acct_interim_jitter is defined and authentication.radius.acct_interim_jitter is not none %}
-acct-interim-jitter={{ authentication.radius.acct_interim_jitter }}
-{% endif %}
-acct-timeout={{ authentication.radius.acct_timeout }}
-timeout={{ authentication.radius.timeout }}
-max-try={{ authentication.radius.max_try }}
-{% if authentication.radius.nas_identifier is defined and authentication.radius.nas_identifier is not none %}
-nas-identifier={{ authentication.radius.nas_identifier }}
-{% endif %}
-{% if authentication.radius.nas_ip_address is defined and authentication.radius.nas_ip_address is not none %}
-nas-ip-address={{ authentication.radius.nas_ip_address }}
-{% endif %}
-{% if authentication.radius.source_address is defined and authentication.radius.source_address is not none %}
-bind={{ authentication.radius.source_address }}
-{% endif %}
-{% if authentication.radius.called_sid_format is defined and authentication.radius.called_sid_format is not none %}
-called-sid={{ authentication.radius.called_sid_format }}
-{% endif %}
-{% if authentication.radius.dynamic_author.server is defined and authentication.radius.dynamic_author.server is not none %}
-dae-server={{ authentication.radius.dynamic_author.server }}:{{ authentication.radius.dynamic_author.port }},{{ authentication.radius.dynamic_author.key }}
-{% endif -%}
-{% endif %}
-
-{% if gateway_address is defined and gateway_address is not none %}
-gw-ip-address={{ gateway_address }}
-{% endif %}
+{# Common chap-secrets and RADIUS server/option definitions #}
+{% include 'accel-ppp/config_chap_secrets_radius.j2' %}
{% if session_control is defined and session_control != 'disable' %}
[common]
@@ -170,5 +127,8 @@ timeout={{ limits.timeout }}
{% endif %}
{% endif %}
+{# Common RADIUS shaper configuration #}
+{% include 'accel-ppp/config_shaper_radius.j2' %}
+
[cli]
tcp=127.0.0.1:2001
diff --git a/data/templates/accel-ppp/sstp.config.tmpl b/data/templates/accel-ppp/sstp.config.tmpl
index c9e4a1d7d..7ca7b1c1e 100644
--- a/data/templates/accel-ppp/sstp.config.tmpl
+++ b/data/templates/accel-ppp/sstp.config.tmpl
@@ -3,22 +3,16 @@
log_syslog
sstp
shaper
-{% if auth_mode == 'local' %}
-chap-secrets
-{% elif auth_mode == 'radius' %}
-radius
-{% endif -%}
+{# Common authentication backend definitions #}
+{% include 'accel-ppp/config_modules_auth_mode.j2' %}
ippool
-ipv6pool
-ipv6_nd
-ipv6_dhcp
-
-{% for proto in auth_proto %}
-{{proto}}
-{% endfor %}
+{# Common IPv6 definitions #}
+{% include 'accel-ppp/config_modules_ipv6.j2' %}
+{# Common authentication protocols (pap, chap ...) #}
+{% include 'accel-ppp/config_modules_auth_protocols.j2' %}
[core]
-thread-count={{thread_cnt}}
+thread-count={{ thread_count }}
[common]
single-session=replace
@@ -35,112 +29,37 @@ disable
verbose=1
ifname=sstp%d
accept=ssl
-ssl-ca-file={{ ssl_ca }}
-ssl-pemfile={{ ssl_cert }}
-ssl-keyfile={{ ssl_key }}
-
-{% if client_ip_pool %}
-[ip-pool]
-gw-ip-address={{ client_gateway }}
-{% for subnet in client_ip_pool %}
-{{ subnet }}
-{% endfor %}
-{% endif %}
+ssl-ca-file={{ ssl.ca_cert_file }}
+ssl-pemfile={{ ssl.cert_file }}
+ssl-keyfile={{ ssl.key_file }}
-{% if dnsv4 %}
-[dns]
-{% for dns in dnsv4 -%}
-dns{{ loop.index }}={{ dns }}
-{% endfor -%}
-{% endif %}
+{# Common IP pool definitions #}
+{% include 'accel-ppp/config_ip_pool.j2' %}
-{% if dnsv6 %}
-[ipv6-dns]
-{% for dns in dnsv6 -%}
-{{ dns }}
-{% endfor -%}
-{% endif %}
+{# Common IPv6 pool definitions #}
+{% include 'accel-ppp/config_ipv6_pool.j2' %}
+{# Common DNS name-server definition #}
+{% include 'accel-ppp/config_name_server.j2' %}
-{% if auth_mode == 'local' %}
-[chap-secrets]
-chap-secrets={{ chap_secrets_file }}
-{% elif auth_mode == 'radius' %}
-[radius]
-verbose=1
-{% for r in radius_server %}
-server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
-{% endfor -%}
-
-acct-timeout={{ radius_acct_tmo }}
-timeout={{ radius_timeout }}
-max-try={{ radius_max_try }}
-
-{% if radius_nas_id %}
-nas-identifier={{ radius_nas_id }}
-{% endif -%}
-{% if radius_nas_ip %}
-nas-ip-address={{ radius_nas_ip }}
-{% endif -%}
-{% if radius_source_address %}
-bind={{ radius_source_address }}
-{% endif -%}
-
-
-{% if radius_dynamic_author %}
-dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
-{% endif -%}
-{% endif %}
-{% if client_gateway %}
-gw-ip-address={{ client_gateway }}
-{% endif %}
+{# Common chap-secrets and RADIUS server/option definitions #}
+{% include 'accel-ppp/config_chap_secrets_radius.j2' %}
[ppp]
verbose=1
check-ip=1
-{% if mtu %}
+{# MTU #}
mtu={{ mtu }}
-{% endif -%}
-{% if client_ipv6_pool %}
+{% if client_ipv6_pool is defined %}
ipv6=allow
{% endif %}
+mppe={{ ppp_options.mppe }}
+lcp-echo-interval={{ ppp_options.lcp_echo_interval }}
+lcp-echo-timeout={{ ppp_options.lcp_echo_timeout }}
+lcp-echo-failure={{ ppp_options.lcp_echo_failure }}
-{% if ppp_mppe %}
-mppe={{ ppp_mppe }}
-{% endif -%}
-{% if ppp_echo_interval %}
-lcp-echo-interval={{ ppp_echo_interval }}
-{% endif -%}
-{% if ppp_echo_failure %}
-lcp-echo-failure={{ ppp_echo_failure }}
-{% endif -%}
-{% if ppp_echo_timeout %}
-lcp-echo-timeout={{ ppp_echo_timeout }}
-{% endif %}
-
-{% if client_ipv6_pool %}
-[ipv6-pool]
-{% for p in client_ipv6_pool %}
-{{ p.prefix }},{{ p.mask }}
-{% endfor %}
-{% for p in client_ipv6_delegate_prefix %}
-delegate={{ p.prefix }},{{ p.mask }}
-{% endfor %}
-{% endif %}
-
-{% if client_ipv6_delegate_prefix %}
-[ipv6-dhcp]
-verbose=1
-{% endif %}
-
-{% if radius_shaper_attr %}
-[shaper]
-verbose=1
-attr={{ radius_shaper_attr }}
-{% if radius_shaper_vendor %}
-vendor={{ radius_shaper_vendor }}
-{% endif -%}
-{% endif %}
+{# Common RADIUS shaper configuration #}
+{% include 'accel-ppp/config_shaper_radius.j2' %}
[cli]
tcp=127.0.0.1:2005
diff --git a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
index ff7822b0d..bdeea71da 100644
--- a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
+++ b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
@@ -12,6 +12,15 @@ option dhcp6.preference {{ preference }};
{% for network in shared_network %}
{%- if not network.disabled -%}
shared-network {{ network.name }} {
+ {%- if network.common.info_refresh_time %}
+ option dhcp6.info-refresh-time {{ network.common.info_refresh_time }};
+ {%- endif %}
+ {%- if network.common.domain_search %}
+ option dhcp6.domain-search "{{ network.common.domain_search | join('", "') }}";
+ {%- endif %}
+ {%- if network.common.dns_server %}
+ option dhcp6.name-servers {{ network.common.dns_server | join(', ') }};
+ {%- endif %}
{%- for subnet in network.subnet %}
subnet6 {{ subnet.network }} {
{%- for range in subnet.range6_prefix %}
diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl
index b0ae3cc61..8799718b0 100644
--- a/data/templates/dns-forwarding/recursor.conf.tmpl
+++ b/data/templates/dns-forwarding/recursor.conf.tmpl
@@ -10,8 +10,8 @@ threads=1
allow-from={{ allow_from | join(',') }}
log-common-errors=yes
non-local-bind=yes
-query-local-address=0.0.0.0
-query-local-address6=::
+query-local-address={{ source_address_v4 | join(',') }}
+query-local-address6={{ source_address_v6 | join(',') }}
lua-config-file=recursor.conf.lua
# cache-size
diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl
index 0c29f536b..286c21859 100644
--- a/data/templates/firewall/nftables-nat.tmpl
+++ b/data/templates/firewall/nftables-nat.tmpl
@@ -28,6 +28,9 @@ add rule ip raw NAT_CONNTRACK counter accept
{% endif %}
{% macro nat_rule(rule, chain) %}
+{% set comment = "" %}
+{% set base_log = "" %}
+
{% set src_addr = "ip saddr " + rule.source_address if rule.source_address %}
{% set dst_addr = "ip daddr " + rule.dest_address if rule.dest_address %}
@@ -45,13 +48,15 @@ add rule ip raw NAT_CONNTRACK counter accept
{% set dst_port = "dport { " + rule.dest_port +" }" if rule.dest_port %}
{% endif %}
-{% set comment = "DST-NAT-" + rule.number %}
-
{% if chain == "PREROUTING" %}
+{% set comment = "DST-NAT-" + rule.number %}
+{% set base_log = "[NAT-DST-" + rule.number %}
{% set interface = " iifname \"" + rule.interface_in + "\"" if rule.interface_in is defined and rule.interface_in != 'any' else '' %}
{% set trns_addr = "dnat to " + rule.translation_address %}
{% elif chain == "POSTROUTING" %}
+{% set comment = "SRC-NAT-" + rule.number %}
+{% set base_log = "[NAT-SRC-" + rule.number %}
{% set interface = " oifname \"" + rule.interface_out + "\"" if rule.interface_out is defined and rule.interface_out != 'any' else '' %}
{% if rule.translation_address == 'masquerade' %}
{% set trns_addr = rule.translation_address %}
@@ -72,7 +77,6 @@ add rule ip raw NAT_CONNTRACK counter accept
{% endif %}
{% if rule.log %}
-{% set base_log = "[NAT-DST-" + rule.number %}
{% if rule.exclude %}
{% set log = base_log + "-EXCL]" %}
{% elif rule.translation_address == 'masquerade' %}
diff --git a/data/templates/frr/ldpd.frr.tmpl b/data/templates/frr/ldpd.frr.tmpl
index dbaa917e8..5f080d75f 100644
--- a/data/templates/frr/ldpd.frr.tmpl
+++ b/data/templates/frr/ldpd.frr.tmpl
@@ -15,6 +15,12 @@ neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}}
{% endfor -%}
address-family ipv4
label local allocate host-routes
+{% if old_ldp.export_ipv4_exp -%}
+no label local advertise explicit-null
+{% endif -%}
+{% if ldp.export_ipv4_exp -%}
+label local advertise explicit-null
+{% endif -%}
{% if old_ldp.d_transp_ipv4 -%}
no discovery transport-address {{ old_ldp.d_transp_ipv4 }}
{% endif -%}
@@ -33,6 +39,12 @@ no discovery hello interval {{ old_ldp.hello_interval }}
{% if ldp.hello_interval -%}
discovery hello interval {{ ldp.hello_interval }}
{% endif -%}
+{% if old_ldp.ses_ipv4_hold -%}
+no session holdtime {{ old_ldp.ses_ipv4_hold }}
+{% endif -%}
+{% if ldp.ses_ipv4_hold -%}
+session holdtime {{ ldp.ses_ipv4_hold }}
+{% endif -%}
{% for interface in old_ldp.interfaces -%}
no interface {{interface}}
{% endfor -%}
@@ -46,6 +58,18 @@ exit-address-family
{% if ldp.d_transp_ipv6 -%}
address-family ipv6
label local allocate host-routes
+{% if old_ldp.export_ipv6_exp -%}
+no label local advertise explicit-null
+{% endif -%}
+{% if ldp.export_ipv6_exp -%}
+label local advertise explicit-null
+{% endif -%}
+{% if old_ldp.ses_ipv6_hold -%}
+no session holdtime {{ old_ldp.ses_ipv6_hold }}
+{% endif -%}
+{% if ldp.ses_ipv6_hold -%}
+session holdtime {{ ldp.ses_ipv6_hold }}
+{% endif -%}
{% if old_ldp.d_transp_ipv6 -%}
no discovery transport-address {{ old_ldp.d_transp_ipv6 }}
{% endif -%}
diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.tmpl
index bc3f7667b..a610d132f 100644
--- a/data/templates/syslog/rsyslog.conf.tmpl
+++ b/data/templates/syslog/rsyslog.conf.tmpl
@@ -22,19 +22,23 @@ $outchannel {{file}},{{files[file]['log-file']}},{{files[file]['max-size']}},{{f
{% if hosts %}
## remote logging
{% for host in hosts %}
-{% if hosts[host]['proto'] == 'tcp' %}
-{% if hosts[host]['port'] %}
+{% if hosts[host]['proto'] == 'tcp' %}
+{% if hosts[host]['port'] %}
+{% if hosts[host]['oct_count'] %}
+{{hosts[host]['selectors']}} @@(o){{host}}:{{hosts[host]['port']}};RSYSLOG_SyslogProtocol23Format
+{% else %}
{{hosts[host]['selectors']}} @@{{host}}:{{hosts[host]['port']}}
-{% else %}
+{% endif %}
+{% else %}
{{hosts[host]['selectors']}} @@{{host}}
-{% endif %}
-{% else %}
-{% if hosts[host]['port'] %}
+{% endif %}
+{% else %}
+{% if hosts[host]['port'] %}
{{hosts[host]['selectors']}} @{{host}}:{{hosts[host]['port']}}
-{% else %}
+{% else %}
{{hosts[host]['selectors']}} @{{host}}
-{% endif %}
-{% endif %}
+{% endif %}
+{% endif %}
{% endfor %}
{% endif %}
{% if user %}
diff --git a/data/templates/tftp-server/default.tmpl b/data/templates/tftp-server/default.tmpl
index 18fee35d1..6b2d6a903 100644
--- a/data/templates/tftp-server/default.tmpl
+++ b/data/templates/tftp-server/default.tmpl
@@ -1,2 +1,2 @@
### Autogenerated by tftp_server.py ###
-DAEMON_ARGS="--listen --user tftp --address {% for a in listen-%}{{ a }}{% endfor %}{% if allow_upload %} --create --umask 000{% endif %} --secure {{ directory }}"
+DAEMON_ARGS="--listen --user tftp --address {{ listen_address }} {{ "--create --umask 000" if allow_upload is defined }} --secure {{ directory }}"
diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in
index e8bdff3df..ca8abc036 100644
--- a/interface-definitions/dhcp-server.xml.in
+++ b/interface-definitions/dhcp-server.xml.in
@@ -140,15 +140,14 @@
</leafNode>
<leafNode name="domain-name">
<properties>
- <help>Client domain name</help>
- </properties>
- </leafNode>
- <leafNode name="domain-search">
- <properties>
- <help>Client domain search</help>
- <multi/>
+ <help>Client Domain Name</help>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ <constraintErrorMessage>Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers and .-_</constraintErrorMessage>
</properties>
</leafNode>
+ #include <include/dhcp-server-domain-search.xml.i>
<leafNode name="exclude">
<properties>
<help>IP address to exclude from DHCP lease range</help>
diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in
index 4073b46b2..0b4bcf9ea 100644
--- a/interface-definitions/dhcpv6-server.xml.in
+++ b/interface-definitions/dhcpv6-server.xml.in
@@ -43,6 +43,39 @@
<valueless/>
</properties>
</leafNode>
+ <node name="common-options">
+ <properties>
+ <help>Common options to distribute to all clients, including stateless clients</help>
+ </properties>
+ <children>
+ <leafNode name="info-refresh-time">
+ <properties>
+ <help>Time (in seconds) that stateless clients should wait between refreshing the information they were given</help>
+ <valueHelp>
+ <format>1-4294967295</format>
+ <description>DHCPv6 information refresh time</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4294967295"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/dhcp-server-domain-search.xml.i>
+ <leafNode name="name-server">
+ <properties>
+ <help>IPv6 address of a Recursive DNS Server</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of DNS name server</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<tagNode name="subnet">
<properties>
<help>IPv6 DHCP subnet for this shared network [REQUIRED]</help>
@@ -108,16 +141,7 @@
</tagNode>
</children>
</node>
- <leafNode name="domain-search">
- <properties>
- <help>Domain name for client to search</help>
- <constraint>
- <regex>[-_a-zA-Z0-9.]+</regex>
- </constraint>
- <constraintErrorMessage>Invalid domain name. May only contain letters, numbers and .-_</constraintErrorMessage>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/dhcp-server-domain-search.xml.i>
<node name="lease-time">
<properties>
<help>Parameters relating to the lease time</help>
diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index 07e63d54a..ad6eb2463 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -128,24 +128,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="listen-address">
- <properties>
- <help>Addresses to listen for DNS queries [REQUIRED]</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Domain Name Server (DNS) IPv4 address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>Domain Name Server (DNS) IPv6 address</description>
- </valueHelp>
- <multi/>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/listen-address.xml.i>
<leafNode name="negative-ttl">
<properties>
<help>Maximum amount of time negative entries are cached (default: 3600)</help>
@@ -177,6 +160,27 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="source-address">
+ <properties>
+ <help>Local addresses from which to send DNS queries.
+ If unspecified, the querier will use any available address on
+ the outbound interface.</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address from which to send traffic</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address from which to send traffic</description>
+ </valueHelp>
+ <multi/>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ <defaultValue>0.0.0.0 ::</defaultValue>
+ </leafNode>
<leafNode name="system">
<properties>
<help>Use system name servers</help>
diff --git a/interface-definitions/include/accel-auth-local-users.xml.i b/interface-definitions/include/accel-auth-local-users.xml.i
index 4dc6c6dff..0d66b8135 100644
--- a/interface-definitions/include/accel-auth-local-users.xml.i
+++ b/interface-definitions/include/accel-auth-local-users.xml.i
@@ -35,7 +35,7 @@
<properties>
<help>Upload bandwidth limit in kbits/sec</help>
<constraint>
- <validator name="numeric" argument="--range 1-65535"/>
+ <validator name="numeric" argument="--range 1-10000000"/>
</constraint>
</properties>
</leafNode>
@@ -43,7 +43,7 @@
<properties>
<help>Download bandwidth limit in kbits/sec</help>
<constraint>
- <validator name="numeric" argument="--range 1-65535"/>
+ <validator name="numeric" argument="--range 1-10000000"/>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/accel-radius-additions-rate-limit.xml.i b/interface-definitions/include/accel-radius-additions-rate-limit.xml.i
index deab40e03..23a4a51cf 100644
--- a/interface-definitions/include/accel-radius-additions-rate-limit.xml.i
+++ b/interface-definitions/include/accel-radius-additions-rate-limit.xml.i
@@ -8,6 +8,7 @@
<properties>
<help>Specifies which radius attribute contains rate information. (default is Filter-Id)</help>
</properties>
+ <defaultValue>Filter-Id</defaultValue>
</leafNode>
<leafNode name="vendor">
<properties>
diff --git a/interface-definitions/include/dhcp-server-domain-search.xml.i b/interface-definitions/include/dhcp-server-domain-search.xml.i
new file mode 100644
index 000000000..9b3568b72
--- /dev/null
+++ b/interface-definitions/include/dhcp-server-domain-search.xml.i
@@ -0,0 +1,12 @@
+<!-- included start from dhcp-server-domain-search.xml.i -->
+<leafNode name="domain-search">
+ <properties>
+ <help>Client Domain Name search list</help>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ <constraintErrorMessage>Invalid domain name (RFC 1123 section 2).\nMay only contain letters, numbers, period, and underscore.</constraintErrorMessage>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/interface-disable-forwarding.xml.i b/interface-definitions/include/interface-disable-forwarding.xml.i
new file mode 100644
index 000000000..7cbb726ec
--- /dev/null
+++ b/interface-definitions/include/interface-disable-forwarding.xml.i
@@ -0,0 +1,8 @@
+<!-- included start from interface-disable-forwarding.xml.i -->
+<leafNode name="disable-forwarding">
+ <properties>
+ <help>Disable IPv4 forwarding on this interface</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/interface-ipv4.xml.i b/interface-definitions/include/interface-ipv4.xml.i
index 551059247..66842ab9b 100644
--- a/interface-definitions/include/interface-ipv4.xml.i
+++ b/interface-definitions/include/interface-ipv4.xml.i
@@ -5,6 +5,7 @@
</properties>
<children>
#include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
diff --git a/interface-definitions/include/listen-address.xml.i b/interface-definitions/include/listen-address.xml.i
new file mode 100644
index 000000000..e474344e4
--- /dev/null
+++ b/interface-definitions/include/listen-address.xml.i
@@ -0,0 +1,20 @@
+<!-- included start from listen-address.xml.i -->
+<leafNode name="listen-address">
+ <properties>
+ <help>Local IP addresses for service to listen on</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IP address to listen for incoming connections</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address to listen for incoming connections</description>
+ </valueHelp>
+ <multi/>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/vif.xml.i b/interface-definitions/include/vif.xml.i
index 15c453fcc..a0f7c0bc8 100644
--- a/interface-definitions/include/vif.xml.i
+++ b/interface-definitions/include/vif.xml.i
@@ -47,6 +47,7 @@
<children>
#include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index b28be387b..4e2c61d07 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -84,6 +84,7 @@
<children>
#include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index 92356d696..0a777865b 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -5,7 +5,7 @@
<tagNode name="bridge" owner="${vyos_conf_scripts_dir}/interfaces-bridge.py">
<properties>
<help>Bridge Interface</help>
- <priority>489</priority>
+ <priority>310</priority>
<constraint>
<regex>^br[0-9]+$</regex>
</constraint>
@@ -85,6 +85,7 @@
<children>
#include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 0aef0d332..a19a766d3 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -63,6 +63,7 @@
<children>
#include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index 28df42220..320dfd64d 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -28,6 +28,12 @@
#include <include/interface-enable-proxy-arp.xml.i>
</children>
</node>
+ <node name="ipv6">
+ <children>
+ #include <include/ipv6-disable-forwarding.xml.i>
+ #include <include/ipv6-dup-addr-detect-transmits.xml.i>
+ </children>
+ </node>
#include <include/interface-mac.xml.i>
#include <include/interface-mtu-1450-9000.xml.i>
<leafNode name="remote">
diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in
index 4382db598..3fceb70b6 100644
--- a/interface-definitions/interfaces-pseudo-ethernet.xml.in
+++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in
@@ -27,6 +27,7 @@
<children>
#include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 67001174f..7fdead16a 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -39,6 +39,7 @@
<children>
#include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index 8c594e758..423ec7ba2 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -465,6 +465,7 @@
<children>
#include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
@@ -699,6 +700,7 @@
</constraint>
<constraintErrorMessage>Unknown WPA mode</constraintErrorMessage>
</properties>
+ <defaultValue>both</defaultValue>
</leafNode>
<leafNode name="passphrase">
<properties>
diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in
index 8a14f4d25..00aaddb17 100644
--- a/interface-definitions/nat.xml.in
+++ b/interface-definitions/nat.xml.in
@@ -42,7 +42,11 @@
<format>ipv4range</format>
<description>IPv4 address range to match</description>
</valueHelp>
- <!-- TODO: add general iptables constraint script -->
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ <validator name="ipv4-address"/>
+ <validator name="ipv4-range"/>
+ </constraint>
</properties>
</leafNode>
#include <include/nat-translation-port.xml.i>
diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in
index 485487a42..6070cafe0 100644
--- a/interface-definitions/ntp.xml.in
+++ b/interface-definitions/ntp.xml.in
@@ -58,24 +58,7 @@
</leafNode>
</children>
</node>
- <leafNode name="listen-address">
- <properties>
- <help>Addresses to listen for NTP queries</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Network Time Protocol (NTP) IPv4 address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>Network Time Protocol (NTP) IPv6 address</description>
- </valueHelp>
- <multi/>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/listen-address.xml.i>
#include <include/interface-vrf.xml.i>
</children>
</node>
diff --git a/interface-definitions/protocols-mpls.xml.in b/interface-definitions/protocols-mpls.xml.in
index 3e9edbf72..3ea610d8b 100644
--- a/interface-definitions/protocols-mpls.xml.in
+++ b/interface-definitions/protocols-mpls.xml.in
@@ -78,6 +78,30 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="session-ipv4-holdtime">
+ <properties>
+ <help>Session ipv4 holdtime</help>
+ <valueHelp>
+ <format>15-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 15-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="session-ipv6-holdtime">
+ <properties>
+ <help>Session ipv6 holdtime</help>
+ <valueHelp>
+ <format>15-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 15-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="transport-ipv4-address">
<properties>
<help>Transport ipv4 address</help>
@@ -104,6 +128,39 @@
</leafNode>
</children>
</node>
+ <node name="export">
+ <properties>
+ <help>Export parameters</help>
+ </properties>
+ <children>
+ <node name="ipv4">
+ <properties>
+ <help>IPv4 parameters</help>
+ </properties>
+ <children>
+ <leafNode name="explicit-null">
+ <properties>
+ <help>Explicit-Null Label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>IPv6 parameters</help>
+ </properties>
+ <children>
+ <leafNode name="explicit-null">
+ <properties>
+ <help>Explicit-Null Label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
<leafNode name="interface">
<properties>
<help>Listen interface for LDP</help>
diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in
index d253c2f34..3db740131 100644
--- a/interface-definitions/ssh.xml.in
+++ b/interface-definitions/ssh.xml.in
@@ -110,24 +110,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="listen-address">
- <properties>
- <help>Local addresses SSH service should listen on</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IP address to listen for incoming connections</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address to listen for incoming connections</description>
- </valueHelp>
- <multi/>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/listen-address.xml.i>
<leafNode name="loglevel">
<properties>
<help>Log level</help>
diff --git a/interface-definitions/system-syslog.xml.in b/interface-definitions/system-syslog.xml.in
index 194cdb851..b357bb036 100644
--- a/interface-definitions/system-syslog.xml.in
+++ b/interface-definitions/system-syslog.xml.in
@@ -382,6 +382,19 @@
</leafNode>
</children>
</tagNode>
+ <node name="format">
+ <properties>
+ <help>Logging format</help>
+ </properties>
+ <children>
+ <leafNode name="octet-counted">
+ <properties>
+ <help>Allows for the transmission of all characters inside a syslog message</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
</children>
</tagNode>
<node name="global">
diff --git a/interface-definitions/tftp-server.xml.in b/interface-definitions/tftp-server.xml.in
index 2874b034c..abab71abd 100644
--- a/interface-definitions/tftp-server.xml.in
+++ b/interface-definitions/tftp-server.xml.in
@@ -22,34 +22,18 @@
</leafNode>
<leafNode name="port">
<properties>
- <help>Port for TFTP service</help>
+ <help>Port number used to listen for connections</help>
<valueHelp>
<format>1-65535</format>
- <description>Numeric IP port (default: 69)</description>
+ <description>Numeric IP port</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
+ <defaultValue>69</defaultValue>
</leafNode>
- <leafNode name="listen-address">
- <properties>
- <help>Addresses for TFTP server to listen [REQUIRED]</help>
- <valueHelp>
- <format>ipv4</format>
- <description>TFTP IPv4 listen address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>TFTP IPv6 listen address</description>
- </valueHelp>
- <multi/>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/listen-address.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in
index 16fe660a9..203f85bbc 100644
--- a/interface-definitions/vpn_openconnect.xml.in
+++ b/interface-definitions/vpn_openconnect.xml.in
@@ -231,24 +231,7 @@
</leafNode>
</children>
</node>
- <leafNode name="name-server">
- <properties>
- <help>Domain Name Server (DNS) propagated to client</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Domain Name Server (DNS) IPv4 address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>Domain Name Server (DNS) IPv6 address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/accel-name-server.xml.i>
</children>
</node>
</children>
diff --git a/op-mode-definitions/reset-mpls.xml b/op-mode-definitions/reset-mpls.xml
new file mode 100644
index 000000000..4e5d37d5b
--- /dev/null
+++ b/op-mode-definitions/reset-mpls.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="reset">
+ <children>
+ <node name="mpls">
+ <properties>
+ <help>Reset MPLS and related protocol commands</help>
+ </properties>
+ <children>
+ <node name="ldp">
+ <properties>
+ <help>Reset LDP commands</help>
+ </properties>
+ <children>
+ <tagNode name="neighbor">
+ <properties>
+ <help>Reset MPLS LDP neighbor/session</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt; &lt;h:h:h:h:h:h:h:h&gt;</list>
+ <script>/usr/bin/vtysh -c "show mpls ldp neighbor" | awk '{print $2}' | egrep -v "ID"</script>
+ </completionHelp>
+ </properties>
+ <command>/usr/bin/vtysh -c "clear mpls ldp neighbor $5"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition> \ No newline at end of file
diff --git a/op-mode-definitions/show-protocols-bfd.xml b/op-mode-definitions/show-protocols-bfd.xml
index 4483b39bf..3d9b67c67 100644
--- a/op-mode-definitions/show-protocols-bfd.xml
+++ b/op-mode-definitions/show-protocols-bfd.xml
@@ -40,6 +40,12 @@
</leafNode>
</children>
</tagNode>
+ <leafNode name="peers">
+ <properties>
+ <help>Show Bidirectional Forwarding Detection (BFD) peers brief</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show bfd peers brief"</command>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/show-protocols-static.xml b/op-mode-definitions/show-protocols-static.xml
index 1211a7fe5..2693c4248 100644
--- a/op-mode-definitions/show-protocols-static.xml
+++ b/op-mode-definitions/show-protocols-static.xml
@@ -2,6 +2,23 @@
<interfaceDefinition>
<node name="show">
<children>
+ <node name="arp">
+ <properties>
+ <help>Show Address Resolution Protocol (ARP) information</help>
+ </properties>
+ <command>/usr/sbin/arp -e -n</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show Address Resolution Protocol (ARP) cache for specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py -b</script>
+ </completionHelp>
+ </properties>
+ <command>/usr/sbin/arp -e -n -i "$6"</command>
+ </tagNode>
+ </children>
+ </node>
<node name="protocols">
<children>
<node name="static">
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index ce6d58693..62df3334c 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -18,12 +18,8 @@ A library for retrieving value dicts from VyOS configs in a declarative fashion.
"""
import os
-from copy import deepcopy
-
from vyos.util import vyos_dict_search
from vyos.xml import defaults
-from vyos.xml import is_tag
-from vyos.xml import is_leaf
from vyos import ConfigError
def retrieve_config(path_hash, base_path, config):
@@ -200,6 +196,7 @@ def is_member(conf, interface, intftype=None):
"""
ret_val = None
intftypes = ['bonding', 'bridge']
+
if intftype not in intftypes + [None]:
raise ValueError((
f'unknown interface type "{intftype}" or it cannot '
@@ -211,19 +208,13 @@ def is_member(conf, interface, intftype=None):
old_level = conf.get_level()
conf.set_level([])
- for it in intftype:
- base = ['interfaces', it]
+ for iftype in intftype:
+ base = ['interfaces', iftype]
for intf in conf.list_nodes(base):
- memberintf = base + [intf, 'member', 'interface']
- if is_tag(memberintf):
- if interface in conf.list_nodes(memberintf):
- ret_val = intf
- break
- elif is_leaf(memberintf):
- if ( conf.exists(memberintf) and
- interface in conf.return_values(memberintf) ):
- ret_val = intf
- break
+ member = base + [intf, 'member', 'interface', interface]
+ if conf.exists(member):
+ tmp = conf.get_config_dict(member, key_mangling=('-', '_'), get_first_key=True)
+ ret_val = {intf : tmp}
old_level = conf.set_level(old_level)
return ret_val
@@ -265,11 +256,12 @@ def is_source_interface(conf, interface, intftype=None):
def get_interface_dict(config, base, ifname=''):
"""
- Common utility function to retrieve and mandgle the interfaces available
- in CLI configuration. All interfaces have a common base ground where the
- value retrival is identical - so it can and should be reused
+ Common utility function to retrieve and mangle the interfaces configuration
+ from the CLI input nodes. All interfaces have a common base where value
+ retrival is identical. This function must be used whenever possible when
+ working on the interfaces node!
- Will return a dictionary with the necessary interface configuration
+ Return a dictionary with the necessary interface config keys.
"""
if not ifname:
# determine tagNode instance
@@ -405,3 +397,70 @@ def get_interface_dict(config, base, ifname=''):
# Check vif, vif-s/vif-c VLAN interfaces for removal
dict = get_removed_vlans(config, dict)
return dict
+
+
+def get_accel_dict(config, base, chap_secrets):
+ """
+ Common utility function to retrieve and mangle the Accel-PPP configuration
+ from different CLI input nodes. All Accel-PPP services have a common base
+ where value retrival is identical. This function must be used whenever
+ possible when working with Accel-PPP services!
+
+ Return a dictionary with the necessary interface config keys.
+ """
+ from vyos.util import get_half_cpus
+ from vyos.validate import is_ipv4
+
+ dict = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+
+ # defaults include RADIUS server specifics per TAG node which need to be
+ # added to individual RADIUS servers instead - so we can simply delete them
+ if vyos_dict_search('authentication.radius.server', default_values):
+ del default_values['authentication']['radius']['server']
+
+ # defaults include static-ip address per TAG node which need to be added to
+ # individual local users instead - so we can simply delete them
+ if vyos_dict_search('authentication.local_users.username', default_values):
+ del default_values['authentication']['local_users']['username']
+
+ dict = dict_merge(default_values, dict)
+
+ # set CPUs cores to process requests
+ dict.update({'thread_count' : get_half_cpus()})
+ # we need to store the path to the secrets file
+ dict.update({'chap_secrets_file' : chap_secrets})
+
+ # We can only have two IPv4 and three IPv6 nameservers - also they are
+ # configured in a different way in the configuration, this is why we split
+ # the configuration
+ if 'name_server' in dict:
+ ns_v4 = []
+ ns_v6 = []
+ for ns in dict['name_server']:
+ if is_ipv4(ns): ns_v4.append(ns)
+ else: ns_v6.append(ns)
+
+ dict.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6})
+ del dict['name_server']
+
+ # Add individual RADIUS server default values
+ if vyos_dict_search('authentication.radius.server', dict):
+ default_values = defaults(base + ['authentication', 'radius', 'server'])
+
+ for server in vyos_dict_search('authentication.radius.server', dict):
+ dict['authentication']['radius']['server'][server] = dict_merge(
+ default_values, dict['authentication']['radius']['server'][server])
+
+ # Add individual local-user default values
+ if vyos_dict_search('authentication.local_users.username', dict):
+ default_values = defaults(base + ['authentication', 'local-users', 'username'])
+
+ for username in vyos_dict_search('authentication.local_users.username', dict):
+ dict['authentication']['local_users']['username'][username] = dict_merge(
+ default_values, dict['authentication']['local_users']['username'][username])
+
+ return dict
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 944fc4294..422483663 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -22,6 +22,7 @@
# makes use of it!
from vyos import ConfigError
+from vyos.util import vyos_dict_search
def verify_mtu(config):
"""
@@ -51,27 +52,26 @@ def verify_mtu_ipv6(config):
configured on the interface. IPv6 requires a 1280 bytes MTU.
"""
from vyos.validate import is_ipv6
- from vyos.util import vyos_dict_search
- # IPv6 minimum required link mtu
- min_mtu = 1280
+ if 'mtu' in config:
+ # IPv6 minimum required link mtu
+ min_mtu = 1280
+ if int(config['mtu']) < min_mtu:
+ interface = config['ifname']
+ error_msg = f'IPv6 address will be configured on interface "{interface}" ' \
+ f'thus the minimum MTU requirement is {min_mtu}!'
- if int(config['mtu']) < min_mtu:
- interface = config['ifname']
- error_msg = f'IPv6 address will be configured on interface "{interface}" ' \
- f'thus the minimum MTU requirement is {min_mtu}!'
+ if not vyos_dict_search('ipv6.address.no_default_link_local', config):
+ raise ConfigError('link-local ' + error_msg)
- if not vyos_dict_search('ipv6.address.no_default_link_local', config):
- raise ConfigError('link-local ' + error_msg)
+ for address in (vyos_dict_search('address', config) or []):
+ if address in ['dhcpv6'] or is_ipv6(address):
+ raise ConfigError(error_msg)
- for address in (vyos_dict_search('address', config) or []):
- if address in ['dhcpv6'] or is_ipv6(address):
+ if vyos_dict_search('ipv6.address.autoconf', config):
raise ConfigError(error_msg)
- if vyos_dict_search('ipv6.address.autoconf', config):
- raise ConfigError(error_msg)
-
- if vyos_dict_search('ipv6.address.eui64', config):
- raise ConfigError(error_msg)
+ if vyos_dict_search('ipv6.address.eui64', config):
+ raise ConfigError(error_msg)
def verify_vrf(config):
@@ -204,3 +204,58 @@ def verify_vlan_config(config):
verify_dhcpv6(vlan)
verify_address(vlan)
verify_vrf(vlan)
+
+def verify_accel_ppp_base_service(config):
+ """
+ Common helper function which must be used by all Accel-PPP services based
+ on get_config_dict()
+ """
+ # vertify auth settings
+ if vyos_dict_search('authentication.mode', config) == 'local':
+ if not vyos_dict_search('authentication.local_users', config):
+ raise ConfigError('PPPoE local auth mode requires local users to be configured!')
+
+ for user in vyos_dict_search('authentication.local_users.username', config):
+ user_config = config['authentication']['local_users']['username'][user]
+
+ if 'password' not in user_config:
+ raise ConfigError(f'Password required for local user "{user}"')
+
+ if 'rate_limit' in user_config:
+ # if up/download is set, check that both have a value
+ if not {'upload', 'download'} <= set(user_config['rate_limit']):
+ raise ConfigError(f'User "{user}" has rate-limit configured for only one ' \
+ 'direction but both upload and download must be given!')
+
+ elif vyos_dict_search('authentication.mode', config) == 'radius':
+ if not vyos_dict_search('authentication.radius.server', config):
+ raise ConfigError('RADIUS authentication requires at least one server')
+
+ for server in vyos_dict_search('authentication.radius.server', config):
+ radius_config = config['authentication']['radius']['server'][server]
+ if 'key' not in radius_config:
+ raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
+
+ if 'gateway_address' not in config:
+ raise ConfigError('PPPoE server requires gateway-address to be configured!')
+
+ if 'name_server_ipv4' in config:
+ if len(config['name_server_ipv4']) > 2:
+ raise ConfigError('Not more then two IPv4 DNS name-servers ' \
+ 'can be configured')
+
+ if 'name_server_ipv6' in config:
+ if len(config['name_server_ipv6']) > 3:
+ raise ConfigError('Not more then three IPv6 DNS name-servers ' \
+ 'can be configured')
+
+ if 'client_ipv6_pool' in config:
+ ipv6_pool = config['client_ipv6_pool']
+ if 'delegate' in ipv6_pool:
+ if 'prefix' not in ipv6_pool:
+ raise ConfigError('IPv6 "delegate" also requires "prefix" to be defined!')
+
+ for delegate in ipv6_pool['delegate']:
+ if 'delegation_prefix' not in ipv6_pool['delegate'][delegate]:
+ raise ConfigError('delegation-prefix length required!')
+
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index c133a56fc..bf78f8972 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -16,7 +16,6 @@
from netifaces import interfaces
from vyos.ifconfig.interface import Interface
-from vyos.ifconfig.stp import STP
from vyos.validate import assert_boolean
from vyos.validate import assert_positive
from vyos.util import cmd
@@ -234,25 +233,33 @@ class BridgeIf(Interface):
if member in interfaces():
self.del_port(member)
- STPBridgeIf = STP.enable(BridgeIf)
tmp = vyos_dict_search('member.interface', config)
if tmp:
for interface, interface_config in tmp.items():
- # if we've come here we already verified the interface
- # does not have an addresses configured so just flush
- # any remaining ones
- Interface(interface).flush_addrs()
+ # if interface does yet not exist bail out early and
+ # add it later
+ if interface not in interfaces():
+ continue
+
+ # Bridge lower "physical" interface
+ lower = Interface(interface)
+
+ # If we've come that far we already verified the interface does
+ # not have any addresses configured by CLI so just flush any
+ # remaining ones
+ lower.flush_addrs()
# enslave interface port to bridge
self.add_port(interface)
- tmp = STPBridgeIf(interface)
# set bridge port path cost
- value = interface_config.get('cost')
- tmp.set_path_cost(value)
+ if 'cost' in interface_config:
+ value = interface_config.get('cost')
+ lower.set_path_cost(value)
# set bridge port path priority
- value = interface_config.get('priority')
- tmp.set_path_priority(value)
+ if 'priority' in interface_config:
+ value = interface_config.get('priority')
+ lower.set_path_priority(value)
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index d200fc7a8..ae747e87c 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -147,6 +147,10 @@ class Interface(Control):
'validate': assert_boolean,
'location': '/proc/sys/net/ipv4/conf/{ifname}/arp_ignore',
},
+ 'ipv4_forwarding': {
+ 'validate': assert_boolean,
+ 'location': '/proc/sys/net/ipv4/conf/{ifname}/forwarding',
+ },
'ipv6_accept_ra': {
'validate': lambda ara: assert_range(ara,0,3),
'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_ra',
@@ -163,6 +167,18 @@ class Interface(Control):
'validate': assert_positive,
'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
},
+ 'path_cost': {
+ # XXX: we should set a maximum
+ 'validate': assert_positive,
+ 'location': '/sys/class/net/{ifname}/brport/path_cost',
+ 'errormsg': '{ifname} is not a bridge port member'
+ },
+ 'path_priority': {
+ # XXX: we should set a maximum
+ 'validate': assert_positive,
+ 'location': '/sys/class/net/{ifname}/brport/priority',
+ 'errormsg': '{ifname} is not a bridge port member'
+ },
'proxy_arp': {
'validate': assert_boolean,
'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp',
@@ -461,6 +477,12 @@ class Interface(Control):
"""
return self.set_interface('arp_ignore', arp_ignore)
+ def set_ipv4_forwarding(self, forwarding):
+ """
+ Configure IPv4 forwarding.
+ """
+ return self.set_interface('ipv4_forwarding', forwarding)
+
def set_ipv6_accept_ra(self, accept_ra):
"""
Accept Router Advertisements; autoconfigure using them.
@@ -618,6 +640,28 @@ class Interface(Control):
self._admin_state_down_cnt += 1
return self.set_interface('admin_state', state)
+ def set_path_cost(self, cost):
+ """
+ Set interface path cost, only relevant for STP enabled interfaces
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_path_cost(4)
+ """
+ self.set_interface('path_cost', cost)
+
+ def set_path_priority(self, priority):
+ """
+ Set interface path priority, only relevant for STP enabled interfaces
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_path_priority(4)
+ """
+ self.set_interface('path_priority', priority)
+
def set_proxy_arp(self, enable):
"""
Set per interface proxy ARP configuration
@@ -799,24 +843,27 @@ class Interface(Control):
# flush all addresses
self._cmd(f'ip addr flush dev "{self.ifname}"')
- def add_to_bridge(self, br):
+ def add_to_bridge(self, bridge_dict):
"""
Adds the interface to the bridge with the passed port config.
Returns False if bridge doesn't exist.
"""
- # check if the bridge exists (on boot it doesn't)
- if br not in Section.interfaces('bridge'):
- return False
-
+ # drop all interface addresses first
self.flush_addrs()
- # add interface to bridge - use Section.klass to get BridgeIf class
- Section.klass(br)(br, create=False).add_port(self.ifname)
- # TODO: port config (STP)
+ for bridge, bridge_config in bridge_dict.items():
+ # add interface to bridge - use Section.klass to get BridgeIf class
+ Section.klass(bridge)(bridge, create=True).add_port(self.ifname)
- return True
+ # set bridge port path cost
+ if 'cost' in bridge_config:
+ self.set_path_cost(bridge_config['cost'])
+
+ # set bridge port path priority
+ if 'priority' in bridge_config:
+ self.set_path_cost(bridge_config['priority'])
def set_dhcp(self, enable):
"""
@@ -974,6 +1021,11 @@ class Interface(Control):
value = '1' if (tmp != None) else '0'
self.set_proxy_arp_pvlan(value)
+ # IPv4 forwarding
+ tmp = vyos_dict_search('ip.disable_forwarding', config)
+ value = '0' if (tmp != None) else '1'
+ self.set_ipv4_forwarding(value)
+
# IPv6 forwarding
tmp = vyos_dict_search('ipv6.disable_forwarding', config)
value = '0' if (tmp != None) else '1'
@@ -1032,8 +1084,8 @@ class Interface(Control):
# re-add ourselves to any bridge we might have fallen out of
if 'is_bridge_member' in config:
- bridge = config.get('is_bridge_member')
- self.add_to_bridge(bridge)
+ bridge_dict = config.get('is_bridge_member')
+ self.add_to_bridge(bridge_dict)
# remove no longer required 802.1ad (Q-in-Q VLANs)
ifname = config['ifname']
diff --git a/python/vyos/ifconfig/stp.py b/python/vyos/ifconfig/stp.py
deleted file mode 100644
index 5e83206c2..000000000
--- a/python/vyos/ifconfig/stp.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2019 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 vyos.ifconfig.interface import Interface
-
-from vyos.validate import assert_positive
-
-
-class STP:
- """
- A spanning-tree capable interface. This applies only to bridge port member
- interfaces!
- """
-
- @classmethod
- def enable (cls, adaptee):
- adaptee._sysfs_set = {**adaptee._sysfs_set, **cls._sysfs_set}
- adaptee.set_path_cost = cls.set_path_cost
- adaptee.set_path_priority = cls.set_path_priority
- return adaptee
-
- _sysfs_set = {
- 'path_cost': {
- # XXX: we should set a maximum
- 'validate': assert_positive,
- 'location': '/sys/class/net/{ifname}/brport/path_cost',
- 'errormsg': '{ifname} is not a bridge port member'
- },
- 'path_priority': {
- # XXX: we should set a maximum
- 'validate': assert_positive,
- 'location': '/sys/class/net/{ifname}/brport/priority',
- 'errormsg': '{ifname} is not a bridge port member'
- },
- }
-
- def set_path_cost(self, cost):
- """
- Set interface path cost, only relevant for STP enabled interfaces
-
- Example:
-
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').set_path_cost(4)
- """
- self.set_interface('path_cost', cost)
-
- def set_path_priority(self, priority):
- """
- Set interface path priority, only relevant for STP enabled interfaces
-
- Example:
-
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').set_path_priority(4)
- """
- self.set_interface('path_priority', priority)
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index cf401b0d8..56cbf1dd4 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -44,6 +44,9 @@ class BasicAccelPPPTest:
def set(self, path):
self.session.set(self._base_path + path)
+ def delete(self, path):
+ self.session.delete(self._base_path + path)
+
def basic_config(self):
# PPPoE local auth mode requires local users to be configured!
self.set(['authentication', 'local-users', 'username', 'vyos', 'password', 'vyos'])
@@ -117,6 +120,20 @@ class BasicAccelPPPTest:
# Check for running process
self.assertTrue(process_named_running(self._process_name))
+ # Check local-users default value(s)
+ self.delete(['authentication', 'local-users', 'username', user, 'static-ip'])
+ # commit changes
+ self.session.commit()
+
+ # check local users
+ tmp = cmd(f'sudo cat {self._chap_secrets}')
+ regex = f'{user}\s+\*\s+{password}\s+\*\s+{download}/{upload}'
+ tmp = re.findall(regex, tmp)
+ self.assertTrue(tmp)
+
+ # Check for running process
+ self.assertTrue(process_named_running(self._process_name))
+
def test_authentication_radius(self):
""" Test configuration of RADIUS authentication for PPPoE server """
self.basic_config()
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 047c19dd0..c6bb5bd1a 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -228,7 +228,7 @@ class BasicInterfaceTest:
self._mtu_test(vif)
def test_ip_options(self):
- """ test IP options like arp """
+ """ Test interface base IPv4 options """
if not self._test_ip:
return None
@@ -241,6 +241,7 @@ class BasicInterfaceTest:
# Options
self.session.set(path + ['ip', 'arp-cache-timeout', arp_tmo])
self.session.set(path + ['ip', 'disable-arp-filter'])
+ self.session.set(path + ['ip', 'disable-forwarding'])
self.session.set(path + ['ip', 'enable-arp-accept'])
self.session.set(path + ['ip', 'enable-arp-announce'])
self.session.set(path + ['ip', 'enable-arp-ignore'])
@@ -266,6 +267,9 @@ class BasicInterfaceTest:
tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_ignore')
self.assertEqual('1', tmp)
+ tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/forwarding')
+ self.assertEqual('0', tmp)
+
tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/proxy_arp')
self.assertEqual('1', tmp)
@@ -274,3 +278,27 @@ class BasicInterfaceTest:
tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/rp_filter')
self.assertEqual('2', tmp)
+
+ def test_ipv6_options(self):
+ """ Test interface base IPv6 options """
+ if not self._test_ipv6:
+ return None
+
+ for interface in self._interfaces:
+ dad_transmits = '10'
+ path = self._base_path + [interface]
+ for option in self._options.get(interface, []):
+ self.session.set(path + option.split())
+
+ # Options
+ self.session.set(path + ['ipv6', 'disable-forwarding'])
+ self.session.set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits])
+
+ self.session.commit()
+
+ for interface in self._interfaces:
+ tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding')
+ self.assertEqual('0', tmp)
+
+ tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/dad_transmits')
+ self.assertEqual(dad_transmits, tmp)
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
new file mode 100755
index 000000000..0ac91c170
--- /dev/null
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import unittest
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+
+PROCESS_NAME = 'openvpn'
+
+base_path = ['interfaces', 'openvpn']
+ca_cert = '/config/auth/ovpn_test_ca.crt'
+ssl_cert = '/config/auth/ovpn_test_server.crt'
+ssl_key = '/config/auth/ovpn_test_server.key'
+
+class TestInterfacesOpenVPN(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+
+ def tearDown(self):
+ self.session.delete(base_path)
+ self.session.commit()
+ del self.session
+
+ def test_client(self):
+ """ Basic OpenVPN client test """
+ interface = 'vtun10'
+ remote_host = '192.0.2.1'
+ remote_port = '1194'
+ protocol = 'udp'
+ path = base_path + [interface]
+
+ self.session.set(path + ['device-type', 'tun'])
+ self.session.set(path + ['encryption', 'cipher', 'aes256'])
+ self.session.set(path + ['hash', 'sha1'])
+ self.session.set(path + ['mode', 'client'])
+ self.session.set(path + ['persistent-tunnel'])
+ self.session.set(path + ['protocol', protocol])
+ self.session.set(path + ['remote-host', remote_host])
+ self.session.set(path + ['remote-port', remote_port])
+ self.session.set(path + ['tls', 'ca-cert-file', ca_cert])
+ self.session.set(path + ['tls', 'cert-file', ssl_cert])
+ self.session.set(path + ['tls', 'key-file', ssl_key])
+
+ self.session.commit()
+
+ config_file = f'/run/openvpn/{interface}.conf'
+ config = read_file(config_file)
+
+ self.assertIn(f'dev {interface}', config)
+ self.assertIn('dev-type tun', config)
+ self.assertIn('persist-key', config)
+ self.assertIn(f'proto {protocol}', config)
+ self.assertIn(f'rport {remote_port}', config)
+ self.assertIn(f'remote {remote_host}', config)
+ self.assertIn('persist-tun', config)
+
+
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+if __name__ == '__main__':
+ # Our SSL certificates need a subject ...
+ subject = '/C=DE/ST=BY/O=VyOS/localityName=Cloud/commonName=vyos/' \
+ 'organizationalUnitName=VyOS/emailAddress=maintainers@vyos.io/'
+
+ if not os.path.isfile(ssl_key) and not os.path.isfile(ssl_cert) and not os.path.isfile(ca_cert):
+ # Generate mandatory SSL certificate
+ tmp = f'openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 '\
+ f'-keyout {ssl_key} -out {ssl_cert} -subj {subject}'
+ cmd(tmp)
+
+ # Generate "CA"
+ tmp = f'openssl req -new -x509 -key {ssl_key} -out {ca_cert} '\
+ f'-subj {subject}'
+ cmd(tmp)
+
+ for file in [ca_cert, ssl_cert, ssl_key]:
+ cmd(f'sudo chown openvpn:openvpn {file}')
+
+ unittest.main()
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index b06fa239d..b5bde743b 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -19,13 +19,14 @@ import jmespath
import json
import unittest
-from vyos.configsession import ConfigSession, ConfigSessionError
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
from vyos.util import cmd
+from vyos.util import vyos_dict_search
base_path = ['nat']
-source_path = base_path + ['source']
-
-snat_pattern = 'nftables[?rule].rule[?chain].{chain: chain, comment: comment, address: { network: expr[].match.right.prefix.addr | [0], prefix: expr[].match.right.prefix.len | [0]}}'
+src_path = base_path + ['source']
+dst_path = base_path + ['destination']
class TestNAT(unittest.TestCase):
def setUp(self):
@@ -38,37 +39,126 @@ class TestNAT(unittest.TestCase):
self.session.delete(base_path)
self.session.commit()
- def test_source_nat(self):
- """ Configure and validate source NAT rule(s) """
-
- network = '192.168.0.0/16'
- self.session.set(source_path + ['rule', '1', 'destination', 'address', network])
- self.session.set(source_path + ['rule', '1', 'exclude'])
+ def test_snat(self):
+ """ Test source NAT (SNAT) rules """
+
+ rules = ['100', '110', '120', '130', '200', '210', '220', '230']
+ outbound_iface_100 = 'eth0'
+ outbound_iface_200 = 'eth1'
+ for rule in rules:
+ network = f'192.168.{rule}.0/24'
+ # depending of rule order we check either for source address for NAT
+ # or configured destination address for NAT
+ if int(rule) < 200:
+ self.session.set(src_path + ['rule', rule, 'source', 'address', network])
+ self.session.set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_100])
+ self.session.set(src_path + ['rule', rule, 'translation', 'address', 'masquerade'])
+ else:
+ self.session.set(src_path + ['rule', rule, 'destination', 'address', network])
+ self.session.set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_200])
+ self.session.set(src_path + ['rule', rule, 'exclude'])
- # check validate() - outbound-interface must be defined
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
-
- self.session.set(source_path + ['rule', '1', 'outbound-interface', 'any'])
self.session.commit()
tmp = cmd('sudo nft -j list table nat')
- nftable_json = json.loads(tmp)
- condensed_json = jmespath.search(snat_pattern, nftable_json)[0]
-
- self.assertEqual(condensed_json['comment'], 'DST-NAT-1')
- self.assertEqual(condensed_json['address']['network'], network.split('/')[0])
- self.assertEqual(str(condensed_json['address']['prefix']), network.split('/')[1])
+ data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
+
+ for idx in range(0, len(data_json)):
+ rule = str(rules[idx])
+ data = data_json[idx]
+ network = f'192.168.{rule}.0/24'
+
+ self.assertEqual(data['chain'], 'POSTROUTING')
+ self.assertEqual(data['comment'], f'SRC-NAT-{rule}')
+ self.assertEqual(data['family'], 'ip')
+ self.assertEqual(data['table'], 'nat')
+
+ iface = vyos_dict_search('match.right', data['expr'][0])
+ direction = vyos_dict_search('match.left.payload.field', data['expr'][1])
+ address = vyos_dict_search('match.right.prefix.addr', data['expr'][1])
+ mask = vyos_dict_search('match.right.prefix.len', data['expr'][1])
+
+ if int(rule) < 200:
+ self.assertEqual(direction, 'saddr')
+ self.assertEqual(iface, outbound_iface_100)
+ # check for masquerade keyword
+ self.assertIn('masquerade', data['expr'][3])
+ else:
+ self.assertEqual(direction, 'daddr')
+ self.assertEqual(iface, outbound_iface_200)
+ # check for return keyword due to 'exclude'
+ self.assertIn('return', data['expr'][3])
+
+ self.assertEqual(f'{address}/{mask}', network)
+
+ def test_dnat(self):
+ """ Test destination NAT (DNAT) rules """
+
+ rules = ['100', '110', '120', '130', '200', '210', '220', '230']
+ inbound_iface_100 = 'eth0'
+ inbound_iface_200 = 'eth1'
+ inbound_proto_100 = 'udp'
+ inbound_proto_200 = 'tcp'
+
+ for rule in rules:
+ port = f'10{rule}'
+ self.session.set(dst_path + ['rule', rule, 'source', 'port', port])
+ self.session.set(dst_path + ['rule', rule, 'translation', 'address', '192.0.2.1'])
+ self.session.set(dst_path + ['rule', rule, 'translation', 'port', port])
+ if int(rule) < 200:
+ self.session.set(dst_path + ['rule', rule, 'protocol', inbound_proto_100])
+ self.session.set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_100])
+ else:
+ self.session.set(dst_path + ['rule', rule, 'protocol', inbound_proto_200])
+ self.session.set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_200])
+ self.session.commit()
- def test_validation(self):
+ tmp = cmd('sudo nft -j list table nat')
+ data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
+
+ for idx in range(0, len(data_json)):
+ rule = str(rules[idx])
+ data = data_json[idx]
+ port = int(f'10{rule}')
+
+ self.assertEqual(data['chain'], 'PREROUTING')
+ self.assertEqual(data['comment'].split()[0], f'DST-NAT-{rule}')
+ self.assertEqual(data['family'], 'ip')
+ self.assertEqual(data['table'], 'nat')
+
+ iface = vyos_dict_search('match.right', data['expr'][0])
+ direction = vyos_dict_search('match.left.payload.field', data['expr'][1])
+ protocol = vyos_dict_search('match.left.payload.protocol', data['expr'][1])
+ dnat_addr = vyos_dict_search('dnat.addr', data['expr'][3])
+ dnat_port = vyos_dict_search('dnat.port', data['expr'][3])
+
+ self.assertEqual(direction, 'sport')
+ self.assertEqual(dnat_addr, '192.0.2.1')
+ self.assertEqual(dnat_port, port)
+ if int(rule) < 200:
+ self.assertEqual(iface, inbound_iface_100)
+ self.assertEqual(protocol, inbound_proto_100)
+ else:
+ self.assertEqual(iface, inbound_iface_200)
+
+
+ def test_validation_logic(self):
""" T2813: Ensure translation address is specified """
- self.session.set(source_path + ['rule', '100', 'outbound-interface', 'eth0'])
+ rule = '5'
+ self.session.set(src_path + ['rule', rule, 'source', 'address', '192.0.2.0/24'])
+
+ # check validate() - outbound-interface must be defined
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(src_path + ['rule', rule, 'outbound-interface', 'eth0'])
# check validate() - translation address not specified
with self.assertRaises(ConfigSessionError):
self.session.commit()
+ self.session.set(src_path + ['rule', rule, 'translation', 'address', 'masquerade'])
+ self.session.commit()
if __name__ == '__main__':
unittest.main()
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 8db002b57..f0c71e2de 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -47,7 +47,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.BaseTest):
mtu = '1492'
# validate some common values in the configuration
- for tmp in ['log_syslog', 'pppoe', 'chap-secrets', 'ippool',
+ for tmp in ['log_syslog', 'pppoe', 'ippool',
'auth_mschap_v2', 'auth_mschap_v1', 'auth_chap_md5',
'auth_pap', 'shaper']:
# Settings without values provide None
diff --git a/smoketest/scripts/cli/test_service_tftp-server.py b/smoketest/scripts/cli/test_service_tftp-server.py
new file mode 100755
index 000000000..92333392a
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_tftp-server.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019-2020 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 os
+import unittest
+
+from psutil import process_iter
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.util import read_file
+from vyos.util import process_named_running
+from vyos.validate import is_ipv6
+
+PROCESS_NAME = 'in.tftpd'
+base_path = ['service', 'tftp-server']
+dummy_if_path = ['interfaces', 'dummy', 'dum69']
+address_ipv4 = '192.0.2.1'
+address_ipv6 = '2001:db8::1'
+
+class TestServiceTFTPD(unittest.TestCase):
+ def setUp(self):
+ self.session = ConfigSession(os.getpid())
+ self.session.set(dummy_if_path + ['address', address_ipv4 + '/32'])
+ self.session.set(dummy_if_path + ['address', address_ipv6 + '/128'])
+
+ def tearDown(self):
+ self.session.delete(base_path)
+ self.session.delete(dummy_if_path)
+ self.session.commit()
+ del self.session
+
+ def test_01_tftpd_single(self):
+ directory = '/tmp'
+ port = '69' # default port
+
+ self.session.set(base_path + ['allow-upload'])
+ self.session.set(base_path + ['directory', directory])
+ self.session.set(base_path + ['listen-address', address_ipv4])
+
+ # commit changes
+ self.session.commit()
+
+ config = read_file('/etc/default/tftpd0')
+ # verify listen IP address
+ self.assertIn(f'{address_ipv4}:{port} -4', config)
+ # verify directory
+ self.assertIn(directory, config)
+ # verify upload
+ self.assertIn('--create --umask 000', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def test_02_tftpd_multi(self):
+ directory = '/tmp'
+ address = [address_ipv4, address_ipv6]
+ port = '70'
+
+ self.session.set(base_path + ['directory', directory])
+ for addr in address:
+ self.session.set(base_path + ['listen-address', addr])
+ self.session.set(base_path + ['port', port])
+
+ # commit changes
+ self.session.commit()
+
+ for idx in range(0, len(address)):
+ config = read_file(f'/etc/default/tftpd{idx}')
+ addr = address[idx]
+
+ # verify listen IP address
+ if is_ipv6(addr):
+ addr = f'[{addr}]'
+ self.assertIn(f'{addr}:{port} -6', config)
+ else:
+ self.assertIn(f'{addr}:{port} -4', config)
+
+ # verify directory
+ self.assertIn(directory, config)
+
+ # Check for running processes - one process is spawned per listen
+ # IP address, wheter it's IPv4 or IPv6
+ count = 0
+ for p in process_iter():
+ if PROCESS_NAME in p.name():
+ count += 1
+ self.assertEqual(count, len(address))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py
index 2a7c64870..4f62b62d5 100755
--- a/smoketest/scripts/cli/test_system_ntp.py
+++ b/smoketest/scripts/cli/test_system_ntp.py
@@ -70,10 +70,6 @@ class TestSystemNTP(unittest.TestCase):
def test_ntp_clients(self):
""" Test the allowed-networks statement """
- listen_address = ['127.0.0.1', '::1']
- for listen in listen_address:
- self.session.set(base_path + ['listen-address', listen])
-
networks = ['192.0.2.0/24', '2001:db8:1000::/64']
for network in networks:
self.session.set(base_path + ['allow-clients', 'address', network])
@@ -99,9 +95,7 @@ class TestSystemNTP(unittest.TestCase):
# Check listen address
tmp = get_config_value('interface')
- test = ['ignore wildcard']
- for listen in listen_address:
- test.append(f'listen {listen}')
+ test = ['ignore wildcard', 'listen 127.0.0.1', 'listen ::1']
self.assertEqual(tmp, test)
# Check for running process
diff --git a/smoketest/scripts/cli/test_vpn_sstp.py b/smoketest/scripts/cli/test_vpn_sstp.py
index 83be4c248..9babb83dc 100755
--- a/smoketest/scripts/cli/test_vpn_sstp.py
+++ b/smoketest/scripts/cli/test_vpn_sstp.py
@@ -19,7 +19,6 @@ import unittest
from base_accel_ppp_test import BasicAccelPPPTest
from vyos.util import cmd
-process_name = 'accel-pppd'
ca_cert = '/tmp/ca.crt'
ssl_cert = '/tmp/server.crt'
ssl_key = '/tmp/server.key'
diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py
index 4a47b9246..78daeb6be 100755
--- a/src/conf_mode/bcast_relay.py
+++ b/src/conf_mode/bcast_relay.py
@@ -78,7 +78,8 @@ def generate(relay):
continue
config['instance'] = instance
- render(config_file_base + instance, 'bcast-relay/udp-broadcast-relay.tmpl', config)
+ render(config_file_base + instance, 'bcast-relay/udp-broadcast-relay.tmpl',
+ config, trim_blocks=True)
return None
diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py
index 4ce4cada1..1777d4db7 100755
--- a/src/conf_mode/dhcpv6_server.py
+++ b/src/conf_mode/dhcpv6_server.py
@@ -65,6 +65,7 @@ def get_config(config=None):
config = {
'name': network,
'disabled': False,
+ 'common': {},
'subnet': []
}
@@ -72,6 +73,31 @@ def get_config(config=None):
if conf.exists(['disable']):
config['disabled'] = True
+ # Common options shared among subnets. These can be overridden if
+ # the same option is specified on a per-subnet or per-host
+ # basis. These are the only options that can be handed out to
+ # stateless clients via an information-request message.
+ if conf.exists(['common-options']):
+ conf.set_level(base + ['shared-network-name', network, 'common-options'])
+
+ # How often stateless clients should refresh their information. This is
+ # mostly taken as a hint by clients, and only if they request it.
+ # (if not specified, the server does not supply this to the client)
+ if conf.exists(['info-refresh-time']):
+ config['common']['info_refresh_time'] = conf.return_value(['info-refresh-time'])
+
+ # The domain-search option specifies a 'search list' of Domain Names to be used
+ # by the client to locate not-fully-qualified domain names.
+ if conf.exists(['domain-search']):
+ config['common']['domain_search'] = conf.return_values(['domain-search'])
+
+ # Specifies a list of Domain Name System name servers available to the client.
+ # Servers should be listed in order of preference.
+ if conf.exists(['name-server']):
+ config['common']['dns_server'] = conf.return_values(['name-server'])
+
+ conf.set_level(base + ['shared-network-name', network])
+
# check for multiple subnet configurations in a shared network
if conf.exists(['subnet']):
for net in conf.list_nodes(['subnet']):
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 5101c1e79..2187b3c73 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -26,6 +26,7 @@ from vyos.util import chown
from vyos.util import vyos_dict_search
from vyos.template import render
from vyos.xml import defaults
+from vyos.validate import is_ipv6
from vyos import ConfigError
from vyos import airbag
@@ -65,6 +66,21 @@ def get_config(config=None):
if conf.exists(base_nameservers_dhcp):
dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)})
+ # Split the source_address property into separate IPv4 and IPv6 lists
+ # NOTE: In future versions of pdns-recursor (> 4.4.0), this logic can be removed
+ # as both IPv4 and IPv6 addresses can be specified in a single setting.
+ source_address_v4 = []
+ source_address_v6 = []
+
+ for source_address in dns['source_address']:
+ if is_ipv6(source_address):
+ source_address_v6.append(source_address)
+ else:
+ source_address_v4.append(source_address)
+
+ dns.update({'source_address_v4': source_address_v4})
+ dns.update({'source_address_v6': source_address_v6})
+
return dns
def verify(dns):
diff --git a/src/conf_mode/intel_qat.py b/src/conf_mode/intel_qat.py
index 86dbccaf0..ab98cbc03 100755
--- a/src/conf_mode/intel_qat.py
+++ b/src/conf_mode/intel_qat.py
@@ -67,7 +67,7 @@ def verify(qat):
output, err = popen('lspci -nn', decode='utf-8')
if not err:
data = re.findall(
- '(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)', output)
+ '(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)|(8086:1f18)', output)
# If QAT devices found
if not data:
raise ConfigError('No QAT acceleration device found')
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 9763620ac..ea9bd54d4 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -109,7 +109,7 @@ def get_config(config=None):
# Check if member interface is already member of a bond
tmp = is_member(conf, interface, 'bonding')
- if tmp and tmp != bond['ifname']:
+ if tmp and bond['ifname'] not in tmp:
interface_config.update({'is_bond_member' : tmp})
# Check if member interface is used as source-interface on another interface
@@ -162,11 +162,11 @@ def verify(bond):
raise ConfigError(error_msg + 'it does not exist!')
if 'is_bridge_member' in interface_config:
- tmp = interface_config['is_bridge_member']
+ tmp = next(iter(interface_config['is_bridge_member']))
raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
if 'is_bond_member' in interface_config:
- tmp = interface_config['is_bond_member']
+ tmp = next(iter(interface_config['is_bond_member']))
raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
if 'is_source_interface' in interface_config:
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 485decb17..4aeb8fc67 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -24,6 +24,7 @@ from vyos.configdict import get_interface_dict
from vyos.configdict import node_changed
from vyos.configdict import is_member
from vyos.configdict import is_source_interface
+from vyos.configdict import dict_merge
from vyos.configverify import verify_dhcpv6
from vyos.configverify import verify_vrf
from vyos.ifconfig import BridgeIf
@@ -69,25 +70,26 @@ def get_config(config=None):
# the default dictionary is not properly paged into the dict (see T2665)
# thus we will ammend it ourself
default_member_values = defaults(base + ['member', 'interface'])
- for interface, interface_config in bridge['member']['interface'].items():
- interface_config.update(default_member_values)
+ for interface in bridge['member']['interface']:
+ bridge['member']['interface'][interface] = dict_merge(
+ default_member_values, bridge['member']['interface'][interface])
# Check if member interface is already member of another bridge
tmp = is_member(conf, interface, 'bridge')
- if tmp and tmp != bridge['ifname']:
- interface_config.update({'is_bridge_member' : tmp})
+ if tmp and bridge['ifname'] not in tmp:
+ bridge['member']['interface'][interface].update({'is_bridge_member' : tmp})
# Check if member interface is already member of a bond
tmp = is_member(conf, interface, 'bonding')
- if tmp: interface_config.update({'is_bond_member' : tmp})
+ if tmp: bridge['member']['interface'][interface].update({'is_bond_member' : tmp})
# Check if member interface is used as source-interface on another interface
tmp = is_source_interface(conf, interface)
- if tmp: interface_config.update({'is_source_interface' : tmp})
+ if tmp: bridge['member']['interface'][interface].update({'is_source_interface' : tmp})
# Bridge members must not have an assigned address
tmp = has_address_configured(conf, interface)
- if tmp: interface_config.update({'has_address' : ''})
+ if tmp: bridge['member']['interface'][interface].update({'has_address' : ''})
return bridge
@@ -105,15 +107,12 @@ def verify(bridge):
if interface == 'lo':
raise ConfigError('Loopback interface "lo" can not be added to a bridge')
- if interface not in interfaces():
- raise ConfigError(error_msg + 'it does not exist!')
-
if 'is_bridge_member' in interface_config:
- tmp = interface_config['is_bridge_member']
+ tmp = next(iter(interface_config['is_bridge_member']))
raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
if 'is_bond_member' in interface_config:
- tmp = interface_config['is_bond_member']
+ tmp = next(iter(interface_config['is_bond_member']))
raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
if 'is_source_interface' in interface_config:
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 518dbdc0e..f2b580c6f 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -208,7 +208,8 @@ def get_config(config=None):
openvpn['auth_user_pass_file'] = f"/run/openvpn/{openvpn['intf']}.pw"
# check if interface is member of a bridge
- openvpn['is_bridge_member'] = is_member(conf, openvpn['intf'], 'bridge')
+ tmp = is_member(conf, openvpn['intf'], 'bridge')
+ if tmp: openvpn['is_bridge_member'] = next(iter(tmp))
# Check if interface instance has been removed
if not conf.exists('interfaces openvpn ' + openvpn['intf']):
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index f1d885b15..5561514bd 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -462,7 +462,8 @@ def get_config(config=None):
options['tunnel'] = {}
# check for bridges
- options['bridge'] = is_member(config, ifname, 'bridge')
+ tmp = is_member(config, ifname, 'bridge')
+ if tmp: options['bridge'] = next(iter(tmp))
options['interfaces'] = interfaces()
for name in ct:
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index ad8aee168..c1770771e 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -76,6 +76,14 @@ def get_config(config=None):
base = ['interfaces', 'wireless']
wifi = get_interface_dict(conf, base)
+
+ # Cleanup "delete" default values when required user selectable values are
+ # not defined at all
+ tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
+ if not (vyos_dict_search('security.wpa.passphrase', tmp) or
+ vyos_dict_search('security.wpa.radius', tmp)):
+ del wifi['security']['wpa']
+
# defaults include RADIUS server specifics per TAG node which need to be
# added to individual RADIUS servers instead - so we can simply delete them
if vyos_dict_search('security.wpa.radius.server.port', wifi):
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index e515490d0..904d219e2 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -43,7 +43,12 @@ def get_config(config=None):
'd_transp_ipv4' : None,
'd_transp_ipv6' : None,
'hello_holdtime' : None,
- 'hello_interval' : None
+ 'hello_interval' : None,
+ 'ses_ipv4_hold' : None,
+ 'ses_ipv6_hold' : None,
+ 'export_ipv4_exp' : False,
+ 'export_ipv6_exp' : False
+
},
'ldp' : {
'interfaces' : [],
@@ -51,7 +56,12 @@ def get_config(config=None):
'd_transp_ipv4' : None,
'd_transp_ipv6' : None,
'hello_holdtime' : None,
- 'hello_interval' : None
+ 'hello_interval' : None,
+ 'ses_ipv4_hold' : None,
+ 'ses_ipv6_hold' : None,
+ 'export_ipv4_exp' : False,
+ 'export_ipv6_exp' : False
+
}
}
if not (conf.exists('protocols mpls') or conf.exists_effective('protocols mpls')):
@@ -82,6 +92,20 @@ def get_config(config=None):
if conf.exists('discovery hello-interval'):
mpls_conf['ldp']['hello_interval'] = conf.return_value('discovery hello-interval')
+ # Get session-ipv4-holdtime
+ if conf.exists_effective('discovery session-ipv4-holdtime'):
+ mpls_conf['old_ldp']['ses_ipv4_hold'] = conf.return_effective_value('discovery session-ipv4-holdtime')
+
+ if conf.exists('discovery session-ipv4-holdtime'):
+ mpls_conf['ldp']['ses_ipv4_hold'] = conf.return_value('discovery session-ipv4-holdtime')
+
+ # Get session-ipv6-holdtime
+ if conf.exists_effective('discovery session-ipv6-holdtime'):
+ mpls_conf['old_ldp']['ses_ipv6_hold'] = conf.return_effective_value('discovery session-ipv6-holdtime')
+
+ if conf.exists('discovery session-ipv6-holdtime'):
+ mpls_conf['ldp']['ses_ipv6_hold'] = conf.return_value('discovery session-ipv6-holdtime')
+
# Get discovery transport-ipv4-address
if conf.exists_effective('discovery transport-ipv4-address'):
mpls_conf['old_ldp']['d_transp_ipv4'] = conf.return_effective_value('discovery transport-ipv4-address')
@@ -96,6 +120,20 @@ def get_config(config=None):
if conf.exists('discovery transport-ipv6-address'):
mpls_conf['ldp']['d_transp_ipv6'] = conf.return_value('discovery transport-ipv6-address')
+ # Get export ipv4 explicit-null
+ if conf.exists_effective('export ipv4 explicit-null'):
+ mpls_conf['old_ldp']['export_ipv4_exp'] = True
+
+ if conf.exists('export ipv4 explicit-null'):
+ mpls_conf['ldp']['export_ipv4_exp'] = True
+
+ # Get export ipv6 explicit-null
+ if conf.exists_effective('export ipv6 explicit-null'):
+ mpls_conf['old_ldp']['export_ipv6_exp'] = True
+
+ if conf.exists('export ipv6 explicit-null'):
+ mpls_conf['ldp']['export_ipv6_exp'] = True
+
# Get interfaces
if conf.exists_effective('interface'):
mpls_conf['old_ldp']['interfaces'] = conf.return_effective_values('interface')
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 445311391..a520120f8 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -19,13 +19,11 @@ import os
from sys import exit
from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.validate import is_ipv4
+from vyos.configdict import get_accel_dict
+from vyos.configverify import verify_accel_ppp_base_service
from vyos.template import render
from vyos.util import call
-from vyos.util import get_half_cpus
from vyos.util import vyos_dict_search
-from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -42,108 +40,22 @@ def get_config(config=None):
if not conf.exists(base):
return None
- pppoe = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(base)
-
- # defaults include RADIUS server specifics per TAG node which need to be
- # added to individual RADIUS servers instead - so we can simply delete them
- if vyos_dict_search('authentication.radius.server', default_values):
- del default_values['authentication']['radius']['server']
- # defaults include static-ip address per TAG node which need to be added to
- # individual local users instead - so we can simply delete them
- if vyos_dict_search('authentication.local_users.username', default_values):
- del default_values['authentication']['local_users']['username']
-
- pppoe = dict_merge(default_values, pppoe)
-
- # set CPUs cores to process requests
- pppoe.update({'thread_count' : get_half_cpus()})
- # we need to store the path to the secrets file
- pppoe.update({'chap_secrets_file' : pppoe_chap_secrets})
-
- # We can only have two IPv4 and three IPv6 nameservers - also they are
- # configured in a different way in the configuration, this is why we split
- # the configuration
- if 'name_server' in pppoe:
- ns_v4 = []
- ns_v6 = []
- for ns in pppoe['name_server']:
- if is_ipv4(ns): ns_v4.append(ns)
- else: ns_v6.append(ns)
-
- pppoe.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6})
- del pppoe['name_server']
-
- # Add individual RADIUS server default values
- if vyos_dict_search('authentication.radius.server', pppoe):
- default_values = defaults(base + ['authentication', 'radius', 'server'])
-
- for server in vyos_dict_search('authentication.radius.server', pppoe):
- pppoe['authentication']['radius']['server'][server] = dict_merge(
- default_values, pppoe['authentication']['radius']['server'][server])
-
- # Add individual local-user default values
- if vyos_dict_search('authentication.local_users.username', pppoe):
- default_values = defaults(base + ['authentication', 'local_users', 'username'])
-
- for username in vyos_dict_search('authentication.local_users.username', pppoe):
- pppoe['authentication']['local_users']['username'][username] = dict_merge(
- default_values, pppoe['authentication']['local_users']['username'][username])
-
+ # retrieve common dictionary keys
+ pppoe = get_accel_dict(conf, base, pppoe_chap_secrets)
return pppoe
-
def verify(pppoe):
if not pppoe:
return None
- # vertify auth settings
- if vyos_dict_search('authentication.mode', pppoe) == 'local':
- if not vyos_dict_search('authentication.local_users', pppoe):
- raise ConfigError('PPPoE local auth mode requires local users to be configured!')
-
- for user in vyos_dict_search('authentication.local_users.username', pppoe):
- user_config = pppoe['authentication']['local_users']['username'][user]
-
- if 'password' not in user_config:
- raise ConfigError(f'Password required for local user "{user}"')
-
- if 'rate_limit' in user_config:
- # if up/download is set, check that both have a value
- if not {'upload', 'download'} <= set(user_config['rate_limit']):
- raise ConfigError(f'User "{user}" has rate-limit configured for only one ' \
- 'direction but both upload and download must be given!')
-
- elif vyos_dict_search('authentication.mode', pppoe) == 'radius':
- if not vyos_dict_search('authentication.radius.server', pppoe):
- raise ConfigError('RADIUS authentication requires at least one server')
-
- for server in vyos_dict_search('authentication.radius.server', pppoe):
- radius_config = pppoe['authentication']['radius']['server'][server]
- if 'key' not in radius_config:
- raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
+ verify_accel_ppp_base_service(pppoe)
if 'wins_server' in pppoe and len(pppoe['wins_server']) > 2:
raise ConfigError('Not more then two IPv4 WINS name-servers can be configured')
- if 'name_server_ipv4' in pppoe:
- if len(pppoe['name_server_ipv4']) > 2:
- raise ConfigError('Not more then two IPv4 DNS name-servers ' \
- 'can be configured')
-
- if 'name_server_ipv6' in pppoe:
- if len(pppoe['name_server_ipv6']) > 3:
- raise ConfigError('Not more then three IPv6 DNS name-servers ' \
- 'can be configured')
-
if 'interface' not in pppoe:
raise ConfigError('At least one listen interface must be defined!')
- if 'gateway_address' not in pppoe:
- raise ConfigError('PPPoE server requires gateway-address to be configured!')
-
# local ippool and gateway settings config checks
if not (vyos_dict_search('client_ip_pool.subnet', pppoe) or
(vyos_dict_search('client_ip_pool.start', pppoe) and
@@ -164,7 +76,8 @@ def generate(pppoe):
render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe, trim_blocks=True)
if vyos_dict_search('authentication.mode', pppoe) == 'local':
- render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.pppoe.tmpl', pppoe, trim_blocks=True, permission=0o640)
+ render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
+ pppoe, trim_blocks=True, permission=0o640)
else:
if os.path.exists(pppoe_chap_secrets):
os.unlink(pppoe_chap_secrets)
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 2aca199f9..2c0bbd4f7 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -233,8 +233,8 @@ def generate(login):
env = os.environ.copy()
env['vyos_libexec_dir'] = '/usr/libexec/vyos'
- call("/opt/vyatta/sbin/my_set system login user '{name}' "
- "authentication plaintext-password '{password_plaintext}'"
+ call("/opt/vyatta/sbin/my_delete system login user '{name}' "
+ "authentication plaintext-password"
.format(**user), env=env)
call("/opt/vyatta/sbin/my_set system login user '{name}' "
diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py
index d29109c41..b1daf7a82 100755
--- a/src/conf_mode/system-syslog.py
+++ b/src/conf_mode/system-syslog.py
@@ -146,6 +146,12 @@ def get_config(config=None):
config_data['hosts'][rhost][
'port'] = c.return_value(['host', rhost, 'port'])
+ # set system syslog host x.x.x.x format octet-counted
+ if c.exists('host ' + rhost + ' format octet-counted'):
+ config_data['hosts'][rhost]['oct_count'] = True
+ else:
+ config_data['hosts'][rhost]['oct_count'] = False
+
# set system syslog user
if c.exists('user'):
usrs = c.list_nodes('user')
diff --git a/src/conf_mode/system-timezone.py b/src/conf_mode/system-timezone.py
index 4d9f017a6..3d98ba774 100755
--- a/src/conf_mode/system-timezone.py
+++ b/src/conf_mode/system-timezone.py
@@ -48,6 +48,7 @@ def generate(tz):
def apply(tz):
call('/usr/bin/timedatectl set-timezone {}'.format(tz['name']))
+ call('systemctl restart rsyslog')
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py
index ad5ee9c33..cac95afe2 100755
--- a/src/conf_mode/tftp_server.py
+++ b/src/conf_mode/tftp_server.py
@@ -23,64 +23,52 @@ from glob import glob
from sys import exit
from vyos.config import Config
-from vyos.validate import is_ipv4, is_addr_assigned
-from vyos import ConfigError
-from vyos.util import call
+from vyos.configdict import dict_merge
from vyos.template import render
-
+from vyos.util import call
+from vyos.util import chmod_755
+from vyos.validate import is_ipv4
+from vyos.validate import is_addr_assigned
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
config_file = r'/etc/default/tftpd'
-default_config_data = {
- 'directory': '',
- 'allow_upload': False,
- 'port': '69',
- 'listen': []
-}
-
def get_config(config=None):
- tftpd = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
+
base = ['service', 'tftp-server']
if not conf.exists(base):
return None
- else:
- conf.set_level(base)
-
- if conf.exists(['directory']):
- tftpd['directory'] = conf.return_value(['directory'])
-
- if conf.exists(['allow-upload']):
- tftpd['allow_upload'] = True
-
- if conf.exists(['port']):
- tftpd['port'] = conf.return_value(['port'])
-
- if conf.exists(['listen-address']):
- tftpd['listen'] = conf.return_values(['listen-address'])
+ tftpd = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ tftpd = dict_merge(default_values, tftpd)
return tftpd
def verify(tftpd):
# bail out early - looks like removal from running config
- if tftpd is None:
+ if not tftpd:
return None
# Configuring allowed clients without a server makes no sense
- if not tftpd['directory']:
+ if 'directory' not in tftpd:
raise ConfigError('TFTP root directory must be configured!')
- if not tftpd['listen']:
+ if 'listen_address' not in tftpd:
raise ConfigError('TFTP server listen address must be configured!')
- for addr in tftpd['listen']:
- if not is_addr_assigned(addr):
- print('WARNING: TFTP server listen address {0} not assigned to any interface!'.format(addr))
+ for address in tftpd['listen_address']:
+ if not is_addr_assigned(address):
+ print(f'WARNING: TFTP server listen address "{address}" not ' \
+ 'assigned to any interface!')
return None
@@ -95,23 +83,23 @@ def generate(tftpd):
return None
idx = 0
- for listen in tftpd['listen']:
+ for address in tftpd['listen_address']:
config = deepcopy(tftpd)
- if is_ipv4(listen):
- config['listen'] = [listen + ":" + tftpd['port'] + " -4"]
+ port = tftpd['port']
+ if is_ipv4(address):
+ config['listen_address'] = f'{address}:{port} -4'
else:
- config['listen'] = ["[" + listen + "]" + tftpd['port'] + " -6"]
+ config['listen_address'] = f'[{address}]:{port} -6'
file = config_file + str(idx)
- render(file, 'tftp-server/default.tmpl', config)
-
+ render(file, 'tftp-server/default.tmpl', config, trim_blocks=True)
idx = idx + 1
return None
def apply(tftpd):
# stop all services first - then we will decide
- call('systemctl stop tftpd@{0..20}.service')
+ call('systemctl stop tftpd@*.service')
# bail out early - e.g. service deletion
if tftpd is None:
@@ -120,7 +108,7 @@ def apply(tftpd):
tftp_root = tftpd['directory']
if not os.path.exists(tftp_root):
os.makedirs(tftp_root)
- os.chmod(tftp_root, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH)
+ chmod_755(tftp_root)
# get UNIX uid for user 'tftp'
tftp_uid = pwd.getpwnam('tftp').pw_uid
@@ -135,8 +123,8 @@ def apply(tftpd):
os.chown(tftp_root, tftp_uid, tftp_gid)
idx = 0
- for listen in tftpd['listen']:
- call('systemctl restart tftpd@{0}.service'.format(idx))
+ for address in tftpd['listen_address']:
+ call(f'systemctl restart tftpd@{idx}.service')
idx = idx + 1
return None
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 3eece1922..2597ba42f 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -16,340 +16,66 @@
import os
-from time import sleep
from sys import exit
-from copy import deepcopy
-from stat import S_IRUSR, S_IWUSR, S_IRGRP
from vyos.config import Config
+from vyos.configdict import get_accel_dict
+from vyos.configverify import verify_accel_ppp_base_service
from vyos.template import render
-from vyos.util import call, run, get_half_cpus
-from vyos.validate import is_ipv4
+from vyos.util import call
+from vyos.util import vyos_dict_search
from vyos import ConfigError
-
from vyos import airbag
airbag.enable()
sstp_conf = '/run/accel-pppd/sstp.conf'
sstp_chap_secrets = '/run/accel-pppd/sstp.chap-secrets'
-default_config_data = {
- 'local_users' : [],
- 'auth_mode' : 'local',
- 'auth_proto' : ['auth_mschap_v2'],
- 'chap_secrets_file': sstp_chap_secrets, # used in Jinja2 template
- 'client_ip_pool' : [],
- 'client_ipv6_pool': [],
- 'client_ipv6_delegate_prefix': [],
- 'client_gateway': '',
- 'dnsv4' : [],
- 'dnsv6' : [],
- 'radius_server' : [],
- 'radius_acct_tmo' : '3',
- 'radius_max_try' : '3',
- 'radius_timeout' : '3',
- 'radius_nas_id' : '',
- 'radius_nas_ip' : '',
- 'radius_source_address' : '',
- 'radius_shaper_attr' : '',
- 'radius_shaper_vendor': '',
- 'radius_dynamic_author' : '',
- 'ssl_ca' : '',
- 'ssl_cert' : '',
- 'ssl_key' : '',
- 'mtu' : '',
- 'ppp_mppe' : 'prefer',
- 'ppp_echo_failure' : '',
- 'ppp_echo_interval' : '',
- 'ppp_echo_timeout' : '',
- 'thread_cnt' : get_half_cpus()
-}
-
def get_config(config=None):
- sstp = deepcopy(default_config_data)
- base_path = ['vpn', 'sstp']
if config:
conf = config
else:
conf = Config()
- if not conf.exists(base_path):
+ base = ['vpn', 'sstp']
+ if not conf.exists(base):
return None
- conf.set_level(base_path)
-
- if conf.exists(['authentication', 'mode']):
- sstp['auth_mode'] = conf.return_value(['authentication', 'mode'])
-
- #
- # local auth
- if conf.exists(['authentication', 'local-users']):
- for username in conf.list_nodes(['authentication', 'local-users', 'username']):
- user = {
- 'name' : username,
- 'password' : '',
- 'state' : 'enabled',
- 'ip' : '*',
- 'upload' : None,
- 'download' : None
- }
-
- conf.set_level(base_path + ['authentication', 'local-users', 'username', username])
-
- if conf.exists(['password']):
- user['password'] = conf.return_value(['password'])
-
- if conf.exists(['disable']):
- user['state'] = 'disable'
-
- if conf.exists(['static-ip']):
- user['ip'] = conf.return_value(['static-ip'])
-
- if conf.exists(['rate-limit', 'download']):
- user['download'] = conf.return_value(['rate-limit', 'download'])
-
- if conf.exists(['rate-limit', 'upload']):
- user['upload'] = conf.return_value(['rate-limit', 'upload'])
-
- sstp['local_users'].append(user)
-
- #
- # RADIUS auth and settings
- conf.set_level(base_path + ['authentication', 'radius'])
- if conf.exists(['server']):
- for server in conf.list_nodes(['server']):
- radius = {
- 'server' : server,
- 'key' : '',
- 'fail_time' : 0,
- 'port' : '1812',
- 'acct_port' : '1813'
- }
-
- conf.set_level(base_path + ['authentication', 'radius', 'server', server])
-
- if conf.exists(['fail-time']):
- radius['fail_time'] = conf.return_value(['fail-time'])
-
- if conf.exists(['port']):
- radius['port'] = conf.return_value(['port'])
-
- if conf.exists(['acct-port']):
- radius['acct_port'] = conf.return_value(['acct-port'])
-
- if conf.exists(['key']):
- radius['key'] = conf.return_value(['key'])
-
- if not conf.exists(['disable']):
- sstp['radius_server'].append(radius)
-
- #
- # advanced radius-setting
- conf.set_level(base_path + ['authentication', 'radius'])
-
- if conf.exists(['acct-timeout']):
- sstp['radius_acct_tmo'] = conf.return_value(['acct-timeout'])
-
- if conf.exists(['max-try']):
- sstp['radius_max_try'] = conf.return_value(['max-try'])
-
- if conf.exists(['timeout']):
- sstp['radius_timeout'] = conf.return_value(['timeout'])
-
- if conf.exists(['nas-identifier']):
- sstp['radius_nas_id'] = conf.return_value(['nas-identifier'])
-
- if conf.exists(['nas-ip-address']):
- sstp['radius_nas_ip'] = conf.return_value(['nas-ip-address'])
-
- if conf.exists(['source-address']):
- sstp['radius_source_address'] = conf.return_value(['source-address'])
-
- # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA)
- if conf.exists(['dynamic-author']):
- dae = {
- 'port' : '1700',
- 'server' : '',
- 'key' : ''
- }
-
- if conf.exists(['dynamic-author', 'server']):
- dae['server'] = conf.return_value(['dynamic-author', 'server'])
-
- if conf.exists(['dynamic-author', 'port']):
- dae['port'] = conf.return_value(['dynamic-author', 'port'])
-
- if conf.exists(['dynamic-author', 'key']):
- dae['key'] = conf.return_value(['dynamic-author', 'key'])
-
- sstp['radius_dynamic_author'] = dae
-
- if conf.exists(['rate-limit', 'enable']):
- sstp['radius_shaper_attr'] = 'Filter-Id'
- c_attr = ['rate-limit', 'enable', 'attribute']
- if conf.exists(c_attr):
- sstp['radius_shaper_attr'] = conf.return_value(c_attr)
-
- c_vendor = ['rate-limit', 'enable', 'vendor']
- if conf.exists(c_vendor):
- sstp['radius_shaper_vendor'] = conf.return_value(c_vendor)
-
- #
- # authentication protocols
- conf.set_level(base_path + ['authentication'])
- if conf.exists(['protocols']):
- # clear default list content, now populate with actual CLI values
- sstp['auth_proto'] = []
- auth_mods = {
- 'pap': 'auth_pap',
- 'chap': 'auth_chap_md5',
- 'mschap': 'auth_mschap_v1',
- 'mschap-v2': 'auth_mschap_v2'
- }
-
- for proto in conf.return_values(['protocols']):
- sstp['auth_proto'].append(auth_mods[proto])
-
- #
- # read in SSL certs
- conf.set_level(base_path + ['ssl'])
- if conf.exists(['ca-cert-file']):
- sstp['ssl_ca'] = conf.return_value(['ca-cert-file'])
-
- if conf.exists(['cert-file']):
- sstp['ssl_cert'] = conf.return_value(['cert-file'])
-
- if conf.exists(['key-file']):
- sstp['ssl_key'] = conf.return_value(['key-file'])
-
-
- #
- # read in client IPv4 pool
- conf.set_level(base_path + ['client-ip-pool'])
- if conf.exists(['subnet']):
- sstp['client_ip_pool'] = conf.return_values(['subnet'])
-
- #
- # read in client IPv6 pool
- conf.set_level(base_path + ['client-ipv6-pool'])
- if conf.exists(['prefix']):
- for prefix in conf.list_nodes(['prefix']):
- tmp = {
- 'prefix': prefix,
- 'mask': '64'
- }
-
- if conf.exists(['prefix', prefix, 'mask']):
- tmp['mask'] = conf.return_value(['prefix', prefix, 'mask'])
-
- sstp['client_ipv6_pool'].append(tmp)
-
- if conf.exists(['delegate']):
- for prefix in conf.list_nodes(['delegate']):
- tmp = {
- 'prefix': prefix,
- 'mask': ''
- }
-
- if conf.exists(['delegate', prefix, 'delegation-prefix']):
- tmp['mask'] = conf.return_value(['delegate', prefix, 'delegation-prefix'])
-
- sstp['client_ipv6_delegate_prefix'].append(tmp)
-
- #
- # read in network settings
- conf.set_level(base_path)
- if conf.exists(['gateway-address']):
- sstp['client_gateway'] = conf.return_value(['gateway-address'])
-
- if conf.exists(['name-server']):
- for name_server in conf.return_values(['name-server']):
- if is_ipv4(name_server):
- sstp['dnsv4'].append(name_server)
- else:
- sstp['dnsv6'].append(name_server)
-
- if conf.exists(['mtu']):
- sstp['mtu'] = conf.return_value(['mtu'])
-
- #
- # read in PPP stuff
- conf.set_level(base_path + ['ppp-options'])
- if conf.exists('mppe'):
- sstp['ppp_mppe'] = conf.return_value(['mppe'])
-
- if conf.exists(['lcp-echo-failure']):
- sstp['ppp_echo_failure'] = conf.return_value(['lcp-echo-failure'])
-
- if conf.exists(['lcp-echo-interval']):
- sstp['ppp_echo_interval'] = conf.return_value(['lcp-echo-interval'])
-
- if conf.exists(['lcp-echo-timeout']):
- sstp['ppp_echo_timeout'] = conf.return_value(['lcp-echo-timeout'])
-
+ # retrieve common dictionary keys
+ sstp = get_accel_dict(conf, base, sstp_chap_secrets)
return sstp
-
def verify(sstp):
- if sstp is None:
+ if not sstp:
return None
- # vertify auth settings
- if sstp['auth_mode'] == 'local':
- if not sstp['local_users']:
- raise ConfigError('SSTP local auth mode requires local users to be configured!')
-
- for user in sstp['local_users']:
- username = user['name']
- if not user['password']:
- raise ConfigError(f'Password required for local user "{username}"')
-
- # if up/download is set, check that both have a value
- if user['upload'] and not user['download']:
- raise ConfigError(f'Download speed value required for local user "{username}"')
-
- if user['download'] and not user['upload']:
- raise ConfigError(f'Upload speed value required for local user "{username}"')
-
- if not sstp['client_ip_pool']:
- raise ConfigError('Client IP subnet required')
-
- if not sstp['client_gateway']:
- raise ConfigError('Client gateway IP address required')
-
- if len(sstp['dnsv4']) > 2:
- raise ConfigError('Not more then two IPv4 DNS name-servers can be configured')
+ verify_accel_ppp_base_service(sstp)
- # check ipv6
- if sstp['client_ipv6_delegate_prefix'] and not sstp['client_ipv6_pool']:
- raise ConfigError('IPv6 prefix delegation requires client-ipv6-pool prefix')
+ if not sstp['client_ip_pool']:
+ raise ConfigError('Client IP subnet required')
- for prefix in sstp['client_ipv6_delegate_prefix']:
- if not prefix['mask']:
- raise ConfigError('Delegation-prefix required for individual delegated networks')
-
- if not sstp['ssl_ca'] or not sstp['ssl_cert'] or not sstp['ssl_key']:
- raise ConfigError('One or more SSL certificates missing')
-
- if not os.path.exists(sstp['ssl_ca']):
- file = sstp['ssl_ca']
- raise ConfigError(f'SSL CA certificate file "{file}" does not exist')
-
- if not os.path.exists(sstp['ssl_cert']):
- file = sstp['ssl_cert']
- raise ConfigError(f'SSL public key file "{file}" does not exist')
-
- if not os.path.exists(sstp['ssl_key']):
- file = sstp['ssl_key']
- raise ConfigError(f'SSL private key file "{file}" does not exist')
+ #
+ # SSL certificate checks
+ #
+ tmp = vyos_dict_search('ssl.ca_cert_file', sstp)
+ if not tmp:
+ raise ConfigError(f'SSL CA certificate file required!')
+ else:
+ if not os.path.isfile(tmp):
+ raise ConfigError(f'SSL CA certificate "{tmp}" does not exist!')
- if sstp['auth_mode'] == 'radius':
- if len(sstp['radius_server']) == 0:
- raise ConfigError('RADIUS authentication requires at least one server')
+ tmp = vyos_dict_search('ssl.cert_file', sstp)
+ if not tmp:
+ raise ConfigError(f'SSL public key file required!')
+ else:
+ if not os.path.isfile(tmp):
+ raise ConfigError(f'SSL public key "{tmp}" does not exist!')
- for radius in sstp['radius_server']:
- if not radius['key']:
- server = radius['server']
- raise ConfigError(f'Missing RADIUS secret key for server "{ server }"')
+ tmp = vyos_dict_search('ssl.key_file', sstp)
+ if not tmp:
+ raise ConfigError(f'SSL private key file required!')
+ else:
+ if not os.path.isfile(tmp):
+ raise ConfigError(f'SSL private key "{tmp}" does not exist!')
def generate(sstp):
if not sstp:
@@ -358,9 +84,9 @@ def generate(sstp):
# accel-cmd reload doesn't work so any change results in a restart of the daemon
render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp, trim_blocks=True)
- if sstp['local_users']:
- render(sstp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', sstp, trim_blocks=True)
- os.chmod(sstp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
+ if vyos_dict_search('authentication.mode', sstp) == 'local':
+ render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
+ sstp, trim_blocks=True, permission=0o640)
else:
if os.path.exists(sstp_chap_secrets):
os.unlink(sstp_chap_secrets)
diff --git a/src/migration-scripts/pppoe-server/1-to-2 b/src/migration-scripts/pppoe-server/1-to-2
index 7cae3b5bc..902efb86b 100755
--- a/src/migration-scripts/pppoe-server/1-to-2
+++ b/src/migration-scripts/pppoe-server/1-to-2
@@ -14,7 +14,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# Convert "service pppoe-server interface ethX" to: "service pppoe-server interface ethX {}"
+# change mppe node to a leaf node with value prefer
+
+import os
from sys import argv, exit
from vyos.configtree import ConfigTree
@@ -28,21 +30,32 @@ file_name = argv[1]
with open(file_name, 'r') as f:
config_file = f.read()
-ctree = ConfigTree(config_file)
-cbase = ['service', 'pppoe-server','interface']
-
-if not ctree.exists(cbase):
+config = ConfigTree(config_file)
+base = ['service', 'pppoe-server']
+if not config.exists(base):
+ # Nothing to do
exit(0)
else:
- nics = ctree.return_values(cbase)
- # convert leafNode to a tagNode
- ctree.set(cbase)
- ctree.set_tag(cbase)
- for nic in nics:
- ctree.set(cbase + [nic])
+ mppe_base = base + ['ppp-options', 'mppe']
+ if config.exists(mppe_base):
+ # get current values
+ tmp = config.list_nodes(mppe_base)
+ # drop node(s) first ...
+ config.delete(mppe_base)
+
+ print(tmp)
+ # set new value based on preference
+ if 'require' in tmp:
+ config.set(mppe_base, value='require')
+ elif 'prefer' in tmp:
+ config.set(mppe_base, value='prefer')
+ elif 'deny' in tmp:
+ config.set(mppe_base, value='deny')
try:
- open(file_name,'w').write(ctree.to_string())
+ 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))
exit(1)
+
diff --git a/src/migration-scripts/pppoe-server/2-to-3 b/src/migration-scripts/pppoe-server/2-to-3
index 5f9730a41..7cae3b5bc 100755
--- a/src/migration-scripts/pppoe-server/2-to-3
+++ b/src/migration-scripts/pppoe-server/2-to-3
@@ -14,9 +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/>.
-# - remove primary/secondary identifier from nameserver
-
-import os
+# Convert "service pppoe-server interface ethX" to: "service pppoe-server interface ethX {}"
from sys import argv, exit
from vyos.configtree import ConfigTree
@@ -30,112 +28,21 @@ file_name = argv[1]
with open(file_name, 'r') as f:
config_file = f.read()
-config = ConfigTree(config_file)
-base = ['service', 'pppoe-server']
-if not config.exists(base):
- # Nothing to do
+ctree = ConfigTree(config_file)
+cbase = ['service', 'pppoe-server','interface']
+
+if not ctree.exists(cbase):
exit(0)
else:
-
- # Migrate IPv4 DNS servers
- dns_base = base + ['dns-servers']
- if config.exists(dns_base):
- for server in ['server-1', 'server-2']:
- if config.exists(dns_base + [server]):
- dns = config.return_value(dns_base + [server])
- config.set(base + ['name-server'], value=dns, replace=False)
-
- config.delete(dns_base)
-
- # Migrate IPv6 DNS servers
- dns_base = base + ['dnsv6-servers']
- if config.exists(dns_base):
- for server in ['server-1', 'server-2', 'server-3']:
- if config.exists(dns_base + [server]):
- dns = config.return_value(dns_base + [server])
- config.set(base + ['name-server'], value=dns, replace=False)
-
- config.delete(dns_base)
-
- # Migrate IPv4 WINS servers
- wins_base = base + ['wins-servers']
- if config.exists(wins_base):
- for server in ['server-1', 'server-2']:
- if config.exists(wins_base + [server]):
- wins = config.return_value(wins_base + [server])
- config.set(base + ['wins-server'], value=wins, replace=False)
-
- config.delete(wins_base)
-
- # Migrate radius-settings node to RADIUS and use this as base for the
- # later migration of the RADIUS servers - this will save a lot of code
- radius_settings = base + ['authentication', 'radius-settings']
- if config.exists(radius_settings):
- config.rename(radius_settings, 'radius')
-
- # Migrate RADIUS dynamic author / change of authorisation server
- dae_old = base + ['authentication', 'radius', 'dae-server']
- if config.exists(dae_old):
- config.rename(dae_old, 'dynamic-author')
- dae_new = base + ['authentication', 'radius', 'dynamic-author']
-
- if config.exists(dae_new + ['ip-address']):
- config.rename(dae_new + ['ip-address'], 'server')
-
- if config.exists(dae_new + ['secret']):
- config.rename(dae_new + ['secret'], 'key')
-
- # Migrate RADIUS server
- radius_server = base + ['authentication', 'radius-server']
- if config.exists(radius_server):
- new_base = base + ['authentication', 'radius', 'server']
- config.set(new_base)
- config.set_tag(new_base)
- for server in config.list_nodes(radius_server):
- old_base = radius_server + [server]
- config.copy(old_base, new_base + [server])
-
- # migrate key
- if config.exists(new_base + [server, 'secret']):
- config.rename(new_base + [server, 'secret'], 'key')
-
- # remove old req-limit node
- if config.exists(new_base + [server, 'req-limit']):
- config.delete(new_base + [server, 'req-limit'])
-
- config.delete(radius_server)
-
- # Migrate IPv6 prefixes
- ipv6_base = base + ['client-ipv6-pool']
- if config.exists(ipv6_base + ['prefix']):
- prefix_old = config.return_values(ipv6_base + ['prefix'])
- # delete old prefix CLI nodes
- config.delete(ipv6_base + ['prefix'])
- # create ned prefix tag node
- config.set(ipv6_base + ['prefix'])
- config.set_tag(ipv6_base + ['prefix'])
-
- for p in prefix_old:
- prefix = p.split(',')[0]
- mask = p.split(',')[1]
- config.set(ipv6_base + ['prefix', prefix, 'mask'], value=mask)
-
- if config.exists(ipv6_base + ['delegate-prefix']):
- prefix_old = config.return_values(ipv6_base + ['delegate-prefix'])
- # delete old delegate prefix CLI nodes
- config.delete(ipv6_base + ['delegate-prefix'])
- # create ned delegation tag node
- config.set(ipv6_base + ['delegate'])
- config.set_tag(ipv6_base + ['delegate'])
-
- for p in prefix_old:
- prefix = p.split(',')[0]
- mask = p.split(',')[1]
- config.set(ipv6_base + ['delegate', prefix, 'delegation-prefix'], value=mask)
+ nics = ctree.return_values(cbase)
+ # convert leafNode to a tagNode
+ ctree.set(cbase)
+ ctree.set_tag(cbase)
+ for nic in nics:
+ ctree.set(cbase + [nic])
try:
- with open(file_name, 'w') as f:
- f.write(config.to_string())
+ open(file_name,'w').write(ctree.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
exit(1)
diff --git a/src/migration-scripts/pppoe-server/3-to-4 b/src/migration-scripts/pppoe-server/3-to-4
index 6709d5f86..5f9730a41 100755
--- a/src/migration-scripts/pppoe-server/3-to-4
+++ b/src/migration-scripts/pppoe-server/3-to-4
@@ -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/>.
-# change mppe node to a leaf node with value prefer
+# - remove primary/secondary identifier from nameserver
import os
@@ -36,12 +36,102 @@ if not config.exists(base):
# Nothing to do
exit(0)
else:
- mppe_base = base + ['ppp-options', 'mppe']
- if config.exists(mppe_base):
- # drop node first ...
- config.delete(mppe_base)
- # ... and set new default
- config.set(mppe_base, value='prefer')
+
+ # Migrate IPv4 DNS servers
+ dns_base = base + ['dns-servers']
+ if config.exists(dns_base):
+ for server in ['server-1', 'server-2']:
+ if config.exists(dns_base + [server]):
+ dns = config.return_value(dns_base + [server])
+ config.set(base + ['name-server'], value=dns, replace=False)
+
+ config.delete(dns_base)
+
+ # Migrate IPv6 DNS servers
+ dns_base = base + ['dnsv6-servers']
+ if config.exists(dns_base):
+ for server in ['server-1', 'server-2', 'server-3']:
+ if config.exists(dns_base + [server]):
+ dns = config.return_value(dns_base + [server])
+ config.set(base + ['name-server'], value=dns, replace=False)
+
+ config.delete(dns_base)
+
+ # Migrate IPv4 WINS servers
+ wins_base = base + ['wins-servers']
+ if config.exists(wins_base):
+ for server in ['server-1', 'server-2']:
+ if config.exists(wins_base + [server]):
+ wins = config.return_value(wins_base + [server])
+ config.set(base + ['wins-server'], value=wins, replace=False)
+
+ config.delete(wins_base)
+
+ # Migrate radius-settings node to RADIUS and use this as base for the
+ # later migration of the RADIUS servers - this will save a lot of code
+ radius_settings = base + ['authentication', 'radius-settings']
+ if config.exists(radius_settings):
+ config.rename(radius_settings, 'radius')
+
+ # Migrate RADIUS dynamic author / change of authorisation server
+ dae_old = base + ['authentication', 'radius', 'dae-server']
+ if config.exists(dae_old):
+ config.rename(dae_old, 'dynamic-author')
+ dae_new = base + ['authentication', 'radius', 'dynamic-author']
+
+ if config.exists(dae_new + ['ip-address']):
+ config.rename(dae_new + ['ip-address'], 'server')
+
+ if config.exists(dae_new + ['secret']):
+ config.rename(dae_new + ['secret'], 'key')
+
+ # Migrate RADIUS server
+ radius_server = base + ['authentication', 'radius-server']
+ if config.exists(radius_server):
+ new_base = base + ['authentication', 'radius', 'server']
+ config.set(new_base)
+ config.set_tag(new_base)
+ for server in config.list_nodes(radius_server):
+ old_base = radius_server + [server]
+ config.copy(old_base, new_base + [server])
+
+ # migrate key
+ if config.exists(new_base + [server, 'secret']):
+ config.rename(new_base + [server, 'secret'], 'key')
+
+ # remove old req-limit node
+ if config.exists(new_base + [server, 'req-limit']):
+ config.delete(new_base + [server, 'req-limit'])
+
+ config.delete(radius_server)
+
+ # Migrate IPv6 prefixes
+ ipv6_base = base + ['client-ipv6-pool']
+ if config.exists(ipv6_base + ['prefix']):
+ prefix_old = config.return_values(ipv6_base + ['prefix'])
+ # delete old prefix CLI nodes
+ config.delete(ipv6_base + ['prefix'])
+ # create ned prefix tag node
+ config.set(ipv6_base + ['prefix'])
+ config.set_tag(ipv6_base + ['prefix'])
+
+ for p in prefix_old:
+ prefix = p.split(',')[0]
+ mask = p.split(',')[1]
+ config.set(ipv6_base + ['prefix', prefix, 'mask'], value=mask)
+
+ if config.exists(ipv6_base + ['delegate-prefix']):
+ prefix_old = config.return_values(ipv6_base + ['delegate-prefix'])
+ # delete old delegate prefix CLI nodes
+ config.delete(ipv6_base + ['delegate-prefix'])
+ # create ned delegation tag node
+ config.set(ipv6_base + ['delegate'])
+ config.set_tag(ipv6_base + ['delegate'])
+
+ for p in prefix_old:
+ prefix = p.split(',')[0]
+ mask = p.split(',')[1]
+ config.set(ipv6_base + ['delegate', prefix, 'delegation-prefix'], value=mask)
try:
with open(file_name, 'w') as f:
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 671a89036..5b1ab1f1f 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -136,7 +136,7 @@ def initialization(socket):
session_string = ''
# check first for resent init msg, in case of client timeout
while True:
- msg = socket.recv().decode()
+ msg = socket.recv().decode("utf-8", "ignore")
try:
message = json.loads(msg)
if message["type"] == "init":
@@ -149,10 +149,10 @@ def initialization(socket):
active_string = msg
resp = "active"
socket.send(resp.encode())
- session_string = socket.recv().decode()
+ session_string = socket.recv().decode("utf-8", "ignore")
resp = "session"
socket.send(resp.encode())
- pid_string = socket.recv().decode()
+ pid_string = socket.recv().decode("utf-8", "ignore")
resp = "pid"
socket.send(resp.encode())